[스프링 시큐리티] JwtTokenProvider, JwtAuthenticationFilter

jjuya·2023년 12월 17일
0
post-thumbnail

JwtTokenProvider

public class JwtTokenProvider {

    // ** JWT 토큰의 만료 시간을 1시간으로 설정.
    private static final Long EXP = 1000L * 60 * 60;

    // ** 인증 헤더에 사용될 토큰의 접두어 ("Bearer ")
    public static final String TOKEN_PREFIX = "Bearer ";
    
    // ** 인증 헤더의 이름을 "Authorization"으로 설정.
    public static final String HEADER = "Authorization";
    
    // ** 토큰의 서명을 생성하고 검증할 때 사용하는 비밀 키
    private static final String SECRET = "SECRET_KEY";

    // ** User 객체의 정보를 사용해 JWT 토큰을 생성하고 반환.
    public static String create(User user) {

        // ** StringArrayConverter 객체 생성
        StringArrayConverter stringArrayConverter = new StringArrayConverter();

        // ** User의 권한 정보를 String 로 변경
        String roles = stringArrayConverter.convertToDatabaseColumn(
                user.getRoles()
        );

        String jwt = JWT.create()
                .withSubject(user.getEmail()) // ** 토큰의 대상정보 셋팅 
                .withExpiresAt(new Date(System.currentTimeMillis() + EXP)) // ** 시간 설정
                .withClaim("id", user.getId()) // ** id설정
                .withClaim("roles", roles) // ** 권한정보 설정
                .sign(Algorithm.HMAC512(SECRET)); // ** jwt 생성 알고리즘 설정

        return TOKEN_PREFIX + jwt;
    }


    // **  JWT 토큰 문자열을 검증하고, 유효하다면 디코딩된 DecodedJWT 객체를 반환.
    public static DecodedJWT verify(String jwt) throws SignatureVerificationException, TokenExpiredException {

        // ** 토큰 검증을 시작.
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512(SECRET))
                .build()
                .verify(jwt);

        return decodedJWT;
    }

}

참고

DecodedJWT decodedJWT = JwtTokenProvider.verify(jwt);
String userEmail = decodedJWT.getSubject(); // 사용자 이메일
String userRoles = decodedJWT.getClaim("roles").asString(); // 사용자 권한

해당 방법으로 사용자 이메일과 사용자 권한을 읽어올수 있다.



JwtAuthenticationFilter

@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    // ** Http 요청이 발생할 때마다 호출되는 메서드.
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        String prefixJwt = request.getHeader(JwtTokenProvider.HEADER);

        // ** 헤더가 없다면 더이상 이 메서드에서 할 일은 없음. 다음으로 넘김.
        if(prefixJwt == null) {
            chain.doFilter(request, response);
            return;
        }

        // ** Bearer 제거.
        String jwt = prefixJwt.replace(JwtTokenProvider.TOKEN_PREFIX, "");

        try {
            log.debug("토근 있음.");

            // ** 토큰 검증
            DecodedJWT decodedJWT = JwtTokenProvider.verify(jwt);

            // ** 사용자 정보 추출.
            int id = decodedJWT.getClaim("id").asInt();
            String roles = decodedJWT.getClaim("roles").asString();

            // ** 권한 정보를 문자열 리스트로 변환.
            StringArrayConverter stringArrayConverter = new StringArrayConverter();
            List<String> rolesList = stringArrayConverter.convertToEntityAttribute(roles);

            // ** 추출한 정보로 유저를 생성.
            User user = User.builder().id(id).roles(rolesList).build();
            CustomUserDetails customUserDetails = new CustomUserDetails(user);

            // ** Spring Security 가 인증 정보를 관리하는데 사용.
            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    customUserDetails,
                    customUserDetails.getPassword(),
                    customUserDetails.getAuthorities()
            );

            // ** SecurityContext에 저장.
            SecurityContextHolder.getContext().setAuthentication(authentication);
            log.debug("인증 객체 생성");
        }
        catch (SignatureVerificationException sve) {
            log.debug("토큰 검증 실패");
        }
        catch (TokenExpiredException tee) {
            log.debug("토큰 사용 만료");
        } finally {
            // ** 필터로 응답을 넘긴다.
            chain.doFilter(request, response);
        }
    }
}
profile
Review the Record⭐

0개의 댓글