[Spring] Spring Security로 로그인이 진행되는 과정

이신영·2023년 4월 4일
0
post-thumbnail

막상 인증과 인가를 짜보았는데 Spring Security로 진행하니까 정확한 과정을 모르는느낌을 받았다. 과정을 상세하게 알아볼까?

깃허브


우선 내가 사용한 방식으로 순서를 대략적으로 설명하자면

  1. 뷰를 통해 사용자가 로그인 정보를 입력(POST)
  2. 사용자가 입력한 파라미터를 컨트롤러에서 처리되어 spring security 설정에 따라 로그인 url로 전송됨
  3. spring security에서 전송받은 로그인정보를 인증하고 맞으면 인증토큰을 생성함
  4. 생성된 인증토큰으로 인증처리가 완료됨
  5. 인증처리가 완료되면 spring security에서 인증된 사용자정보를 세션에 저장
  6. 로그인이 성공한 뒤에 spring security에 설정된 url에 따라 이동함.

이 과정을 코드와 함께 설명해볼까?


1. 뷰를 통해 사용자가 로그인 정보를 입력

로그인폼에 입력하고 URL로 POST하는 과정


2. 사용자가 POST한 파라미터를 컨트롤러에서 처리되어 spring security 설정에 따라 로그인 url로 전송

UserController.java

    @GetMapping("/login")
    public String login() {
        return "loginForm";
    }

엥? 컨트롤러에는 @GetMapping밖에없는데? 라고할수있다. 실제 POST처리하는건

SecurityConfig.java


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);
    }

}

UserDetailsServiceloadUserByUsername을 재정의하면된다.

UserDetails : 사용자의 정보를 담는 인터페이스. ID, PW, 권한정보 등이 담겨있음
UserDetailsService : UserDetails 객체를 제공하는 인터페이스


3. spring security에서 전송받은 로그인정보를 검증하고 토큰을 생성하여 인증처리

SecurityConfig.java

public class SecurityConfig {
	...
    @Bean
    AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

근데 왜 @Bean씀?

Bean으로 등록함으로써 authenticationManager가 반환하는 객체를 다른 객체에서 사용하기위함

authenicationManager 빈을 통해 로그인을 검증하여 인증이 성공적으로 이루어진다면 Authentication를 반환하여 토큰을 생성하고 인증이 처리됨


4. 인증된 사용자정보를 세션에 저장

인증된 사용자 정보를 세션에 저장하는 부분은 spring security에서 자동적으로SecurityContextHolder라는 클래스의 ThreadLocal 변수에 저장하여 처리한다. 이거 불러오려면 SecurityContextHolder.getContext().getAuthentication() 으로 불러오면됨 현 사용자 닉네임같은거 불러올때 유용할듯?

ThreadLocal : java에서 각 스레드 내부에서만 사용되는 변수를 선언하기위한 클래스 이걸로 여러 스레드를 사용할 때 발생하는 동기화문제 해결함


5. 로그인이 성공한 뒤에 spring security에 설정된 url에 따라 이동


public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().requestMatchers(		
        ...
                .and()
                    .formLogin()
                    .loginPage("/user/login")
                    .defaultSuccessUrl("/")
		...

로그인이 성공하면 defaultSuccessUrl("/") 으로 이동하고 끝!


후기

스프링이 해주는거에 비하면 그저 커스텀으로 숟가락만 끼얹는수준? 😅 고맙다! 스프링!

profile
후회하지 않는 사람이 되자 🔥

0개의 댓글