인증 프로세스(2/13)

dev.hyjang·2024년 5월 18일

스프링 시큐리티

목록 보기
3/6

폼 인증 - formLogin()

폼 인증

  • HTTP 기반의 폼 로그인 인증 메커니즘을 활성화하는 API
  • 사용자 인증을 위한 사용자 정의 로그인 페이지를 쉽게 구현할 수 있음
  • 따로 설정하지 않으면 스프링 시큐리티가 제공하는 기본 로그인 페이지 사용함
  • 사용자는 웹 폼을 통해 자격 증명(user, password)를 제공하고, Spring Security는 HttpServletRequest에서 이 값을 읽어 옴

formLogin() API

  • FormLoginConfigurer 설정 클래스에서 API 설정
  • 내부적으로 UsernamePasswordAuthenticationFilter 가 생성되어 폼 방식의 인증 처리를 담당함
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
//                .formLogin(Customizer.withDefaults());
                .formLogin(form -> form
//                        .loginPage("/loginPage")
                        .loginProcessingUrl("/loginProc")
                        .defaultSuccessUrl("/", true)
                        .failureUrl("/failed")
                        .usernameParameter("userId")
                        .passwordParameter("password")
                        .successHandler(new AuthenticationSuccessHandler() {
                            @Override
                            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                                System.out.println("autentication : " + authentication);
                                response.sendRedirect("/home");
                            }
                        })
                        .failureHandler((request, response, exception) -> {
                            System.out.println("exception : " + exception.getMessage());
                            response.sendRedirect("/login");
                        })
                        .permitAll()
                );
        return http.build();
    }    
  • defaultSuccessUrl("/", [alwaysuse])

    • 로그인 성공 후 이동 페이지
    • alwaysUse 기본은 false
    • false : 인증 전에 보안이 필요한 페이지를 방문하다가 인증에 성공한 경우면 이전 위치로 리다이렉트
    • true : 지정된 위치로 이동
  • successHandler / failureHandler

    • 이 두 핸들러가 있으면 우선권을 가짐


폼 인증 필터 - UsernamePasswordAuthenticationFilter

[인증 기본 필터] AbstractAuthenticationProcessingFilter

  • 스프링 시큐리티는 해당 클래스를 사용자의 자격 증명을 인증하는 기본 필터로 사용

[인증 수행 필터] UsernamePasswordAuthenticationFilter

  • AbstractAuthenticationProcessingFilter 를 확장한 클래스
  • HttpServletRequest 에서 제출된 사용자 이름과 비밀번호
    로부터 인증을 수행
  • 인증 수행 필터는 커스텀해서 다른 걸 사용할 수도 있음

DefaultLoginPageGeneratingFilter/DefaultLogoutPageGeneratingFilter

  • 로그인 페이지, 로그아웃 페이지 생성 필터
  • 인증 프로세스가 초기화될 때 함께 초기화



기본 인증 – httpBasic()

  • HTTP 는 액세스 제어와 인증을 위한 프레임워크를 제공하며 가장 일반적인 인증 방식은 "Basic" 인증 방식임
  • RFC 7235 표준이며 인증 프로토콜은 HTTP 인증 헤더에 기술되어 있음
  1. 클라이언트는 인증정보 없이 서버로 접속을 시도
  2. 서버가 클라이언트에게 인증요구를 보낼 때 401 Unauthorized 응답과 함께 WWW-Authenticate 헤더를 기술해서 realm(보안영역) 과 Basic 인증방법을 보냄
  3. 클라이언트가 서버로 접속할 때 Base64 로 username 과 password 를 인코딩하고 Authorization 헤더에 담아서 요청함
  4. 성공적으로 완료되면 정상적인 상태 코드를 반환

주의 사항

  • base-64 인코딩된 값은 디코딩이 가능하기 때문에 인증정보가 노출됨!!!
  • HTTP Basic 인증은 반드시 HTTPS 와 같이 TLS 기술과 함께 사용해야 함

httpBasic() API

  • HttpBasicConfigurer 설정 클래스를 통해 여러 API 들을 설정할 수 있음
  • 내부적으로 BasicAuthenticationFilter 가 생성되어 기본 인증 방식의 인증 처리를 담당


기본 인증 필터 - BasicAuthenticationFilter

BasicAuthenticationFilter

  • 이 필터는 기본 인증 서비스를 제공하는 데 사용
  • BasicAuthenticationConverter 를 사용해서 요청 헤더에 기술된 인증정보의 유효성을 체크하며 Base64 인코딩된 username 과 password 를 추출
  • 인증 이후 세션을 사용하는 경우와 사용하지 않는 경우에 따라 처리되는 흐름에 차이 있음
    세션을 사용하는 경우 매 요청 마다 인증과정을 거치지 않으나 세션을 사용하지 않는 경우 매 요청마다 인증과정을 거쳐야 함



기억하기 인증 – rememberMe()

