본문 바로가기

명사 美 비격식 (무리 중에서) 아주 뛰어난[눈에 띄는] 사람[것]

이론

Json Web Token, JWT

JWT

Json Web Token

웹 애플리케이션 간에 정보를 안전하게 전송하기 위한 개방형 표준(RFC 7519)

 

JWT는 세가지 부분으로 구성된다.

Header: 토큰의 유형과 해싱 알고리즘 등의 메타데이터가 json 형식으로 포함

Payload: 클레임이라 부르는 사용자의 id및 권한등이 포함

Signature: Header와 Payload를 인코딩하고 서명하여 토큰의 유효성을 검증한다.

 

JWT를 발급하고 검증해보자.


스프링 시큐리티를 사용해 구현할 수 있다.

1. 의존성 추가 pom.xml/build.gradle 파일에 필요한 의존성을 추가

 <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-security</artifactId>
   </dependency>
   <dependency>
       <groupId>io.jsonwebtoken</groupId>
       <artifactId>jjwt</artifactId>
       <version>0.9.1</version> <!-- 현재 버전 확인 필요 -->
   </dependency>

 

 

2. JWT 설정을 담당할 클래스를 생성

JwtUtil클래스는 JWT 생성, 유효성 검증 및 클레임 추출 등의 기능을 담당한다.

SECRET_KEY와 expiration 값을 application.proerties 또는 application.yml파일에서 관리하도록 설정.

@Component
public class JwtUtil {

    @Value("${jwt.secret}")
    private String SECRET_KEY;

    @Value("${jwt.expiration}")
    private Long expiration;

    // JWT 토큰에서 클레임을 추출합니다.
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    // JWT 토큰에서 만료 날짜를 추출합니다.
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    // JWT 토큰에서 모든 클레임을 추출합니다.
    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    // 토큰이 만료되었는지 확인합니다.
    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    // 유저 정보를 바탕으로 JWT 토큰을 생성합니다.
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }

    // 토큰 생성 및 서명합니다.
    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder().setClaims(claims).setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
    }

    // 토큰의 유효성을 검사합니다.
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    // 토큰에서 사용자 이름을 추출합니다.
    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }
}

 

 

3. 시큐리티 설정 클래스를 적용하여 JWT인증을 적용

SecurityConfigurer클래스는 사용자 인증 및 JWT 필터 설정을 담당.

JwtRequestFilter는 클라이언트에서 전달된 JWT 토큰을 검증하고 유효한 경우에만 인증을 통화시킨다.

@Configuration
@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests().antMatchers("/authenticate").permitAll() // 인증 요청은 모두 허용
            .anyRequest().authenticated(); // 그 외 요청은 인증 필요

        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

 

 

 

외 추가적인 비즈니스 로직을 구현하거나 보안 설정을 더 강화할 수 있다.