Spring Security의 이해

Daehyeon Yun·2023년 8월 2일
0

Spring boot

목록 보기
7/8
post-thumbnail

Spring Security의 기본제공 API와 Filter를 이해해보자.

Spring Security의 기능

  1. 💡모든 Request는 인증이 되어야 자원의 접근이 가능하다.
  2. 💡개발자가 따로 구현을 안해도 기본적으로 웹 보안 기능이 작동함.
  3. Spring Security의 기본 계정, 로그인 페이지를 제공(변경 가능).
  4. 💡인증방식은 Form 로그인, httpBasic 로그인 방식 제공.
  5. 💡Server가 기동되면 Spring Security의 초기화 작업, 보안 설정이 진행됨.

사용자 정의(개발자 정의) Security 기능 구현하기

SecurityConfig : 사용자 정의 보안 설정 클래스
WebSecurityConfigurerAdapter : Spring Security의 웹 보안 기능 초기화 및 설정 클래스
HttpSecurity : 세부적인 보안 기능 설정 API
인증 API : http.formLogin(), http.logout() 등등..
인가 API : http.hasRole(), http.antMatchers() 등등..

💡WebSecurityConfigurerAdapter은 더 이상 지원하지 않아 SecurityFilterChain을 Bean으로 등록하여 사용해야한다.

사용자 정의 기능 구현하기

@EnableWebSecurity // 개발자가 Spring Security의 기능을 정의하게 해주는 어노테이션
@RequiredArgsConstructor
@ConditionalOnDefaultWebSecurity
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class SecurityConfiguration{

	/*
    	공식 문서와 상이한 이유는 SpringBoot에서 이미 default로 SecurityFilterChain을
        등록하는데, @Bean객체로 다시 DI를 시도한다면 둘 중 하나만 선택하라는 오류가 발생한다.
        따라서 아래와 같이 2개의 annotation을 클래스에 추가하고 Bean으로 사용하고자 하는 함수에 SecurityFilterChain 함수 위에 @Order을 추가하면 된다.
    */
    @Bean
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    public void SecurityFilterChain filterChain(HttpSecurity http) throws 	Exception {
        http
                .authorizeRequests()
                .antMatchers("/resources/**","/login-page","/register-page","/login-fail").permitAll()
                .antMatchers("loginAction").hasRole("USER")
                .antMatchers("loginAction").hasRole("ADMIN")
                .and()
                .formLogin()
                .loginPage("/login-page")
                .loginProcessingUrl("/login-action")
                .successHandler(new LoginSuccessHandler())
                .failureUrl("/login-fail")
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/login-page")
                .and()
                .csrf().disable();
        return http.build();
    }
}
! Security 설정

HttpSecurity에 사용되는 API들은 docs.spring.io에서 확인할 수 있다. (공식 가이드북)

왜 이렇게 어려워..

Login 인증 처리 프로세스 (Client <-> Server)

과정

  1. 사용자가 request를 GET 방식으로 /home에 접근시도
  2. 인증이 안되었다면 로그인 페이지로 리다이렉트
  3. 사용자가 POST 방식으로 username, password을 넘겨 인증 시도
  4. Server는 Spring Security가 해당 사용자의 Session을 생성하고, 해당 세션에 Authorization 객체를 생성하여 SecurityContext에 저장.
  5. 인증을 받은 사용자가 /home에 접근을 시도할 시 Spring Security는 사용자가 가진 Session으로부터 Token의 존재 여부를 판단 후, 자원에 접근시킨다.
  6. 이후 Spring Security는 Session에 저장된 Token이 있다면 사용자가 인증된 사용자라고 판단하여 인증을 유지한다.
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin()
                .loginPage("/login") // 해당 url을 로그인 페이지로 지정
                .defaultSuccessUrl("/index") // 로그인 성공 시 해당 url로 리다이렉트
                .failureUrl("/login") // 로그인 실패 시 해당 url로 리다이렉트 (기본적으로 ?error=true를 파라미터로 보냄)
                .usernameParameter("username") // 사용자 ID 파라미터 지정
                .passwordParameter("password") // 사용자 Password 파라미터 지정
                .loginProcessingUrl("/login") // 사용자 ID와 password를 제출할 url
                .successHandler() // 로그인 성공 후 핸들러 지정 (정의 가능)
                .failureHandler() // 로그인 실패 후 핸들러 지정 (정의 가능)
    }

