1. 필요한 의존성 추가
먼저 JWT를 처리하기 위한 라이브러리를 프로젝트에 추가해야 합니다. 이 예제에서는 'jjwt' 라이브러리를 사용합니다.
io.jsonwebtoken
jjwt-api
0.11.5
io.jsonwebtoken
jjwt-impl
0.11.5
runtime
io.jsonwebtoken
jjwt-jackson
0.11.5
runtime
2. JWT 유틸리티 클래스 구현
JWT를 생성하고 검증하는 메서드를 포함한 유틸리티 클래스를 구현합니다.
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
public class JwtUtil {
private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 864_000_000; // 10 일
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(key)
.compact();
}
public static String extractUsername(String token) {
return extractClaims(token).getSubject();
}
public static boolean validateToken(String token) {
try {
extractClaims(token);
return true;
} catch (Exception e) {
return false;
}
}
private static Claims extractClaims(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
}
이 클래스는 토큰 생성, 사용자 이름 추출, 토큰 유효성 검증 등의 기능을 제공합니다.
3. 인증 서비스 구현
사용자 인증을 처리하고 JWT를 생성하는 서비스를 구현합니다.
public class AuthenticationService {
public String authenticate(String username, String password) {
// 여기에 실제 사용자 인증 로직을 구현합니다.
// 예를 들어, 데이터베이스에서 사용자 정보를 확인하는 등의 작업을 수행합니다.
if (isValidUser(username, password)) {
return JwtUtil.generateToken(username);
} else {
throw new RuntimeException("Invalid credentials");
}
}
private boolean isValidUser(String username, String password) {
// 실제 사용자 검증 로직을 구현합니다.
// 이 예제에서는 간단히 true를 반환합니다.
return true;
}
}
4. JWT 필터 구현
HTTP 요청을 가로채고 JWT를 검증하는 필터를 구현합니다.
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtAuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String token = httpRequest.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
if (JwtUtil.validateToken(token)) {
String username = JwtUtil.extractUsername(token);
// 여기에서 사용자 인증 정보를 설정합니다.
// 예: SecurityContextHolder.getContext().setAuthentication(...)
}
}
chain.doFilter(request, response);
}
}
5. 사용 예시
구현한 JWT 인증 시스템을 사용하는 예시입니다.
public class AuthController {
private AuthenticationService authService = new AuthenticationService();
public String login(String username, String password) {
try {
String token = authService.authenticate(username, password);
return "Login successful. Token: " + token;
} catch (RuntimeException e) {
return "Login failed: " + e.getMessage();
}
}
public String accessProtectedResource(String token) {
if (JwtUtil.validateToken(token)) {
String username = JwtUtil.extractUsername(token);
return "Access granted for user: " + username;
} else {
return "Access denied: Invalid token";
}
}
}
6. 보안 고려사항
- 비밀 키를 안전하게 관리하세요. 프로덕션 환경에서는 환경 변수나 보안 저장소를 사용하여 키를 관리해야 합니다.
- 토큰의 만료 시간을 적절히 설정하세요. 너무 길게 설정하면 보안 위험이 증가할 수 있습니다.
- HTTPS를 사용하여 토큰을 안전하게 전송하세요.
- 필요한 경우 토큰 갱신 메커니즘을 구현하세요.
- 토큰에 민감한 정보를 포함하지 마세요.
결론
Java를 사용하여 JWT 인증을 구현하는 것은 상대적으로 간단하며, 강력한 보안 기능을 제공합니다. 이 예제를 기반으로 실제 애플리케이션의 요구사항에 맞게 확장하고 커스터마이즈할 수 있습니다. JWT를 사용함으로써 서버의 상태를 유지하지 않고도 안전하게 사용자를 인증할 수 있으며, 마이크로서비스 아키텍처에서도 효과적으로 활용할 수 있습니다.