RememberMe 인증

  • 사용자가 웹 사이트나 애플리케이션에 로그인할 때 자동으로 인증 정보를 기억하는 기능
  • UsernamePasswordAuthenticationFilter 와 함께 사용되며, AbstractAuthenticationProcessingFilter 슈퍼클래스에서 훅을 통해 구현
    • 인증 성공 시 RememberMeServices.loginSuccess() 를 통해 RememberMe 토큰을 생성하고 쿠키로 전달
    • 인증 실패 시 RememberMeServices.loginFail() 를 통해 쿠키를 지움
    • LogoutFilter 와 연계해서 로그아웃 시 쿠키를 지움

토큰 생성

기본적으로 암호화된 토큰으로 생성 되어지며 브라우저에 쿠키를 보내고, 향후 세션에서 이 쿠키를 감지하여 자동 로그인이 이루어지는 방식으로 달성

  • base64(username + ":" + expirationTime + ":" + algorithmName + ":" algorithmHex(username + ":" + expirationTime + ":" password + ":" + key))
    • username: UserDetailsService 로 식별 가능한 사용자 이름
    • password: 검색된 UserDetails 에 일치하는 비밀번호
    • expirationTime: remember-me 토큰이 만료되는 날짜와 시간, 밀리초로 표현
    • key: remember-me 토큰의 수정을 방지하기 위한 개인 키
    • algorithmName: remember-me 토큰 서명을 생성하고 검증하는 데 사용되는 알고리즘(기본적으로 SHA-256 알고리즘을 사용)

RememberMeService 구현제

  • TokenBasedRememberMeServices - 쿠키 기반 토큰의 보안을 위해 해싱을 사용
  • PersistentTokenBasedRememberMeServices -생성된 토큰을 저장하기 위해 데이터베이스나 다른 영구 저장 매체를 사용
  • 두 구현 모두 사용자의 정보를 검색하기 위한 UserDetailsService가 필요

rememberMe() API

  • RememberMeConfigurer 설정 클래스를 통해 여러 API 들을 설정할 수 있음
  • 내부적으로 RememberMeAuthenticationFilter 가 생성되어 자동 인증 처리를 담당


기억하기 인증 필터 - RememberMeAuthenticationFilter

RememberMeAuthenticationFilter

  • SecurityContextHolder에 Authentication이 포함되지 않은 경우 실행되는 필터
  • 세션이 만료되었거나 어플리케이션 종료로 인해 인증 상태가 소멸된 경우 토큰 기반 인증을 사용해 유효성을 검사하고 토큰이 검증되면 자동 로그인 처리를 수행


익명 인증 사용자 – anonymous()

  • 스프링 시큐리티에서 "익명으로 인증된" 사용자와 인증되지 않은 사용자 간에 실제 개념적 차이는 없으며 단지 액세스 제어 속성을 구성하는 더 편리한 방법을 제공한다고 볼 수 있음
  • SecurityContextHolder 가 항상 Authentication 객체를 포함하고 null 을 포함하지 않는다는 것을 규칙을 세우게 되면 클래스를 더 견고하게 작성할 수 있음
  • 인증 사용자와 익명 인증 사용자를 구분해서 어떤 기능을 수행하고자 할 때 유용할 수 있으며 익명 인증 객체를 세션에 저장하지 않음
  • 익명 인증 사용자의 권한을 별도로 운용할 수 있다. 즉 인증 된 사용자가 접근할 수 없도록 구성이 가능!!


로그 아웃 – logout()

  • 스프링 시큐리티는 기본적으로 DefaultLogoutPageGeneratingFilter 를 통해 로그아웃 페이지를 제공하며 “ GET / logout ” URL 로 접근이 가능
  • 로그아웃 실행은 기본적으로 “ POST / logout “ 으로만 가능하나 CSRF 기능을 비활성화 할 경우 혹은 RequestMatcher 를 사용할 경우 GET, PUT, DELETE 모두 가능
  • 로그아웃 필터를 거치지 않고 스프링 MVC 에서 커스텀 하게 구현할 수 있으며 로그인 페이지가 커스텀하게 생성될 경우 로그아웃 기능도 커스텀하게 구현해야 함

logout() API

LogoutFilter



요청 캐시 RequestCache / SavedRequest

RequestCache

  • 인증 절차 문제로 리다이렉트 된 후에 이전에 했던 요청 정보를 담고 있는 'SavedRequest’ 객체를 쿠키 혹은 세션에 저장하고 필요시 다시 가져와 실행하는 캐시 메커니즘

SavedRequest

  • SavedRequest 은 로그인과 같은 인증 절차 후 사용자를 인증 이전의 원래 페이지로 안내하며 이전 요청과 관련된 여러 정보를 저장

requestCache() API

RequestCacheAwareFilter

  • 이전에 저장했던 웹 요청을 다시 불러오는 역할
  • SavedRequest가 현재 Request와 일치하면 이 요청을 필터 체인의 doFilter 메소드에 전달하고, SavedRequest가 없으면 필터는 원래 Request 을 그대로 진행
profile
낭만감자

0개의 댓글