Spring security 3 JWT + custom user detail service 적용하기

정명진·2023년 12월 4일
0

이번에 spring security 3.x.x 버전으로 올리면서 사이드 프로젝트를 진행했다. 기존 2점대 버전으로 했던 방식으로 하니 custom user detail service를 타지 않아 JWT 토큰이 탈퇴한 회원의 것임에도 정상적으로 작동했다. 내가 예상한것은 유저 정보가 없으므로 에러가 터져야 했다. 그래서 해당 문제를 고치려면 접근 권한이 필요한 서비스마다 사전에 유저 검증을 하거나... custom user detail service가 타도록 변경해야 겠다. 아무래도 서비스마다 사전 검증 코드를 넣는건 비효율적이라 custom user detail service가 타지게 변경해야 했다.

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {

    private final TokenService tokenService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        if (!requestURI.contains("actuator")) {
            log.info("Request URI {}", requestURI);
        }

        // set header
        var header = "";

        try {
            header = request.getHeader("Authorization");
        } catch (Exception e) {
            request.setAttribute("exception", new CustomException(CustomErrorCode.HEADER_INVALID));
        }

        log.info("Header {}", header);

        // parse token
        try {
            if (StringUtils.hasText(header)) {
                String token = tokenService.parsePrefix(header);

                // check black list
                String logout = (String) redisTemplate.opsForValue().get(token);
                if ((StringUtils.hasText(logout) && logout.equals("logout"))) {
                    throw new CustomException(CustomErrorCode.LOGGED_OUT_ID);
                }

                String userId = tokenService.getUserIdFromToken(token);
                log.info("User ID {}", userId);
                GrantedAuthority authority = new SimpleGrantedAuthority(UserRole.ROLE_USER.name());
                Set<GrantedAuthority> authorities = Collections.singleton(authority);
                // set authentication
                UsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken.authenticated(new User(userId, "", authorities), "", authorities);
                authenticationToken.setDetails(new WebAuthenticationDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                authenticationManagerBuilder.getObject().authenticate(authenticationToken);
            }
        } catch (Exception e) {
            request.setAttribute("exception", e);
            throw e;
        }

        filterChain.doFilter(request, response);
    }
}

저기서 주의깊게 볼부분은

UsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken.authenticated(new User(userId, "", authorities), "", authorities);
authenticationToken.setDetails(new WebAuthenticationDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
authenticationManagerBuilder.getObject().authenticate(authenticationToken);

이 부분이다. 우선 유저 정보 토큰을 생성한다. 그리고 security의 manager builder에 토큰 정보를 넘겨주면서 검증을 하도록 만들어준다. 이전에 왜 안타나 봤더니 매니저 빌더에 토큰 정보를 넘기지 않아 검증을 하지 않았던 것이다. 이렇게 검증을 하도록 하면 이제 custom user detail service를 타게 된다.

그런데 여기서 문제가 또 발생했다.

BadCredentialsException

이 발생하는것이었다. 그래서 정보를 찾아보니 기본 password encoder를 설정한 값으로 비교를 하게 되는데 custom user 정보를 세팅할때 암호화된 패스워드 값을 넣어줘야 한다는 것이다.

private UserDetails createUserDetails(User user) {
        GrantedAuthority authority = new SimpleGrantedAuthority(user.getUserRole().toString());
        return new org.springframework.security.core.userdetails.User(user.getId(),
                "",
                Collections.singleton(authority));
    }

기존에 패스워드는 빈값으로 둘다 ""을 넘겨줬다. 하지만 raw password이므로 자격증명에 계속 실패했던 것이다. 인코딩된 값을 넘겨주니 정상 작동한다.

private UserDetails createUserDetails(User user) {
        GrantedAuthority authority = new SimpleGrantedAuthority(user.getUserRole().toString());
        return new org.springframework.security.core.userdetails.User(user.getId(),
                passwordEncoder.encode(""),
                Collections.singleton(authority));
    }
profile
개발자로 입사했지만 정체성을 잃어가는중... 다시 준비 시작이다..

0개의 댓글