JWT Token 파일 구성
1. SecurityConfig.java
Java Spring Security에 대한 설정과 JWT 필터 등록을 한다. @EnableWebSecurity 어노테이션을 추가해줘야 하며, 백엔드 상황에 따라 접근 정책을 작성해주면 된다.
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeHttpRequests(
(request) -> request.requestMatchers( "/" )
.permitAll()
.anyRequest()
.authenticated()
)
.httpBasic(Customizer.withDefaults())
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
2. JwtAuthenticationFilter
Java Spring Security에 등록할 JWT 필터 세부 구현사항을 정의한다. Java Spring에서 제공하는 OncePerRequestFilter를 상속받아 doFilterInternal를 재정의한다.
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
var jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && JwtTokenProvider.validateToken(jwt)) {
var userId = JwtTokenProvider.parseUserId(jwt);
var authentication = new UserAuthentication(userId);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
if (!StringUtils.hasText(jwt)) {
request.setAttribute("unauthorization", "401 인증키 없음.");
}
if (JwtTokenProvider.validateToken(jwt)) {
request.setAttribute("unauthorization", "401-001 인증키 만료.");
}
}
} catch (Exception ex) {
System.out.println("Could not set user authentication in security context");
}
filterChain.doFilter(request, response);
}
}
3. JwtTokenProvider
JWT Token에 관련된 필드, 메서드를 정의한다. Token 생성(Access, Refresh Token), 검증, 디코딩 등을 구체적인 구현 사항을 작성한다.
public class JwtTokenProvider {
private static final Key SIGNING_KEY = getSigningKey();
private static final String secretKey = "secertKey";
private static final int ACCESS_TOKEN_DURATION_SECONDS = 60;
private static final int REFRESH_TOKEN_DURATION_SECONDS = 60 * 30;
public static TokenResponse generateToken(Long userId) {
var now = Instant.now();
var expiryDateOfAccessToken = now.plusSeconds(ACCESS_TOKEN_DURATION_SECONDS);
var expiryDateOfRefreshToken = now.plusSeconds(REFRESH_TOKEN_DURATION_SECONDS);
String accessToken = Jwts.builder()
.setClaims(Map.of(
"userId", userId,
"iat", now.getEpochSecond(),
"exp", expiryDateOfAccessToken.getEpochSecond()
))
.signWith(SIGNING_KEY, SignatureAlgorithm.HS256)
.compact();
String refreshToken = Jwts.builder()
.setClaims(Map.of(
"userId", userId,
"iat", now.getEpochSecond(),
"exp", expiryDateOfRefreshToken.getEpochSecond()
))
.signWith(SIGNING_KEY, SignatureAlgorithm.HS256)
.compact();
return TokenResponse.builder()
.accessToken(accessToken)
.accessExpiredDate(expiryDateOfAccessToken.atZone(ZoneId.systemDefault()).toLocalDateTime())
.refreshToken(refreshToken)
.refreshExpiredDate(expiryDateOfRefreshToken.atZone(ZoneId.systemDefault()).toLocalDateTime())
.build();
}
public static Long parseUserId(String token) {
return Jwts.parserBuilder()
.setSigningKey(SIGNING_KEY)
.build()
.parseClaimsJws(token)
.getBody()
.get("userId", Long.class);
}
public static boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(SIGNING_KEY).build().parseClaimsJws(token);
System.out.println(SIGNING_KEY);
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
System.out.println("Invalid JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("Expired JWT Token");
} catch (UnsupportedJwtException e) {
System.out.println("Unsupported JWT Token");
} catch (IllegalArgumentException e) {
System.out.println("JWT claims string is empty.");
}
return false;
}
public static Key getSigningKey() {
return Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
}
}
4. UserAuthentication
JWT Token에 담을 Authentication 정보를 설정한다. Java Spring에서 지원하는 AbstractAuthenticationToken을 상속 받아 작성을 하며, 상황에 맞게 setAuthenticated, getCredentials같은 메서드를 재정의한다.
public class UserAuthentication extends AbstractAuthenticationToken {
private final AuthPayload authPayload;
public UserAuthentication(Long userId) {
super(null);
this.authPayload = new AuthPayload(userId);
setAuthenticated(true);
}
@Override
public void setAuthenticated(boolean authenticated) {
super.setAuthenticated(authenticated);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public AuthPayload getPrincipal() {
return authPayload;
}
}
JWT Token 적용 결과
위와 같이 4개의 파일을 추가적으로 작성하면 정상적으로 JWT Token이 발급되는 것을 확인할 수 있다. 또한 그 Token을 통해서 백엔드에 접근이 이루어지는 것을 확인할 수 있다.
Debug 모드 사용
프로젝트에 JWT 적용할 때 문제가 발생할 수 있다. 이 경우 Java Security Debug 모드를 사용 하면 좀 더 쉽게 해결하 수 있다. Debug 모드 적용은 SecurityConfig 파일의 @EnableWebSecurity 어노테이션에 debug=true 값을 주면 된다.
@EnableWebSecurity(debug = true)
public class SecurityConfig {
...
}
'프로젝트' 카테고리의 다른 글
[ 프로젝트 ] Swagger 적용 (0) | 2023.05.14 |
---|---|
[ 프로젝트 ] JWT 라이브러리 검증 메서드 이름 (1) | 2023.04.23 |
[ Docker ] Docker hub를 사용해 Jar 파일 EC2에서 실행해보기 (0) | 2023.03.26 |
[ AWS ] EC2에 Swap Memory 적용 (0) | 2023.03.05 |
[ AWS ] Jenkins과 Elastic beanstalk을 통한 애플리케이션 배포 (0) | 2023.02.27 |