• 이번에는 security의 기본 User클레스를 확장하여 실제로 필요한 회원 정보를 추가하고 security-context.xml에있던 사용자 인증 쿼리를 mybatis로 이동시켜 사용하겠다. 그리고 처음에 포스팅할때 같이 해야 했던 logout도 적용시키겠다.

  • logout은 별거 없다. security-context.xmlhttp

<logout delete-cookies="true" logout-success-url="/user/loginPage"
		   logout-url="/user/logout" invalidate-session="true"/>

를 추가한다.

  • logout-url : 로그아웃이 요청될 url (이 주소로 요청이 들어오면 security에서 알아서 logout시켜준다)​
  • logout-success-url : 로그아웃이 성공 후 이동할 주소
  • ​delete-cookies : 쿠키 삭제 여부
  • ​invalidate-session : 세션 제거 여부

이제 security에서 제공해주는 User클레스를 확장시켜 사용하는 방법을 알아보자.

security에서 사용중인 User클레스의 내용을 살펴보면 멤버 변수로

//~ Instance fields ====================
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;

가 존재하고 setter가없이 생성자로만 변수값을 지정 할 수 있게 해놨다. 생성자역시 username,password,enabled 만을 입력받는 생성자와 모든 변수를 입력받는 생성자 2개가 존재한다. 값을 가져오는 getter와 계정 잠금여부를 확인하는 is메서드가 존재한다

직접 만들어 확장시킬 클레스는 User클레스를 상속받고 필요한 변수만 추가하여 사용하겠다.

  • User클레스를 상속받는 UserDetailsVO 생성(사용법을 익히기 위해 email필드만 추가하겠다.)

(이부분은 UserDetails인터페이스만 상속받아 VO객체를 만들어도 상관이 없지만 User클레스를 이용하여 확장을 할경우 중복세션 방지등의 기능을 추가 적으로 구현하지 않아도 적용시킬수 있어 User클레스를 직접 확장하는 방법을 이용했다.)

package com.min.study.user.vo;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.userdetails.User;

public class UserDetailsVO extends User{

 /**
  *
  */
 private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

 private String email;

 public UserDetailsVO(String username, String password, boolean enabled,
   boolean accountNonExpired, boolean credentialsNonExpired,
   boolean accountNonLocked,
   Collection<? extends GrantedAuthority> authorities,String email) {
  super(username, password, enabled, accountNonExpired, credentialsNonExpired,
    accountNonLocked, authorities);
  this.email = email;
  // TODO Auto-generated constructor stub
 }

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

 @Override
 public String toString() {
  // TODO Auto-generated method stub
  return super.toString() + "; Email: "+this.email;
 }

}
  • mybatis를 이용하여 사용자 정보를 가져올 UserAuthenticationService 생성
  • security의 인증 절차를 보면 사용자가 입력한 request의 username,password와 디비에서 가져온 정보를 비교하여인증
  • UserDetailsService 인터페이스를 상속받아 loadUserByUsername메서드를 구현하여 필요한 정보를 가져오면 된다.

처음에 UserDetailsService를 상속받아 UserAuthenticationService를 만들었을때는 그냥 간단하게 spring에 등록한 userService@Resource를 이용하여 주입시키면 될줄 알았는데 주입이 되지 않았다. 자세한 이유는 모르겠다. 다른 자료를 찾아봐도 JdbcDaoImpl을 이용한 방법뿐이 없었다. 그래서 UserAuthenticationService에 직접 datasource-context.xml에 지정한 mybatissqlSessionTemplate를 받아와 사용하는 방법을 이용했다. 나중에 알고보니 UserAuthenticationService 서비스를 스프링에서 인식할수 있게 어노테이션이나 xml에 빈을 추가하면 됨.

  • UserDetailsService를 상속받은 UserAuthenticationService생성
package com.min.study.user.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.min.study.user.vo.UserDetailsVO;

public class UserAuthenticationService implements UserDetailsService {

 private static final Logger logger = LoggerFactory.getLogger(UserAuthenticationService.class);
 private SqlSessionTemplate sqlSession;


 public UserAuthenticationService() {
  // TODO Auto-generated constructor stub
 }

 public UserAuthenticationService(SqlSessionTemplate sqlSession) {
  // TODO Auto-generated constructor stub
  this.sqlSession = sqlSession;
 }

 @Override
 public UserDetails loadUserByUsername(String username)
   throws UsernameNotFoundException {
  // TODO Auto-generated method stub
  Map<String, Object> user = sqlSession.selectOne("user.selectUser",username);
  if(user == null ) throw new UsernameNotFoundException(username);
  logger.info(user.toString());
  List<GrantedAuthority> gas = new ArrayList<GrantedAuthority>();
  gas.add(new SimpleGrantedAuthority(user.get("authority").toString()));
  return new UserDetailsVO(user.get("username").toString(), user.get("password").toString(), (Integer)user.get("enabled") == 1, true, true, true, gas,user.get("username").toString());
 }

}
  • security-context.xmlUserAuthenticationService​빈 생성 및 sqlSession주입 및 기존 jdbc-user-service 제거 or 주석 처리
<beans:bean id="userService" class="com.min.study.user.service.UserAuthenticationService">
<beans:constructor-arg name="sqlSession" ref="sqlSession" />
</beans:bean>
  • mybatismysql/user.xml에 쿼리 추가
<select id="selectUser" resulttype="map">
 SELECT
 EMAIL as username,
 PASSWD as password,
 ENABLED as enabled,
 AUTHORITY as authority
 FROM USER
 WHERE EMAIL = #{username}
</select>
  • 사용자 인증 확인

소스를 다 작성 하고 포스팅 하는거라 중간에 빠진 내용이 있을지 모르겠다;

소스 확인 : https://github.com/ParkMinKyu/security