반응형
Spring Security와 JWT 통합하기: 단계별 가이드
Spring Boot 애플리케이션에서 JWT(JSON Web Token)를 활용하여 인증과 권한 부여를 구현하면 보안성과 확장성을 높일 수 있습니다. 이 글에서는 Spring Security와 JWT를 통합하여 인증 시스템을 구축하는 방법을 단계별로 설명합니다.
1. JWT란?
JWT(JSON Web Token)는 JSON 데이터를 기반으로 인증 정보를 안전하게 전송하기 위한 토큰 기반 인증 방식입니다.
구조:
JWT는 Header.Payload.Signature
로 구성됩니다.
- Header: 토큰 유형과 해싱 알고리즘 정보를 포함
- Payload: 사용자 정보와 클레임(Claim)을 포함
- Signature: Header와 Payload를 서명하여 변조를 방지
예제 JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsInJvbGVzIjpbIlJPTEVfVVNFUiJdLCJpYXQiOjE2NzMwMjc4ODAsImV4cCI6MTY3MzAyODI0MH0.abc123signature
2. 프로젝트 설정
의존성 추가:
Spring Security와 JWT 관련 라이브러리를 추가합니다.
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</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>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
}
3. JWT 유틸리티 클래스 생성
JWT 토큰을 생성하고 검증하는 유틸리티 클래스를 작성합니다.
JwtUtil.java:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.function.Function;
@Component
public class JwtUtil {
private final String SECRET_KEY = "mysecretkey";
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
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());
}
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public Boolean validateToken(String token, String username) {
final String extractedUsername = extractUsername(token);
return (extractedUsername.equals(username) && !isTokenExpired(token));
}
}
4. Spring Security 구성
JWT를 활용하여 인증을 처리하도록 Spring Security 설정을 작성합니다.
SecurityConfig.java:
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
}
5. 컨트롤러 작성
AuthController.java:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private AuthenticationManager authenticationManager;
@PostMapping("/login")
public String login(@RequestBody AuthRequest authRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
return jwtUtil.generateToken(authRequest.getUsername());
}
}
class AuthRequest {
private String username;
private String password;
// Getter와 Setter
}
6. JWT 필터 작성
Spring Security 필터 체인에서 JWT 토큰을 검증하는 필터를 추가합니다.
JwtFilter.java:
import org.springframework.stereotype.Component;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
public class JwtFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, java.io.IOException {
// JWT 토큰 검증 로직 추가
chain.doFilter(request, response);
}
}
7. 테스트 및 실행
로그인 요청:
- URL:
POST /api/auth/login
- Body:
{ "username": "user", "password": "password" }
- Response: JWT 토큰
- URL:
보호된 엔드포인트 요청:
- URL:
GET /api/protected
- Header:
Authorization: Bearer <JWT>
- URL:
반응형
'Spring Boot' 카테고리의 다른 글
JWT 토큰 만료 시간을 설정하고 관리하는 방법 (0) | 2025.01.08 |
---|---|
Spring Boot에서 Refresh Token을 사용해 JWT 재발급하기 (0) | 2025.01.08 |
Spring Boot에서 디버깅을 시작하는 방법: 에러 로그 읽기 (1) | 2025.01.08 |
Spring Boot에서 의존성 주입(Dependency Injection)을 사용하는 방법 (0) | 2025.01.08 |
Spring Boot 프로젝트를 빌드하고 실행하는 방법 (Maven 활용) (0) | 2025.01.08 |