DOing
[Spring Security] UserDetailsService를 이용한 인증 본문
이전 포스팅에서는 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);
}
}
'Spring' 카테고리의 다른 글
Spring ShoppingMall 팀프로젝트 회고 (1) | 2021.05.17 |
---|---|
[Maven] Maven에 대해 알고 사용해보자 (0) | 2021.05.08 |
[Spring Security] DB를 이용하여 로그인 (0) | 2021.04.28 |
[Spring Security] 소셜로그인 - 네이버 (0) | 2021.04.11 |
[Spring Boot] 세션저장소 (0) | 2021.04.11 |