public class JwtTokenUtils {
public static Boolean validate(String token, String userName, String key) {
String usernameByToken = getUsername(token, key);
return usernameByToken.equals(userName) && !isTokenExpired(token, key);
}
public static Claims extractAllClaims(String token, String key) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey(key))
.build()
.parseClaimsJws(token)
.getBody();
}
public static String getUsername(String token, String key) {
return extractAllClaims(token, key).get("username", String.class);
}
private static Key getSigningKey(String secretKey) {
byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
return Keys.hmacShaKeyFor(keyBytes);
}
public static Boolean isTokenExpired(String token, String key) {
Date expiration = extractAllClaims(token, key).getExpiration();
return expiration.before(new Date());
}
public static String generateAccessToken(String username, String key, long expiredTimeMs) {
return doGenerateToken(username, expiredTimeMs, key);
}
private static String doGenerateToken(String username, long expireTime, String key) {
Claims claims = Jwts.claims();
claims.put("username", username);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expireTime))
.signWith(getSigningKey(key), SignatureAlgorithm.HS256)
.compact();
}
}
doGenrateToken메서드에서 토큰 생성하고extractAllClaims 메서드에서 모든 클레임(핵심 데이터)을 추출한다.
JWT구조 : 헤더(타입, 서명알고리즘), 페이로드 (사용자 정보,클레임), 서명 (헤더와 페이로드기반으로 비밀키를 사용하여 생성)
@Slf4j
@RequiredArgsConstructor
public class JwtTokenFilter extends OncePerRequestFilter {
private final UserService userService;
private final String secretKey;
private final static List<String> TOKEN_IN_PARAM_URLS = List.of("/api/v1/users/alarm/subscribe");
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws ServletException, IOException {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
final String token;
try {
if (TOKEN_IN_PARAM_URLS.contains(request.getRequestURI())) {
log.info("Request with {} check the query param", request.getRequestURI());
token = request.getQueryString().split("=")[1].trim();
} else if (header == null || !header.startsWith("Bearer ")) {
log.error("Authorization Header does not start with Bearer {}", request.getRequestURI());
chain.doFilter(request, response);
return;
} else {
token = header.split(" ")[1].trim();
}
String userName = JwtTokenUtils.getUsername(token, secretKey);
User userDetails = userService.loadUserByUsername(userName);
if (!JwtTokenUtils.validate(token, userDetails.getUsername(), secretKey)) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null,
userDetails.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (RuntimeException e) {
chain.doFilter(request, response);
return;
}
chain.doFilter(request, response);
}
}
이필터는 HTTP요청을 가로채어 JWT를 검증하고, 인증된 사용자 정보를 SecurityContext에 설정한다.( OncePerRequestFilter 를상속)
TOKEN_IN_PARAM_URLS를 쿼리 파라미터를 통해 JWT를 추출한다. 그리고 JWT에서 사용자 이름을 추출하고 , 해당사용자 정보를 UserService를 통해 로드한다. JWT유효성 검증 후 유효하지 않은 경우 다음 필터로 요청을 전달한다.
유효한 경우 UsernamePasswordAuthenticationToken 을 생성하여 SecurityContext에 인증정보를 저장한다.
LIST
'4차산업혁명의 일꾼 > Spring' 카테고리의 다른 글
스프링부트에서 React 연결 (0) | 2024.12.26 |
---|---|
카프카와 레디스 사용법 정리 (0) | 2024.12.26 |
비동기 클라이언트 수신 SseEmitter 사용법과 어노테이션 끄적 (2) | 2024.12.25 |
스프링부트 3.x 연구 (0) | 2024.12.04 |
스프링부트 2.x 연구 (2) | 2024.12.04 |