spring-security를 활용하여 로그인 기능을 구현해보자.
package com.hkhong.study.config;
import com.hkhong.study.util.JwtUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String header = request.getHeader("Authorization");
String token = null;
String username = null;
// 1. Authorization 헤더 확인
if (header != null && header.startsWith("Bearer ")) {
token = header.substring(7);
username = jwtUtil.extractUsername(token);
}
// 2. 사용자 인증 처리
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(token,username)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
// 1. Authorization 헤더 확인
if (header != null && header.startsWith("Bearer ")) {
token = header.substring(7);
username = jwtUtil.extractUsername(token);
}
// 2. 사용자 인증 처리
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(token,username)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
package com.hkhong.study.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
public class UserDto {
private String id;
private String password;
}
package com.hkhong.study.service;
import com.hkhong.study.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AuthService implements UserDetailsService {
private static String userName = "hkhong";
private static String password = "hkhong2024!";
private final PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username.equals(userName)) {
return User
.withUsername(userName)
.password(passwordEncoder.encode(password))
.roles("USER")
.build();
} else {
throw new UsernameNotFoundException("User not found");
}
}
}
private static String userName = "hkhong";
private static String password = "hkhong2024!";
private final PasswordEncoder passwordEncoder;
1. loadUserByUsername
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username.equals(userName)) {
return User
.withUsername(userName)
.password(passwordEncoder.encode(password))
.roles("USER")
.build();
} else {
throw new UsernameNotFoundException("User not found");
}
}
package com.hkhong.study.controller;
import com.hkhong.study.dto.UserDto;
import com.hkhong.study.service.AuthService;
import com.hkhong.study.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
@RestController
@RequestMapping("/")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil;
private final AuthService authService;
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
@PostMapping("login")
public ResponseEntity<?> login(@RequestBody UserDto userDto) {
try {
// 1. 로그인 요청 (username과 password)
String username = userDto.getId();
String password = userDto.getPassword();
// 2. UsernamePasswordAuthenticationToken 생성
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDto.getId(), userDto.getPassword());
// 3. AuthenticationManager 호출
Authentication authentication = authenticationManager.authenticate(authenticationToken);
// 4. UserDetailsService 호출 하여 UserDetailsService가 사용자의 UserDetails 정보 호출
UserDetails userDetails = userDetailsService.loadUserByUsername(userDto.getId());
// 5. UserDetails 확인 하여 UserDetails의 정보와 입력한 정보가 일치하는지 확인
if (userDetails != null && passwordEncoder.matches(userDto.getPassword(), userDetails.getPassword())){
// 인증 성공
// 6. SecurityContext 객체 저장
// SecurityContextHolder에 인증 후 객체를 저장해 SecurityContext에 사용자 정보 보관
SecurityContextHolder.getContext().setAuthentication(authentication);
// JWT 토큰 생성 및 반환
String jwtToken = jwtUtil.generateToken(userDto.getId());
return ResponseEntity.ok(jwtToken);
}else {
throw new BadCredentialsException("Invalid User");
}
} catch (AuthenticationException e) {
throw new RuntimeException("Invalid Authentication Information");
}
}
}
스프링시큐리티와 JWT를 이용한 로그인을 구현해 보았다. good