반응형
1. JWT 기반 인증의 흐름
JWT를 사용한 인증 과정은 다음과 같이 진행됩니다:
- 사용자가 로그인하면 서버는 사용자 정보를 검증한 후, JWT 토큰을 생성하여 클라이언트에 반환합니다.
- 클라이언트는 이후 요청 시마다 이 JWT를 헤더에 포함하여 서버로 전달합니다.
- 서버는 전달받은 JWT의 유효성을 검증한 후 요청을 처리합니다.
2. 프로젝트 환경 설정
- Spring Boot 3.x
- Dependencies: Spring Web, Spring Security, jjwt(JSON Web Token), Lombok
Maven Dependency 추가:
<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>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
3. JWT 유틸 클래스 작성
JWT 생성과 검증을 담당하는 유틸리티 클래스를 작성합니다.
import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtTokenProvider {
private final String SECRET_KEY = "your-secret-key";
private final long VALIDITY_MS = 3600000; // 1 hour
public String createToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + VALIDITY_MS))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
4. JWT 필터 구현
JWT를 요청의 헤더에서 추출하여 인증하는 필터를 작성합니다.
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider jwtTokenProvider;
public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String token = getTokenFromRequest(request);
if (token != null && jwtTokenProvider.validateToken(token)) {
String username = jwtTokenProvider.getUsernameFromToken(token);
User user = new User(username, "", new ArrayList<>());
var auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
private String getTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
5. Spring Security 설정
작성한 JWT 필터를 Security Filter Chain에 등록합니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
6. 테스트
- /auth/login으로 사용자 인증 후 JWT를 발급받습니다.
- 다른 API를 호출할 때 Authorization: Bearer <JWT> 헤더를 포함합니다.
- 잘못된 JWT나 만료된 JWT를 제공하면 요청이 차단됩니다.
'SpringBoot' 카테고리의 다른 글
SpringBoot -비밀번호 암호화 및 사용자 인증하기 (0) | 2024.11.24 |
---|---|
SpringBoot - JWT 구현 (기초) (0) | 2024.11.23 |
SpringBoot - SLF4J (0) | 2024.11.17 |
SpringBoot - 백엔드 개발할 때 알아두면 좋은 기술 스택 (1) | 2024.11.16 |
SpringBoot - Could not find artifact mysql:mysql-connector-java:pom:unknown in mvn2s [에러] (0) | 2024.09.24 |