springframework/시작하자SpringSecurity

30.WebAuthenticationDetails, AuthenticationDetailsSource

Jungsoomin :) 2020. 9. 29. 00:42

FormLogin 인증 과정에서 유저가 넘겨주는 부가적인 파라미터 정보를 저장하는 객체WebAuthenticationDetails,

 

WebAuthenticationDetails 를 생성하는 객체가 AuthenticationDetailsSource 이다.

 

 

  1. 사용자의 인증요청 
  2. AuthenticationFilter 작동
  3. 사용자의 username, password 외 추가적 정보를 보내는 경우 추가적 정보를 저장하고 참조하여 사용할 수 있게 하는 클래스가 WebAuthenticationDetails
  4. AuntheticationDetailsSourceWebAuthenticationDetails 클래스를 생성한다.

동작과정

 

  1. AuthenticationFilterAuthentication객체 생성
  2. Authentication 객체는 내부적으로 Object 타입의 Details 속성을 가짐
  3. WebAuthenticationDetails 는 사용자가 전달한 request객체를 받아 파라미터 값을 꺼내 저장
  4. WebAuthenticationDetails AuthenticationDetailsSource가 생성함

인증시 추가적 정보를 저장하기위해 AuthenticationDetailsSource 와WebAuthenticationDetails 를 구현하여 사용해보자.

 

  1. WebAuthenticationDetails상속한 클래스를 만든다.
  2. 필요한 값을 필드 선언
  3. 생성자에서 request를 받아 파라미터값으로 받아와 저장한다.

 

import org.springframework.security.web.authentication.WebAuthenticationDetails;

import javax.servlet.http.HttpServletRequest;

public class FormWebAuthenticationDetails extends WebAuthenticationDetails {

    private String secretKey;

    public FormWebAuthenticationDetails(HttpServletRequest request) {
        super(request);
        this.secretKey = request.getParameter("secret_key");
    }

    public String getSecretKey() {
        return secretKey;
    }
}

  1. AuthenticationDetailsSource 구현한 클래스를 정의
  2. 제네릭 타입으로 <HttpServletRequest, WebAuthenticationDetails> 를 정의
  3. 재정의할 메서드인 buildDetails() 에서 CustomWebAuthenticationDetails 를 리턴시킨다.

 

AuthenticationDetailsSource스프링에서 관리되야 하므로 Spring Bean 이여야한다.

import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class FormAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new FormWebAuthenticationDetails(context);
    }
}

등록

 

  1. SpringSecurityConfig 클래스의 configure(HttpSecurity) 메서드로 진입
  2. formLogin() 의 하위 APIauthenticationDetailsSource() 를 제공하고 있기에 해당 메서드의 인자값으로 주입받은 CustomAuthenticationDetailsSource를 준다.
@Autowired
    private AuthenticationDetailsSource authenticationDetailsSource;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/","/users").permitAll()
                .antMatchers("/mypage").hasRole("USER")
                .antMatchers("/messages").hasRole("MANAGER")
                .antMatchers("/config").hasRole("ADMIN")
                .anyRequest().authenticated()

            .and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/")
                .loginProcessingUrl("/login_proc")
                .authenticationDetailsSource(authenticationDetailsSource)
                .permitAll();
    }

사용

  1. AuthenticationProvider의 인증처리 과정에서 authentication 객체의 getDetails() 메서드로 추출
  2. 상속한 WebAuthenticationDetails 에 맞게 캐스팅
  3. 값을 비교하며 검증을 하는 도중 알맞지않다면 InsufficientAuthenticationExceptionthrow
@Autowired
    private UserDetailsService userDetailsService;//유저 정보를 끌어오는 클래스

    @Autowired
    PasswordEncoder passwordEncoder; // 패스워드 검증위한 인코더

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {//검증의 로직
        //authentication 객체는 입력한 username, password 를 담고 있다.
        String username = authentication.getName(); // username 추출
        String password = (String)authentication.getCredentials(); // password 는 credentical 에 저장되어 있음.

        AccountContext accountContext = (AccountContext)userDetailsService.loadUserByUsername(username);// 원하는 UserDetails 로 캐스팅

        if(!passwordEncoder.matches(password, accountContext.getPassword())){// 사용자 암호와 저장된 암호화된 정보를 비교
            throw new BadCredentialsException("BadCredentialsException");
        }

        FormWebAuthenticationDetails formWebAuthenticationDetails =(FormWebAuthenticationDetails)authentication.getDetails();
        String secretKey = formWebAuthenticationDetails.getSecretKey();//<<<<<<<커스텀 WebAuthenticationDetails

        if(secretKey == null && "secret".equals(secretKey)){
            throw new InsufficientAuthenticationException("InsufficientAuthenticationException");
        }

        //principal : 아이디 credentials : 암호 athorities : 권한정보 . 즉 최종 인증 토큰 생성 과정
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(accountContext.getAccount(), null,accountContext.getAuthorities());

        return usernamePasswordAuthenticationToken;//AuthenticationManager 에게 리턴
    }