By a3040, Published on Invalid Date
사용자가 보낸 인증을 위한 정보(id/pwd)가 Authentication 에 담기게 됩니다.
AuthenticationManager 가 db에있는 정보와 보내온 인증정보를 비교해서 인증여부를 판단합니다.
authenticationManager.authenticate()
메서드에서는 DaoAuthenticationProvider
객체를 사용하여 DB에서 사용자 정보를 가져와 인증을 처리하고, additionalAuthenticationChecks()
메서드에서 로그인 폼 정보와 DB 정보를 비교합니다.
@Service
public class JwtUserDetailsService implements UserDetailsService {
...
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Member member = memberRepository.findByEmail(email);
if (member == null) {
throw new UsernameNotFoundException("User not found with email: " + email);
}
return new User(member.getEmail(), member.getPassword(), Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
}
@Configuration
public class SecurityConfig {
...
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(new PlaintextPasswordEncoder());//일반 문자열비교
// provider.setPasswordEncoder(new BCryptPasswordEncoder());
provider.setUserDetailsService(jwtUserDetailsService);
return provider;
}
@Bean
public SecurityFilterChain jwtSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.headers()
.frameOptions().disable();
http.cors()
.configurationSource(corsConfigurationSource());
http.authenticationProvider(daoAuthenticationProvider());
....
}
class PlaintextPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
}
By a3040, Published on Invalid Date
로그인 페이지 설정
@Configuration
public class SecurityConfig {
...
@Bean
public SecurityFilterChain jwtSecurityFilterChain(HttpSecurity http) throws Exception {
...
http.authorizeHttpRequests((requests) -> requests
...
.requestMatchers("/css/**", "/js/**", "/error").permitAll() // 정적자원 허용
.requestMatchers("/member/loginProc").permitAll() // html막테스트
.formLogin((form) -> form
.loginPage("/member/login").permitAll()
...
)
.logout((logout) -> logout.permitAll()
.deleteCookies("jwt")
.invalidateHttpSession(false)
.logoutSuccessUrl("/member/login"));
return http.build();
}
로그인 처리 및 토큰 발급
@RestController
public class AuthenticationController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private UserRepository userRepository;
@PostMapping("/member/loginProc")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
} catch (BadCredentialsException e) {
throw new Exception("Incorrect username or password", e);
}
final User user = userRepository.findByUsername(loginRequest.getUsername());
final UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(user.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new LoginResponse(token));
}
// ...
}
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
이 과정에서 일어나는 일
UsernamePasswordAuthenticationToken
객체를 생성합니다. 이 객체에는 사용자가 입력한 로그인 폼 정보가 포함됩니다.AuthenticationManager
구현체 중 하나인 ProviderManager
객체를 생성합니다. 이 객체는 여러 개의 AuthenticationProvider
를 가질 수 있습니다.ProviderManager
의 authenticate()
메서드를 호출합니다. 이 메서드는 등록된 AuthenticationProvider
에서 인증을 처리합니다.DaoAuthenticationProvider
객체를 생성하여 ProviderManager
에 추가합니다. 이 객체는 DB에서 사용자 정보를 가져와 인증을 처리합니다.DaoAuthenticationProvider
의 additionalAuthenticationChecks()
메서드를 호출하여 사용자 정보를 인증합니다. 이 메서드에서는 DB에서 가져온 사용자 정보와 로그인 폼 정보를 비교합니다.By a3040, Published on Invalid Date
SecurityFilterChain에 jwt토큰 관련 설정을 추가해서 권한에 따라 접근을 허가합니다.
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String authToken = extractAuthTokenFromRequest(request);
if (authToken != null) {
String username = jwtTokenUtil.getUsernameFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = jwtUserDetailsService.loadUserByJwtToken(authToken);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
private String extractAuthTokenFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}