Spring

스프링부트 - Spring Security JWT인증하기

코린이 파닥거리기 2025. 5. 15. 02:21
728x90
반응형
SMALL

JWT는 이전 포스트에서 다뤘기 때문에 간단하게 소개만 하겠다.

사용자 인증 정보를 JSON형태로 담아 서명한 토큰

 

세션을 사용하여 로그인 할 때

로그인 정보가 서버(세션)에 저장되어 있다.

JWT로그인을 사용하여 인증할 때

로그인 정보가 클라이언트(브라우저)에 저장되어있다.

그래서 서버 입장에서 요청을 보내는 사람이 누구인지 알 수 없다.

클라이언트가 스스로 누구인지 증명을 해야하는데 이 JWT를 이용해서 증명한다.

 

보통 4가지의 파일을 가지고 가야된다.

  • JwtUtil.java ( 토큰 생성 및 검증 도구 클래스)
  • JwtAuthFilter.java (요청마다 토큰을 꺼내서 검증하는 필터)
  • JwtLoginController.java (로그인 시 토큰 발급하는 컨트롤러)
  • SecurityConfig.java (필터 등록 및 시큐리티 설정 수정)
                <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.5</version>
        </dependency>
        <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
        </dependency>
        <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.11.5</version>
        <scope>runtime</scope>
        </dependency>

젤 처음에는 pom.xml에 해당 의존성을 주입시켜준다.

 

package com.example.security.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;

@Component
public class JwtUtil {
    // 256비트(32바이트) 이상의 키를 필요
    // Base64는 문자 1개가 6비트로 처리 (3바이트 == Base64 문자 4개)
    // 문자 4개당 3바이트를 표현하므로 32바이트를 넘기려면 최소 43자 이상 필요
    // (40개 == 30바이트, 43자 == 32.25바이트, 44자 == 33바이트)
    private final String SECRET = "1234567890123456789012345678901234567890123";
    private final long EXPIRATION = 1000 * 60 * 60; // 1시간

    private Key getSigningKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET);
        return Keys.hmacShaKeyFor(keyBytes);
    }

    public String createToken(String username) { // JWT 생성
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }

    public String extractUsername(String token) { // 정보 추출
        return Jwts.parserBuilder()
                .setSigningKey(getSigningKey()).build()
                .parseClaimsJws(token)
                .getBody().getSubject();
    }

    public boolean validate(String token) { // 유효성 검사
        try {
            Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }
}

이 코드는 JWT를 생성 정보 추출 유효성 검사하는 도구이다.

 

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
            FilterChain filterChain)
            throws ServletException, IOException{
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            if (jwtUtil.validate(token)) {
                String username = jwtUtil.extractUsername(token);
                //이부분은 아무나 인증을 해주고, DB를 연결안하는 코드이다.
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        username, null, List.of());
                // UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // UsernamePasswordAuthenticationToken authentication =
                // new UsernamePasswordAuthenticationToken(
                // userDetails, null, List.of());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(request, response);
    }

요청할때 마다 JWT를 서버로 전달하는 역할을 하는 코드이다.

그리고 전달받은 코드가 정말로 유효한 값인지 유효성 검사까지 한다.

왜냐하면 JWT는 토큰 유효기간이 있기때문이다.

SecurityContextHolder.getContext().setAuthentication(authentication);

SecurityContextHolder에서 로그인 한 것처럼 만들어준다.(이 필터의 핵심코드이다.)

.addFilterBefore(new JwtAuthFilter(jwtUtil, userDetailsService),
                        UsernamePasswordAuthenticationFilter.class)
                .csrf(csrf -> csrf.disable())

SecurityConfig파일에 해당 addFilterBefore메서드에 넣어주고 테스트를 진행해보면 된다.

그럼 이제 POST방식으로 login을 해보겠다.

다음과 같이 토큰이 발행이 된 것을 볼 수 있다.

 

그럼 이 값으로 인증절차까지 해보겠다.

인증은 GET방식으로 해야되는데 이유는 이미 인증된 사용자의 토큰을 가지고 URL에 접근하는 것이니까 

Header에 인증받은 토큰값만 넘겨주면 되는 것이기 때문

Header에 Authorization과 Value에 

무조건 Bearer + 발급받은 Token값으로 해주면

위와 같이 JWT인증된 사용자만 접근 가능한 영역입니다. 라고 뜨게된다.

728x90
반응형
LIST