
막상 인증과 인가를 짜보았는데 Spring Security로 진행하니까 정확한 과정을 모르는느낌을 받았다. 과정을 상세하게 알아볼까?
우선 내가 사용한 방식으로 순서를 대략적으로 설명하자면
이 과정을 코드와 함께 설명해볼까?

로그인폼에 입력하고 URL로 POST하는 과정
@GetMapping("/login")
public String login() {
return "loginForm";
}
엥? 컨트롤러에는 @GetMapping밖에없는데? 라고할수있다. 실제 POST처리하는건
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers(
...
.and()
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/")
...
여기 formLogin()의 loginPage()부분에서 이루어졌다. url을 /user/login 으로 설정하여 해당 POST 요청을 가로챈다. 그다음 loadUserByUsername 메소드를 호출하게되는데 이것을 사용자에 맞게 정의하기위해서
public class UserSecurityService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
Optional<Member> _member = this.userRepository.findByusername(username);
if(_member.isEmpty()){
throw new UsernameNotFoundException("사용자를 찾을 수 없습니다.");
}
Member member = _member.get();
List<GrantedAuthority> authorities = new ArrayList<>();
if("admin".equals(username)){
authorities.add(new SimpleGrantedAuthority(UserRole.ADMIN.getValue()));
} else {
authorities.add(new SimpleGrantedAuthority(UserRole.USER.getValue()));
}
return new User(member.getUsername(), member.getPassword(), authorities);
}
}
UserDetailsService의 loadUserByUsername을 재정의하면된다.
UserDetails: 사용자의 정보를 담는 인터페이스. ID, PW, 권한정보 등이 담겨있음
UserDetailsService: UserDetails 객체를 제공하는 인터페이스
public class SecurityConfig {
...
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
근데 왜
@Bean씀?Bean으로 등록함으로써 authenticationManager가 반환하는 객체를 다른 객체에서 사용하기위함
authenicationManager 빈을 통해 로그인을 검증하여 인증이 성공적으로 이루어진다면 Authentication를 반환하여 토큰을 생성하고 인증이 처리됨
인증된 사용자 정보를 세션에 저장하는 부분은 spring security에서 자동적으로SecurityContextHolder라는 클래스의 ThreadLocal 변수에 저장하여 처리한다. 이거 불러오려면 SecurityContextHolder.getContext().getAuthentication() 으로 불러오면됨 현 사용자 닉네임같은거 불러올때 유용할듯?
ThreadLocal: java에서 각 스레드 내부에서만 사용되는 변수를 선언하기위한 클래스 이걸로 여러 스레드를 사용할 때 발생하는 동기화문제 해결함
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers(
...
.and()
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/")
...
로그인이 성공하면 defaultSuccessUrl("/") 으로 이동하고 끝!
스프링이 해주는거에 비하면 그저 커스텀으로 숟가락만 끼얹는수준? 😅 고맙다! 스프링!