728x90
반응형
JWT(JSON Web Token)란?
JWT(JSON Web Token)는 당사자 간에 정보를 안전하게 전송하기 위한 콤팩트하고 독립적인 방식을 정의하는 개방형 표준입니다.
JWT의 특징
- JSON 형식: 정보를 JSON 객체로 안전하게 전송합니다.
- 구조: Header, Payload, Signature의 세 부분으로 구성됩니다.
- 상태 비저장(Stateless): 서버에 별도의 저장소가 필요 없어 확장성이 뛰어납니다.
- 자가 수용적(Self-contained): 토큰 자체에 필요한 모든 정보를 포함합니다.
- 보안: 디지털 서명을 통해 데이터 무결성을 보장합니다.
보안을 강화하는 방법
- 안전한 비밀 키 사용: JWT 서명에 사용되는 비밀 키를 안전하게 관리하고 정기적으로 교체합니다.
- 짧은 만료 시간 설정: 토큰의 유효 기간을 짧게 설정하여 탈취된 토큰의 악용 가능성을 줄입니다.
- HTTPS 사용: 모든 통신에 HTTPS를 적용하여 토큰 전송 시 암호화를 보장합니다.
- 토큰 저장소 보안: 클라이언트 측에서 토큰을 안전하게 저장합니다 (예: HttpOnly 쿠키 사용).
- 민감한 정보 제외: 토큰 페이로드에 민감한 정보를 포함하지 않습니다.
- 토큰 무효화 메커니즘: 필요 시 토큰을 무효화할 수 있는 시스템을 구현합니다.
- 적절한 서명 알고리즘 사용: 안전한 서명 알고리즘(예: RS256)을 사용합니다.
- 토큰 재사용 방지: 각 요청마다 새로운 토큰을 발급하거나, nonce 값을 사용합니다.
- 정기적인 보안 감사: JWT 구현을 주기적으로 검토하고 업데이트합니다.
장단점
장점
- 상태 비저장(Stateless): 서버에 별도의 저장소가 필요 없어 확장성이 뛰어납니다.
- 자가 수용적(Self-contained): 토큰 자체에 필요한 모든 정보를 포함하여 처리 속도가 빠릅니다.
- 보안성: 디지털 서명을 통해 데이터 무결성을 보장합니다.
- 다양한 플랫폼 지원: JSON 형식을 사용하여 여러 플랫폼에서 쉽게 사용할 수 있습니다.
단점
- 토큰 크기: 포함된 정보가 많을수록 토큰 크기가 커져 네트워크 부하가 증가할 수 있습니다.
- 보안 취약점: 토큰이 탈취되면 만료 전까지 대응이 어렵습니다.
- 토큰 관리: 한번 발급된 토큰은 서버에서 직접 제어가 어려워 관리에 주의가 필요합니다.
- 데이터 노출: 페이로드가 암호화되지 않아 중요 정보 포함 시 보안 위험이 있습니다.
구현방법
1. 의존성 추가
SpringBoot 를 이용하여 아래와 같이 추가를 진행하여 줍니다.
2. JWT 유틸리티 클래스 생성
토큰 생성, 검증, 추출 등의 기능을 담당할 클래스를 만듭니다.
클래스생성
config 패키지 생성
JWTConfig 클래스 생성
@Configuration
public class JwtConfig {
Key key;
// 24시간 유효 토큰 만료 시간 설정 24 * 60 * 60 * 1000
long tokenValidMillisecond = 1 * 60 * 60 * 1000;
public JwtConfig(@Value("${jwt.secret}") String secretKey) {
this.key = Keys.hmacShaKeyFor(secretKey.getBytes());
}
// 토큰 생성 함수
public String createToken(Long userId) {
// 현재 시간 년월일시분초 가져온다.
Date now = new Date();
// 만료 시간 계산한다.
Date validity = new Date(now.getTime() + tokenValidMillisecond);
return Jwts.builder()
.subject(userId.toString())
.issuedAt(now)
.expiration(validity)
.signWith(key, SignatureAlgorithm.HS256)
.compact(); // 토큰 생성 함수
}
// 토큰에 저장된 데이터 가져오는 함수
public Claims getTokenClaims(String token) {
return Jwts.parser()
.verifyWith((SecretKey) key) // Key 를 SecretKey 로 캐스팅
.build()
.parseSignedClaims(token)
.getPayload();
}
3. 인증 필터 구현
요청에서 JWT를 추출하고 검증하는 필터를 만듭니다.
참조
request.getRequestURI().equals("/api/v1/admin/login")
"/api/v1/admin/login" 와 동일한
request.getRequestURI().startsWith("/api/v1/admin/**")
"/api/v1/admin/"으로 시작하는
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtConfig jwtConfig;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// signup, login 요청은 토큰 검증을 하지 않는다.
if(request.getRequestURI().equals("/api/v1/admin/signup") ||
request.getRequestURI().equals("/api/v1/admin/login") ) {
filterChain.doFilter(request, response);
return;
}
// 헤더에서 토큰을 가져온다.
String bearerToken = request.getHeader("Authorization");
// Bearer 토큰번호 입력
if (bearerToken == null || bearerToken.isEmpty() ||
!bearerToken.startsWith("Bearer ")) {
response.setStatus(401);
return;
}
// 토큰에서 Bearer 제거
String token = bearerToken.substring(7);
// 토큰 유효시간 검증
Claims claims = jwtConfig.getTokenClaims(token);
Date expiration = claims.getExpiration();
if(expiration != null && expiration.before(new Date())) {
response.setStatus(401);
return;
}
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(claims.getSubject(),
null, null);
// SecurityContextHolder 에 인증 정보를 저장한다.
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
4. Security 설정
JWT 필터를 Spring Security 설정에 추가합니다.
@Configuration
public class SecurityConfig {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
// 암호화 처리 클래스 빈 등록
// PasswordEncoder 는 Spring Security 에서 제공하는 인터페이스로
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/admin/signup",
"/api/v1/admin/login").permitAll()
.anyRequest().authenticated() )
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
5. 인증 컨트롤러 구현
사용자 인증 및 JWT 발급을 위한 엔드포인트를 만듭니다.
@GetMapping("/api/v1/admin/crm/users/top-reviewers")
public ResponseEntity<ReviewerListResponse>
getTopReviewers(@RequestHeader("Authorization") String token,
@RequestParam ("size")int size){
ReviewerListResponse reviewerListResponse =
crmService.getTopReviewers(token, size);
return ResponseEntity.status(200).body(reviewerListResponse);
}
728x90
반응형
'API > 이론' 카테고리의 다른 글
65. [JAVA] DB 처리 방식 : JDBC, DAO 패턴, JPA (0) | 2025.01.12 |
---|---|
59. [ JAVA ] SpringData JPA로 Repository 인터페이스 생성 (0) | 2025.01.12 |
52. [ JAVA ] Spring Boot : 데이터 유효성 검사 적용방법(validation) (1) | 2025.01.10 |
43. [ JAVA ] JWT(Json Web Token)(보안) 정의 (0) | 2024.12.30 |
37. [ JAVA ] 서버 개발 환경 설정 (1) | 2024.12.18 |