Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

DOing

[Spring Security] UserDetailsService를 이용한 인증 본문

Spring

[Spring Security] UserDetailsService를 이용한 인증

mangdo 2021. 4. 28. 18:16

이전 포스팅에서는 DB에 있는 유저 정보를 쿼리를 이용해서 인증했다. 이번 포스팅에는 UserDetailsService를 이용하여 인증해보자. UserDetailsService는 원하는 객체를 인증과 권한 체크에 활용할 수 있다.

 

1. 회원 도메인 생성

회원은 여러개의 권한을 가질 수 있는 구조로 설계하였다.

@Data
public class MemberVO {
	private String userid;
	private String userpw;
	private String userName;
	private boolean enabled;
	
	private Date regDate;
	private Date updateDate;
	private List<AuthVO> authList;
}
© 2021 GitHub, Inc.

 

2. 권한 도메인 생성

@Data
public class AuthVO {
	private String userid;
	private String auth;
}

 

3. 회원 Mepper 생성

회원에 대한 정보는 MyBatis를 이용해서 처리할 예정이다. MyBatis의 ResultMap기능을 사용하여 tbl_member와 tbl_member_auth를 조인해서 한번에 가져온다. 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.MemberMapper">


	<resultMap type="org.zerock.domain.MemberVO" id="memberMap">
		<id property="userid" column="userid"/>
		<result property = "userid" column="userid"/>
		<result property="userpw" column="userpw"/>
		<result property="userName" column="username"/>
		<result property="regDate" column="regdate"/>
		<result property="updateDate" column="updatedate"/>
		<collection property="authList" resultMap="authMap">
		</collection>
	</resultMap>
	
	<resultMap type="org.zerock.domain.AuthVO" id="authMap">
		<result property="userid" column="userid"/>
		<result property="auth" column="auth"/>
	</resultMap>
	
	<select id="read" resultMap="memberMap">
		SELECT mem.userid, userpw, username, enabled, regdate, updatedate, auth, auth.userid
		FROM tbl_member mem 
		LEFT OUTER JOIN tbl_member_auth auth
		on mem.userid = auth.userid
		WHERE mem.userid = #{userid}
	</select>

</mapper>

 

4. CustomUserDetailsService 생성

UserDetailsService는 DB의 사용자 정보를 조회한다.

UserDetailsService는 loadUserByUsername이라는 단 한개의 메서드를 가진다.

@Log4j
public class CustomUserDetailsService implements UserDetailsService {
	@Setter(onMethod_= {@Autowired})
	private MemberMapper memberMapper;
	
	@Override
	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException{
		log.warn("Load User By UserName : "+userName);
		
		// userName means userid
		MemberVO vo = memberMapper.read(userName);
		
		log.warn("queried by member Mapper : "+vo);
		
		return null;
	}
	
}

 

5. CustomUserDetailsService 등록

security-context.xml에 CustomUserDetailsService를 등록해준다.

<bean id ="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<bean id="customUserDetailsService" class="org.zerock.security.CustomUserDetailsService"></bean>

<security:http>

  <security:form-login login-page="/customLogin"/>
  <security:logout logout-url="/customLogout" invalidate-session="true"/>
  <security:access-denied-handler ref="customAccessDenied"/>

</security:http>

<security:authentication-manager>
  <security:authentication-provider user-service-ref="customUserDetailsService">

  	<security:password-encoder ref="bcryptPasswordEncoder"/>

  </security:authentication-provider>
</security:authentication-manager>

 

6. CustomUser 생성

UserDetailsService는 loadUserByUsername()이라는 단 한개의 메서드를 가진다.

 loadUserByUsername()의 반환값은 UserDetails이다. UserDetails는 조회한 사용자 정보를 담는 인터페이스이다. 직접 이를 구현할 수도 있지만, Spring Security에서는 UserDetails를 구현한 여러 클래스를 제공하고 있다. 그 중에서 가장 많이 사용되는 User클래스를 상속받아서 CustomUser를 생성한다.

 

@Getter
public class CustomUser extends User{

	private static final long serialVersionUID = 1L;
	private MemberVO member;

	public CustomUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
		super(username, password, authorities);
		// TODO Auto-generated constructor stub
	}
	
	public CustomUser(MemberVO vo) {
		super(vo.getUserid(), vo.getUserpw(), 
				vo.getAuthList()
				.stream().map(auth->new SimpleGrantedAuthority(auth.getAuth()))
				.collect(Collectors.toList()));
		this.member = vo;
	}
}

User클래스를 상속받기 때문에 부모클래스의 생성자를 호출해야지만 정상적인 객체를 생성할 수 있다. 

 

7. CustomUserDetailsService 수정

MemberMapper를 이용해서 MemberVO를 조회하고 만일 존재한다면 CustomUser 타입의 객체로 변환해서 반환하도록 수정한다.

@Log4j
public class CustomUserDetailsService implements UserDetailsService {
	@Setter(onMethod_= {@Autowired})
	private MemberMapper memberMapper;
	
	@Override
	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException{
		log.warn("Load User By UserName : "+userName);
		
		// userName means userid
		MemberVO vo = memberMapper.read(userName);
		
		log.warn("queried by member Mapper : "+vo);
		
		return vo == null? null:new CustomUser(vo);
	}
	
}