jwt관련 추가한 의존성
implementation 'io.jsonwebtoken:jjwt:0.12.5'
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5'
application.properties 수정
# JWT 설정
jwt.secret=your-secret-key-your-secret-key-your-secret-key
jwt.expiration=3600000 # 토큰 만료 시간 (1시간, 밀리초 단위)
jwt.header=Authorization # HTTP 요청 헤더 이름
jwt.prefix=Bearer # 토큰 앞에 붙는 접두사
SecurityConfig.java
@Configuration
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
@Autowired
public SecurityConfig(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
this.userDetailsService = userDetailsService;
this.passwordEncoder = passwordEncoder;
}
@Bean
public AuthenticationManager authenticationManager() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder);
return new ProviderManager(List.of(authProvider));
}
}
PasswordConfig.java
@Configuration
public class PasswordConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
LoginRequestDto.java
@Getter
public class LoginRequestDto {
private String email;
private String password;
}
CustomUserDetails.java
@Getter
public class CustomUserDetails implements UserDetails {
private final String username;
private final String password;
private final Collection<? extends GrantedAuthority> authorities;
public CustomUserDetails(User user) {
this.username = user.getUsername();
this.password = user.getPassword();
this.authorities = Collections.emptyList();
}
@Override
public boolean isAccountNonExpired() { return true; }
@Override
public boolean isAccountNonLocked() { return true; }
@Override
public boolean isCredentialsNonExpired() { return true; }
@Override
public boolean isEnabled() { return true; }
}
CustomUserDetailsService.java
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
return new CustomUserDetails(user);
}
}
JwtUtil.java
@Component
public class JwtUtil {
private final Key key;
private final long expiration;
public JwtUtil(@Value("${jwt.secret}") String secret, @Value("${jwt.expiration}") long expiration) {
this.key = Keys.hmacShaKeyFor(secret.getBytes());
this.expiration = expiration;
}
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(key)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser()
.verifyWith((SecretKey) key)
.build()
.parseSignedClaims(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith((SecretKey) key).build().parseSignedClaims(token);
return true;
} catch (JwtException e) {
return false;
}
}
}
JwtAuthenticationFilter.java
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
public JwtAuthenticationFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
if (jwtUtil.validateToken(token)) {
String username = jwtUtil.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()));
}
}
filterChain.doFilter(request, response);
}
}
UserServiceImpl.java
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil;
@Override
public Map<String, String> login(LoginRequestDto request) {
Optional<User> user = userRepository.findByEmail(request.getEmail());
if (!user.isPresent() || !passwordEncoder.matches(request.getPassword(), user.get().getPassword())) {
return Map.of("error", "Invalid email or password");
}
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.get().getUsername(), request.getPassword())
);
String token = jwtUtil.generateToken(user.get().getUsername());
return Map.of("token", token);
}
}
UserController.java
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/api/login")
public Map<String, String> login(@RequestBody LoginRequestDto request) {
return userService.login(request);
}
}