Spring Security와 JWT(JSON Web Token)를 사용하여 인증된 사용자를 위한 JWT 토큰을 생성하는 필터입니다. 이 필터는 OncePerRequestFilter를 확장하여 한 요청당 한 번만 실행되도록 설계되었습니다.
package com.booktory.booktoryserver.config;
import com.booktory.booktoryserver.config.constants.SecurityConstants;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class JWTTokenGeneratorFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(null != authentication){
SecretKey key = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8));
String jwt = Jwts.builder().setIssuer("Book Tory").setSubject("JWT Token")
.claim("username", authentication.getName())
.claim("authorities", populateAuthorities(authentication.getAuthorities()))
.setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + 30000000))
.signWith(key).compact();
response.setHeader(SecurityConstants.JWT_HEADER, jwt);
}
}
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !request.getServletPath().equals("/user");
}
private String populateAuthorities(Collection<? extends GrantedAuthority> authorities) {
Set<String> authoritiesSet = new HashSet<>();
for(GrantedAuthority authority : authorities) {
authoritiesSet.add(authority.getAuthority());
}
return String.join(",", authoritiesSet);
}
}
클래스 정의 및 doFilterInternal 메소드
public class JWTTokenGeneratorFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(null != authentication){
SecretKey key = Keys.hmacShaKeyFor(SecurityConstants.JWT_KEY.getBytes(StandardCharsets.UTF_8));
String jwt = Jwts.builder()
.setIssuer("Book Tory") // 토큰 발행자 설정
.setSubject("JWT Token") // 토큰 주제 설정
.claim("username", authentication.getName()) // 사용자 이름 클레임 추가
.claim("authorities", populateAuthorities(authentication.getAuthorities())) // 사용자 권한 클레임 추가
.setIssuedAt(new Date()) // 토큰 발행 시간 설정
.setExpiration(new Date((new Date()).getTime() + 30000000)) // 토큰 만료 시간 설정
.signWith(key) // 서명 키로 토큰 서명
.compact(); // 토큰 생성
response.setHeader(SecurityConstants.JWT_HEADER, jwt);
}
}
- doFilterInternal 메소드는 각 요청마다 한 번 실행됩니다.
- Authentication 객체를 가져와 현재 인증된 사용자의 정보를 확인합니다.
- 인증 정보가 있으면 JWT 토큰을 생성합니다.
- Keys.hmacShaKeyFor를 사용하여 비밀 키를 생성합니다.
- Jwts.builder를 사용하여 JWT 토큰을 생성합니다. 여기에는 발행자, 주제, 클레임(사용자명, 권한), 발행 시간, 만료 시간 등이 포함됩니다.
- 생성된 JWT 토큰을 응답 헤더에 추가합니다.
setIssuer와 setSubject의 역할
setIssuer(String issuer)
- 설명: 이 메소드는 JWT 토큰의 발행자를 설정합니다. 발행자는 토큰을 생성한 엔터티를 나타냅니다.
- 용도: 토큰이 어디에서 발행되었는지를 식별하는 데 사용됩니다. 예를 들어, 특정 서비스나 시스템에서 발행된 토큰임을 나타낼 수 있습니다.
.setIssuer("Book Tory")
여기서 "Book Tory"는 토큰을 발행한 시스템 또는 서비스의 이름입니다.
setSubject(String subject)
- 설명: 이 메소드는 JWT 토큰의 주제를 설정합니다. 주제는 토큰의 대상자를 나타내며, 주로 토큰이 특정 사용자 또는 엔터티와 관련이 있음을 나타냅니다.
- 용도: 토큰이 누구를 위해 발행되었는지를 식별하는 데 사용됩니다. 주로 사용자 ID나 이름과 같은 정보를 포함합니다.
.setSubject("JWT Token")
여기서 "JWT Token"은 토큰의 주제를 나타내는 값입니다.
shouldNotFilter 메소드
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return !request.getServletPath().equals("/user");
}
이 메소드는 필터가 특정 경로(/user)에만 적용되도록 합니다. /user 경로가 아닌 다른 경로의 요청에 대해서는 필터를 적용하지 않습니다.
populateAuthorities 메소드
private String populateAuthorities(Collection<? extends GrantedAuthority> authorities) {
Set<String> authoritiesSet = new HashSet<>();
for(GrantedAuthority authority : authorities) {
authoritiesSet.add(authority.getAuthority());
}
return String.join(",", authoritiesSet);
}
}
- 이 메소드는 사용자의 권한을 문자열 형태로 변환합니다.
- GrantedAuthority 객체의 컬렉션을 받아 각 권한을 문자열로 추출하고, 쉼표로 구분된 단일 문자열로 반환합니다.
요약
- JWTTokenGeneratorFilter 클래스: JWT 토큰을 생성하여 응답 헤더에 추가하는 필터입니다.
- doFilterInternal 메소드: 인증된 사용자가 있을 경우 JWT 토큰을 생성하고 응답 헤더에 추가합니다.
- shouldNotFilter 메소드: 특정 경로(/user)에서만 필터가 실행되도록 설정합니다.
- populateAuthorities 메소드: 사용자의 권한을 문자열로 변환합니다.