[Spring] Spring Security + JWT Token(2)

DreamJJW·2023년 10월 12일
0

공부 노트

목록 보기
11/18

JWT 구조

Jwt Token은 Header || Payload || Signature
이 3개의 구조로 이루어져 있다.

Header는 토큰 타입 ex) 'jwt'과 암호화 타입 ex) SHA256 정보가 담겨있고,
Payload에는 토큰의 각종 정보가 담겨있는데 정보의 단락 단위를 클레임(Claim)이라고 한다. 쉽게 말하자면 Jwt의 Body라고 생각하면 된다.
마지막으로 Signature에는 헤더의 인코딩값과, 정보의 인코딩값을 합친후 주어진 비밀키이 해쉬화된 데이터가 담겨있다.

토큰 생성

public static String generateToken(String username, String key, long expiredTimeMs) {
        Claims claims = Jwts.claims();
        claims.put("username", username);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiredTimeMs))
                .signWith(getKey(key),SignatureAlgorithm.HS256)
                .compact();
    }

Key 가져오기

private static Key getKey(String key) {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        return Keys.hmacShaKeyFor(keyBytes);
    }

--------------------------------------------------------------------

// getBytes 문서
public byte[] getBytes(Charset charset) {
        if (charset == null) throw new NullPointerException();
        return encode(charset, coder(), value);
     }
     
--------------------------------------------------------------------, 우리가 사용하는 IDE의 default charset을 사용하여 문자열을 byte로 인코딩하여 byte 배열에 넣어서 
반환해준다는 말이다.

마지막에는 Keys.hmacShaKeyFor(keyBytes)Key 객체를 만들어 반환해준다.

Claim 추출

private static Claims extractClaims(String token, String key) {
        return Jwts.parserBuilder().setSigningKey(getKey(key))
                .build().parseClaimsJwt(token).getBody();
    }


Filter.java

public class JwtTokenFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // header 꺼냄
        final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (header == null || !header.startsWith("Bearer")) {
            log.error("Error occurs - Header Not Found");
            filterChain.doFilter(request, response);
            return;
        }
        try {
            final String token = header.split(" ")[1].trim();
            String username = "";

            // token 유효한지
            // username 유효한지 체크

            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                    null, null, null
            );
            SecurityContextHolder.getContext().getAuthentication();

        } catch (RuntimeException e) {

            // 이게 있어야 다음 필터로 넘어감
            filterChain.doFilter(request, response);
            return;
        }

        filterChain.doFilter(request, response);

    }
}

OncePerRequestFilter를 사용하는 이유 ->

한 요청에 대해 한번만 실행하는 필터이다. 포워딩이 발생하면 필터 체인이 다시 동작되는데, 인증은 여러번 처리가 불필요하기에 한번만 처리를 할 수 있도록 도와주는 역할을 한다.

즉, 새로운 요청에 계속해서 필터를 거쳐야하는 낭비를 방지할 수 있다.

OncePerRequestFilter를 상속하여 구현한 경우 doFilter 대신 doFilterInternal 메서드를 구현하면 된다.

profile
간절한 사람

0개의 댓글