'hmacShaKeyFor(byte[])' in 'io.jsonwebtoken.security.Keys' cannot be applied to '(io.jsonwebtoken.SignatureAlgorithm)'
JJWT 라이브러리 0.11.x 버전에서 API가 변경되었는데, 이전 버전의 메서드를 사용하려 해서 오류가 발생했다.
새 버전의 API에 맞게 코드를 수정하였다.
// 변경 전
this.key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
// 변경 후
this.key = Keys.hmacShaKeyFor(secretFromEnv.getBytes());
Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'jwtAuthenticationFilter' defined in file [...]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'jwtUtil': Injection of autowired dependencies failed
JwtAuthenticationFilter 클래스가 생성자 주입 방식으로 JwtUtil을 의존하고 있었는데, JwtUtil 빈이 제대로 생성되지 않았다.
생성자 주입 방식에서 필드 주입 방식으로 변경하여 의존성 주입 문제를 해결하였다.
// 변경 전
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
// ...
}
// 변경 후
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
// ...
}
org.springframework.util.PlaceholderResolutionException
application.yml에는 spring.jwt.secret으로 설정되어 있었는데, JwtUtil 클래스에서는 @Value("${jwt.secret}")으로 접근하려 해서 불일치가 발생했다.
JwtUtil 클래스의 어노테이션을 application.yml 구조에 맞게 수정하였다.
// 변경 전
@Value("${jwt.secret}")
private String secretFromEnv;
// 변경 후
@Value("${spring.jwt.secret}")
private String secretFromEnv;
@Component
public class JwtUtil {
private static final long JWT_TOKEN_VALIDITY = 24 * 60 * 60 * 1000; // 24시간
@Value("${spring.jwt.secret}")
private String secretFromEnv;
private Key key;
@PostConstruct
public void init() {
this.key = Keys.hmacShaKeyFor(secretFromEnv.getBytes());
}
public String extractEmail(String token) {
return extractClaim(token, Claims::getSubject);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
public String generateToken(String email, String role) {
Map<String, Object> claims = new HashMap<>();
claims.put("role", role);
return createToken(claims, email);
}
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() + JWT_TOKEN_VALIDITY))
.signWith(key)
.compact();
}
// 기타 토큰 검증 메서드들...
}
@Service
@RequiredArgsConstructor
public class AuthService {
private final UserRepository userRepository;
private final PartnerRepository partnerRepository;
private final PasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;
@Transactional(readOnly = true)
public AuthDto.LoginResponse login(AuthDto.LoginRequest request) {
// 일반 사용자 먼저 확인
User user = userRepository.findByEmail(request.getEmail()).orElse(null);
if (user != null) {
// 일반 사용자 비밀번호 검증
if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
throw new CustomException(ErrorCode.INVALID_PASSWORD);
}
// 토큰 생성
String token = jwtUtil.generateToken(user.getEmail(), user.getRole().name());
return AuthDto.LoginResponse.builder()
.token(token)
.role(user.getRole().name())
.id(user.getId())
.email(user.getEmail())
.name(user.getName())
.build();
}
// 파트너 확인
Partner partner = partnerRepository.findByEmail(request.getEmail())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));
// 비밀번호 검증 및 토큰 발급 로직...
}
}
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
private static final String HEADER_STRING = "Authorization";
private static final String TOKEN_PREFIX = "Bearer ";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String header = request.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
filterChain.doFilter(request, response);
return;
}
String token = header.substring(TOKEN_PREFIX.length());
try {
String email = jwtUtil.extractEmail(token);
if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
String role = jwtUtil.extractClaim(token, claims -> claims.get("role", String.class));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
email, null, Collections.singletonList(new SimpleGrantedAuthority(role))
);
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
} catch (Exception e) {
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
}
