본문 바로가기

카테고리 없음

[Spring Boot] Security6 JWTTokenGeneratorFilter

 

 

 

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 메소드: 사용자의 권한을 문자열로 변환합니다.