UsernamePasswordAuthenticationFilter란?

사용자의 인증(로그인)을과 처리를 담당하는 필터

해당 필터는 Spring에서 만들어놓은 구현체이다. 해당 필터를 재사용해서 jwt와 같은 사례에 적용을 할 수 있다.
1. 해당 필터는 자신이 처리할 URL에 요청이 들어오면, 기본적으로 다음 필터로 동작을 넘기지 않고 잡아놓는다.
2. 해당 필터는 다음 필터로 동작을 넘겨도, Context가 생성되지 않은 상태에서 넘기기에 인증 후 정보를 활용할 수 없다.
3. 해당 필터를 대체할 필터를 만들게 된다면 성공과 실패에 따른 로직을 필수적으로 구현해야한다.
4. 만약, 로그인이 완료되었을 때 jwt를 반환하고 싶다면, successHandler를 구현하자.

UsernamePasswordAuthenticationFilter는, Spring Security에서 기본적인(Basic)로그인 폼을 지원하기 위해 미리 구현된 필터이다. Spring Security 설정에서
http.formLogin()을 사용하게 된다면 UsernamePasswordAuthenticationFilter가 등록된다.

   protected void configure(HttpSecurity http) throws Exception {
       http
               .formLogin() // UsernamePasswordAuthenticationFilter 등록
   }

LogoutFilter란?

로그아웃 처리를 담당하는 필터

해당 필터는 사용자의 로그아웃 처리를 담당하는 필터이다.
1. 사용자가 POST로 로그아웃을 요청한다. (💡Spring Security의 로그아웃은 무조건 POST로 처리해야한다)
2. AntPathRequestMatcher가 해당 로그아웃 요청을 확인한다.
3. 인증 정보가 일치하면 인증 객체를 담고있는 SecurityContext를 Authentication을 사용하여 꺼낸다.
4. SecurityContextLogoutHandler가 세션 무효화, 쿠키 삭제, 인증 객체 NULL, SecurityContext에서 정보를 삭제한다.
5. 로그아웃이 성공하면 LogoutSuccessHandler를 호출한다.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .logout() // 로그아웃 처리를 시작한다.
                .logoutUrl("/logout") // 로그아웃을 처리하는 URL 지정
                .logoutSuccessUrl("/login") //로그아웃 성공 시 URL 지정
                .deleteCookies("JSESSIONID","Remember-Me") // 로그아웃 시 해당 쿠키 삭제
                .addLogoutHandler(logoutHandler()) // 로그아웃 핸들러 구현
                .logoutSuccessHandler(logoutSuccessHandler()) // 로그아웃 성공 후 핸들러 구현
    }

Remember Me 사용

Remember Me
: 세션이 만료되거나 클라이언트가 종료되도 Application이 해당 사용자를 기억하는 기능을 말한다.
Remember-Me 쿠키에 대한 http 요청을 확인한 후 토큰 기반 인증을 사용하여 토큰의 유효성을 검사한 뒤, 검증되면 사용자는 로그인이 된다.
💡 중요 : JSEESIONID가 없더라도 Spring Security에 Remember-Me 쿠키가 존재한다면, 다시 인증을 시도하여 JSSSIONID를 재생성한다.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
           .rememberMe() // rememberMe 지정
                .rememberMeParameter("remember") // RememberMe의 기본 파라미터는 "Remember-Me"이다.
                .rememberMeCookieName("remember") // RememberMe의 쿠키 이름을 설정한다.
                .tokenValiditySeconds(3600) // 토큰 유효 시간을 설정하며 기본 값은 14일이다.
                .alwaysRemember(true) // Remember
                .userDetailsService(userDetailsService) // UserDetailsService의 객체를 불러온다.

    }

너무 어렵다.


참고

새로운 도전을 위한 한걸음
김민수님
junhabaeks

profile
열심히 살아야지

0개의 댓글