JWT를 정리한 글입니다
주요 클래스와 해당 클래스의 DI 클래스, 사용 메서드를 정리했습니다
시큐리티필터의 전체적인 설정을 담당하는 클래스
DI
JwtTokenizer
CustomAuthorityUtils
- 1. filterChain()
HttpSecurity의 속성 값을 설정하여 build하는 메서드
보안에 관련한 설정
커스텀 예외처리 헨들러와 커스텀 필터의 적용
Http요청 권한 설정 메서드
- 2. passwordEncoder()
Spring Security에서 사용할 PasswordEncoder를 빈으로 등록하는 메서드
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
- 3. corsConfigurationSource()
CORS정책 설정을 빈으로 등록하는 메서드
CorsConfigurationSource 를 빈으로 등록 .cors(withDefaults())설정 시 사용
new CorsConfigurationSource➝setAllowedOrigins()/setAllowedMethods()new UrlBasedCorsConfigurationSource➝registerCorsConfiguration("/**", configuration)
return UrlBasedCorsConfigurationSource;
extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity>
SecurityConfiguration의 내부 클래스로, 시큐리티 필터에 추가된 커스텀 필터 클래스
- configure()
AbstractHttpConfigurer의 메서드를 오버라이딩
HttpSecurity를 받아 설정을 추가한다
HttpSecurity➝getSharedObject()➝AuthenticationManager
공통으로 사용되는 인증매니저를 호출한다JWT인증 필터 등록
2.new JwtAuthenticationFilter(authenticationManager, jwtTokenizer[DI])
.setFilterProcessesUrl("/v11/auth/login");필터가 동작하는 엔드포인트 설정
.setAuthenticationSuccessHandler(new MemberAuthenticationSuccessHandler());
.setAuthenticationFailureHandler(new MemberAuthenticationFailureHandler());
해당 필터의 성공/실패 핸들러를 커스텀 핸들러로 설정한다JWT유효성 필터 등록(인증필터 실행 전 선행)
3.new JwtVerificationFilter(jwtTokenizer[DI], authorityUtils[DI]);
4.builder.addFilter(jwtAuthenticationFilter)
uilder.addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
기존 시큐리티필터에 추가한다
addFilter()로 필터를 등록하면 기존 스프링 필터보다 먼저 실행된다
먼저 실행된 필터가 요청을 처리함으로 기존 스프링 필터는 활성되지 않는다
extends UsernamePasswordAuthenticationFilter
기존 인증필터를 확장하여 기능하는 JWT인증 필터
DI
AuthenticationManager인증JwtTokenizer토큰 생성
- 1. attemptAuthentication()
Username, Password을 받아 토큰을 생성하고, 토큰으로 검증된 인증을 만드는 메서드
HttpServletRequest➝LoginDto➝authenticationToken
return authenticationManager.authenticate(authenticationToken);
- 2. successfulAuthentication()
검증된 인증을 받아 세부 토큰을 생성하고 헤더에 표시하는 메서드
Authentication➝Member➝accessToken/refreshToken➝responsegetSuccessHandler()
- 3-1 delegateAccessToken()
멤버를 받아 Access Token을 생성하는 메서드
claims의 키 값은 "username"과 "roles", subject는 email
generateAccessToken() 인자는 username, roles, expiration, base64Key
Member➝claims,subjectJwtTokenizer[DI]➝getTokenExpiration(),encodeBase64SecretKey()JwtTokenizer[DI]➝generateAccessToken()->AccessToken
- 3-2 delegateRefreshToken()
멤버를 받아 Refresh Token을 생성하는 메서드
subject는 email
getTokenExpiration()의 인자는
Member➝subjectJwtTokenizer[DI]➝getTokenExpiration(),encodeBase64SecretKey()JwtTokenizer[DI]➝generateRefreshToken()->refreshToken
extends OncePerRequestFilter
DI
JwtTokenizer생성된 토큰 검증용
CustomAuthorityUtils컨텍스트 저장 인증의 권한 생성용
- 1. shouldNotFilter()
OncePerRequestFilter 메서드를 오버라이딩(한번 실행되는 요청필터 관련 클래스)
필터 시작 전 건너뛸지 여부를 확인한다
HttpServletRequest의 해더에서 인증을 추출한다
HttpServletRequest➝Authorization
return authorization == null || !authorization.startsWith("Bearer");
- 2. doFilterInternal()
OncePerRequestFilter의 메서드를 오버라이딩
필터 실행 전 요청 헤더에서 claims 추출하여 검증하고 컨텍스트에 저장하는 메서드
해당 로직에 대한 예외처리를 요청 속성 값으로 설정한다
HttpServletRequest➝verifyJws()->setAuthenticationToContext()try - catch(SignatureException/ExpiredJwtException/Exception){ request.setAttribute()}filterChain.doFilter()
- 3. verifyJws()
요청 헤더의 실제 출력된 값(jws)을 비밀키로 파서를 만들어 검증하는 메서드
검증에 통과하면 claims을 반환한다
jws는 Key "Authorization": value 문자열에서 Bearer를 제외한 부분을 추출한다
HttpServletRequest➝String jwsJwtTokenizer[DI]➝encodeBase64SecretKey()return claims jwtTokenizer.getClaims(jws, base64EncodedSecretKey).getBody()
- 4. setAuthenticationToContext()
검증된 Authentication를 SecurityContext에 저장하는 메서드
credentials이 아닌 인증토큰으로 확인하기 때문에 값이 null 이다
claimsString username
authorityUtils[DI]createAuthorities()
Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
implements AuthenticationFailureHandler
implements AuthenticationSuccessHandler
CustomFilterConfigurer의 configure()로 추가된 JwtAuthenticationFilter 처리(인증)가 성공/실패할 경우 실행되는 핸들러
- onAuthenticationSuccess()
AuthenticationSuccessHandler 인터페이스의 메서드를 오버라이딩
에러 로그를 출력하거나, 검증된 인증과 요청 정보를 추가하여 응답을 커스텀 설정할 수 있다
ex)Authentication/HttpServletRequest
➝HttpServletResponse
➝ErrorResponse아래는 인증처리 실패시 보낼 응답을 커스텀하는 예시
implements AccessDeniedHandler
권한 외 접근시 호출되는 커스텀 클래스
SecurityFilterChain의 http.accessDeniedHandler() 옵션으로 설정 되었다
- handle()
AccessDeniedHandler의 메서드를 오버라이딩
HttpServletRequest, HttpServletResponse, AccessDeniedException을 받는다
ex)HttpServletRequest/AccessDeniedException
➝HttpServletResponse
➝ErrorResponse,log.warn()
implements AuthenticationEntryPoint
미인증시 호출되는 커스텀 클래스
SecurityFilterChain의 http.authenticationEntryPoint() 옵션으로 설정 되었다
- commence()
HttpServletRequest, HttpServletResponse, AuthenticationException을 받는다
ex)HttpServletRequest/AuthenticationException
➝HttpServletResponse
➝ErrorResponse,log.warn()
private String secretKey;
private int accessTokenExpirationMinutes;
private int refreshTokenExpirationMinutes;
Secret Key, access 만료시간, refresh 만료시간에 대한 필드 값
직접 설정하거나 @Value로 프로퍼티에서 받아옴
- 1. encodeBase64SecretKey()
시크릿 키를 Base64 인코팅하여 사용하기 위한 메서드
인코팅된 키는 토큰생성이나 검증을 위한 파싱키를 만드는데 사용된다
- 2. generateAccessToken()
AccessToken을 만드는 메서드
claims, subject, expiration, base64EncodedSecretKey를 받아 생성한다
Jwts.builder()
.setClaims()
.setSubject()
.setIssuedAt()←Calendar.getInstance()
.setExpiration()
.signWith()←getKeyFromBase64EncodedKey()
- 3. generateRefreshToken()
RefreshToken을 만드는 메서드
subject, expiration, base64EncodedSecretKey 받아 생성한다
- 4. getClaims()
jws, 인코팅된 SecretKey로 인증토큰을 파싱하여 유효한지 검증한다
JwtVerificationFilter의 doFilterInternal()에서 호출된다
String base64EncodedSecretKey➝Key➝parseClaimsJws(jws)
return claims;
- 5. getTokenExpiration()
만료시간을 추가설정하는 메서드
JwtAuthenticationFilter의 delegate(Access/Refresh)Token()에서 호출된다
추가되는 만료시간은 토큰 종류에 따라 필드값으로 설정되어 호출된다
calendar.add(Calendar.MINUTE, expirationMinutes);
- 6. getKeyFromBase64EncodedKey()
암호화된 비밀키로 실제 사용되는 키를 만든다
키는 토큰 생성이나 검증에 사용된다
String➝byte[]➝decode()➝Keys.hmacShaKeyFor()➝return key;