스프링 시큐리티/실전프로젝트 - 인증 프로세스 Ajax 인증 구현

ch02. 인증 필터 - AjaxAuthenticationFilter

webmaster 2022. 1. 20. 12:23
728x90
  • AbstractAuthenticationProcessingFilter 상속
    • 대부분의 인증을 하는 클래스들은 이를 상속받아 구현한다
  • 필터 작동 조건
    • AntPathRequestMatcher("/api/login")로 요청정보와 매칭하고 요청 방식이 Ajax 이면 필터 작동
  • AjaxAuthenticationToken 생성하여 AuthenticationManager에게 전달하여 인증처리
  • Filter 추가
    • http.addFilterBefore(AjaxAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
    • 우리가 만든 필터를 UsernamePasswordAuthenticationFilter 전에 동작시키도록 한다.
  • Filter
    • public class AjaxLoginProcessingFilter extends AbstractAuthenticationProcessingFilter {
          
          private ObjectMapper objectMapper = new ObjectMapper();//JSON 객체를 추출하고 반환하기위해
          
          public AjaxLoginProcessingFilter() {
              super(new AntPathRequestMatcher("/api/login")); //사용자가 해당 URL로 요청을 할떄만 필터가 동작
          }
      
          @Override
          public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
              if(!isAjax(request)){   //AJAX인가?
                  throw new IllegalStateException("Authentication is not supported");
              }
      
              AccountDto accountDto = objectMapper.readValue(request.getReader(), AccountDto.class);
              if(StringUtils.isEmpty(accountDto.getUsername()) || StringUtils.isEmpty(accountDto.getPassword())){
                  throw new IllegalArgumentException("Username or Password is empty");
              }
              AjaxAuthenticationToken ajaxAuthenticationToken = new AjaxAuthenticationToken(accountDto.getUsername(), accountDto.getPassword());
              //현재는 Form인증 방식을 AuthenticationProvider 가 동작하게 되는데 Token 이 AjaxAuthenticationToken 과 다르기 떄문에 현재는 동작하지 않는다.
      
              return getAuthenticationManager().authenticate(ajaxAuthenticationToken);
          }
      
          private boolean isAjax(HttpServletRequest request) {
              //header에 값을 담아서 Ajax인지 판단.
              if("XMLHttpRequest".equals(request.getHeader("X-Requested-with"))){
                  return true;
              }
              return false;
          }
      }
    • AbstractAuthenticationProcessingFilter를 상속받아 구현한다.
    • 생성자를 사용하여, 어떤 URL로 접근하였을 때 필터를 동작시킬지 적는다
      • new AntPathRequestMatcher("/api/login")
    • Client에서 AJAX 일 경우 Header에 X-Requested-with​ 필드의 값을 XMLHttpRequest값을 주게 되는데 이 값이 맞는지를 확인한다.
    • Client에서 ID, Password값을 빈 값으로 주진 않았는지 확인한다.
    • 모든 과정이 성공하게 된다면 AuthenticationManager() 에게 인증 권한을 넘긴다
      • 현재는 Provider가 없기 때문에 성공적인 인증 처리를 할 수가 없다.
      • Provider가 AjaxAuthenticationToken을 찾을 수 없기 때문
    • AjaxAuthenticationToken
      • public class AjaxAuthenticationToken extends AbstractAuthenticationToken {
            private static final long serialVersionUID = 560L;
            private final Object principal;
            private Object credentials;
        
            public AjaxAuthenticationToken(Object principal, Object credentials) {
                //실제 인증 받기전 username, password를 받는 생성자
                super((Collection)null);
                this.principal = principal;
                this.credentials = credentials;
                this.setAuthenticated(false);
            }
        
            public AjaxAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
                //실제 인증 받은 후 username, password를 받는 생성
                super(authorities);
                this.principal = principal;
                this.credentials = credentials;
                super.setAuthenticated(true);
            }
        
            public Object getCredentials() {
                return this.credentials;
            }
        
            public Object getPrincipal() {
                return this.principal;
            }
        
            public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
                Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
                super.setAuthenticated(false);
            }
        
            public void eraseCredentials() {
                super.eraseCredentials();
                this.credentials = null;
            }
        }
    • UsernamePasswordAuthenticationFilter 앞에 내 CustomFilter를 위치시킨다.
      • 첫번째 파라미터에 등록시킬 Filter를 위치시킨다.
      • 내 필터 빈으로 등록
      • 내 필터를 빈으로 등록하는데 이때 manager를 등록해야 되는데 authenticationManagerBean을 통해 Manager를 가지고와 주입해 준다.
728x90