Home JWT Token 검증 구현
Post
Cancel

JWT Token 검증 구현


build.gradle

JWT Token을 검증하기 위해선 Spring Security를 사용해야한다.

1
implementation 'org.springframework.boot:spring-boot-starter-security'

Spring Filter

JWT

Filter는 Spring DispatcherServlet에 요청이 들어오기 전 동작하는 자바 서블릿에서 제공해주는 기능이다.
이 기능을 사용하면 Spring에 요청값이 들어오기 전 Jwt 토큰 검사를 할 수 있다.
Filter는 아래와 같은 용도 사용된다.

  • 공통된 보안 및 인증/인가 관련 작업
  • 모든 요청에 대한 로깅 또는 감사
  • 이미지/데이터 압축 및 문자열 인코디
  • Spring과 분리되어야 하는 기능

코드

JwtTokenProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Component
public class JwtTokenProvider {

    private final Key key;

    .... 이전 코드
    
    // 토큰 만료여부 체크
    public boolean extractSubject(String accessToken) {
        Claims claims = parseClaims(accessToken);
        return !claims.getExpiration().before(new Date());
    }

    private Claims parseClaims(String accessToken) {
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(accessToken)
                .getBody();
    }
    
    // Request의 Header에서 token 값을 가져옵니다. "Authorization" : "TOKEN값'
    public String resolveToken(HttpServletRequest request) {
        return request.getHeader("Authorization");
    }
    
    // 토큰에서 회원 정보 추출
    public String getUserPk(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody().getSubject();
    }
    
    // JWT 토큰에서 인증 정보 조회
    public Authentication getAuthentication(String token) {
    	/*
    	 * UserDetails 가져오는 비지니스 로직 추가되야함
    	 * */
        return new UsernamePasswordAuthenticationToken(this.getUserPk(token), "",  Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
    }
}

JwtAuthenticationFilter

Token인증이 필요한 API 메소드 호출 이전 Header에 저장되어있는 토큰을 가져와 유효여부를 체크한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
    	try {
    		// 헤더에서 JWT 를 받아옵니다.
            String token = jwtTokenProvider.resolveToken((HttpServletRequest) request);
            // 유효한 토큰인지 확인합니다.
            if (token != null && jwtTokenProvider.extractSubject(token)) {
                // 토큰이 유효하면 토큰으로부터 유저 정보를 받아옵니다.
                Authentication authentication = jwtTokenProvider.getAuthentication(token);
                // SecurityContext 에 Authentication 객체를 저장합니다.
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (ExpiredJwtException e) {
            //토큰 만료 에러처리
        } catch (JwtException | IllegalArgumentException e) {
            //Jwt 에러처리
        } catch (SignatureException e) {
            //Signature 불일치 에러처리
        }
        
    	filterChain.doFilter(request, response);
    }
}

SecurityConfig

Config에서 위에 코딩한 Filter를 설정해주고 인증이 필요한 Url을 지정해준다.
아래와 같이 지정하면 /auth/**(token 발급 API는 token인증이 필요 없음)을 제외한 url은 Header에 들어간 token인증을 하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Configuration
@EnableMethodSecurity	
@RequiredArgsConstructor
public class SecurityConfig {
	
	private final JwtTokenProvider jwtTokenProvider;
 
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
        	.httpBasic(HttpBasicConfigurer::disable)
        	.csrf(AbstractHttpConfigurer::disable)
        	.authorizeHttpRequests(authorize -> authorize
        			.requestMatchers(
        					new AntPathRequestMatcher("/auth/**") // auth url로 시작하는 API는 JwtAuthenticationFilter를 타지않음
        					)
        			.permitAll()
        			.requestMatchers(
        					new AntPathRequestMatcher("/**") // auth를 제외한 모든 url은 JwtAuthenticationFilter를 거침
        					)
        			.authenticated()
        			.anyRequest().denyAll()
        			)
        	.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class);
 
        return http.build();
    }

}

테스트

JWT 이란? POST 하단에 테스트 참고

This post is licensed under CC BY 4.0 by the author.
Contents