ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Security Session
    스프링/스프링 시큐리티 2024. 1. 8. 20:09

    SecurityConfig.class

    @Configuration
    @EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록이 된다.
    public class SecurityConfig {
    
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http.csrf(CsrfConfigurer::disable);
            http.authorizeHttpRequests(authorize ->
                    authorize
                            .requestMatchers("/user/**").authenticated()
                            .requestMatchers("/manager/**").hasAnyRole("ADMIN", "MANAGER")
                            .requestMatchers("/admin/**").hasAnyRole("ADMIN")
    
                            .anyRequest().permitAll()
            );
            http.formLogin(formLogin -> formLogin
                    .loginPage("/loginForm")
                    .loginProcessingUrl("/login")
                    // login 주소가 호출되면 시큐리티가 낚아채서 대신 로그인 진행
                    .defaultSuccessUrl("/"));
                    // 로그인 시 기존의 페이지로 접근
                    // 만약에 로그인이 안된 상황에서 user에 접근하다 loginForm으로 튕겨지면
                    // 다시 로그인 시 root 페이지가 아니라 user path로 접근
    
            return http.build();
        }
    }

    SecurityConfig에서 loginForm 페이지에서 로그인을 통해 login 페이지를 호출하면 스프링 시큐리티가 낚아채서 대신 로그인을 진행하는 부분을 추가했다.

     

     

    시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시켜주는데, 진행이 완료되면 시큐리티 session을 만들어준다. 이 때의 오브젝트 타입은 Authentication 타입 객체이다.

     

    Authentication 안에 User의 정보가 있어야하는데, 이 때의 User 오브젝트 타입은 UserDetails 타입의 객체이다.

     

     

    쉽게 말하면 시큐리티 세션이 있는데 여기 들어갈 수 있는 객체가 Authentication 객체이다.

    이 객체 안에 user를 저장하는 것은 UserDetails 객체인 것이다.

     

    나중에 꺼내서 사용할 때에 Session에서 Authentication 객체를 꺼내고, 그 안에서 UserDetails 객체를 꺼내면 user의 object에 접근할 수 있는 것이다.

     

     

    PrincipalDetails.class

    public class PrincipalDetails implements UserDetails {
        // UserDetails를 implements 했으니
        // Security Session => Authentication => UserDetails(PrincipalDetails)
    
        private User user;
    
        public PrincipalDetails(User user) {
            this.user = user;
        }
    
        // 해당 User의 권한을 리턴
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            // 해당 반환타입으로 변환을 위해 collect라는 ArrayList를 만들고 안에 user.getRole()을 넣기
            Collection<GrantedAuthority> collect = new ArrayList<>();
            collect.add(new GrantedAuthority() {
                @Override
                public String getAuthority() {
                    return user.getRole();
                }
            });
            return collect;
        }
    
        @Override
        public String getPassword() {
            return user.getPassword();
        }
    
        @Override
        public String getUsername() {
            return user.getUsername();
        }
    
        // 계정이 만료되었으면 false
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
    
        // 계정이 잠겨있으면 false
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
    
        // 자격 증명이 만료되었으면 false
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
    
        // 계정이 활성화되지 않았으면 false
        @Override
        public boolean isEnabled() {
            return true;
        }
    }

    위의 코드에서는 UserDetails를 implement 했으니 PrincipalDetails에서 user의 object에 접근할 수 있다.

     

     

     

     

    PrincipalDetialsService.class

    @Service
    public class PrincipalDetailsService implements UserDetailsService {
    
        @Autowired
        private UserRepository userRepository;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User userEntity = userRepository.findByUsername(username);
            if(userEntity != null) {
                return new PrincipalDetails(userEntity);
            }
            return null;
        }
    }

    시큐리티 설정에서 loginProcessingUrl("/login"); 이라고 된 부분이 있다.

    위의 코드는 login 요청이 오면 자동으로 UserDetailsService 타입으로 IoC 되어있는 loadUserByName 함수가 실행되는 것이다.

     

    만약에 userEntity가 없으면 null을 반환하고 userEntity가 있다면 PrincipalDetials에 userEntity를 넣어서 반환해준다.

     

    이 값들이 스프링 시큐리티의 session에 들어가는 것이다.

     

    loginForm에서 로그인 버튼을 누르면 <form action="/login"method="POST">를 통해 /login이 요청이 되는데, 스프링 시큐리티는 UserDetailsSerivce가 implement 되어있는 PrincipalDetailsService를 찾아서 loadUserByUsername을 호출한다.

     

     

    이러한 방식으로 로그인을 진행하면 세션에 user의 object가 담기는 것이다.

Designed by Tistory.