Spring | Security Authentication Provider

DoItDev·2021년 11월 8일
3
post-thumbnail

Authentication Provider

스크린샷 2021-11-07 오후 1 28 45

AuthenticationProvider 는 한 줄로 정의 하면 db에서 가져온 정보와 input 된 정보가 비교되서 체크되는 로직이 포함되어있는 인터페이스 라고 할 수 있다.

AuthenticationProvider를 상속하면 authenticate 메소드를 오버라이드 할 수 있다.

authenticate 메소드의 경우 Authentication Manager에서 넘겨준 인증객체(Authentication)를 가지고 메소드를 만든다.

Authentication 객체의 경우 사용자의 정보가 담겨져있다.

여기서 사용자는 form 또는 로그인 로직에서 가지고온 정보라고 할 수 있다.

Authentication는 로그인 아이디, 로그인 비밀번호를 객체에서 가지고 올 수 있다.

db에서 사용자 정보를 가지고 오려면 UserDetailsService 에서 리턴해주는 값을 가지고 사용이 가능하다.

UserDetailsService return 값의 경우 UserDetails를 리턴해준다.

리턴된 user 정보를 가지고 PasswordEncoder bean을 사용하여서 input 된 비밀번호와 매칭 시켜주면 된다.

여기서 PasswordEncoder 의 경우 우리가 configuartion에서 bean으로 등록을 시켜준 클래스가 의존성 주입되어 있다.

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

위의 코드의 경우 security 에서 비밀번호 인코딩에 대한 빈을 주입을 해주었다.

만약 해주지 않거나 커스텀 된 비밀번호 인코딩을 사용하고 싶다면

AuthenticationProvider 여기서 커스텀 해주면 된다.

@RequiredArgsConstructor
@Component("authenticationProvider")
public class DomainAuthenticationProvider implements AuthenticationProvider {

    private final PasswordEncoder passwordEncoder;

    private final DomainUserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String loginId = authentication.getName();

        String password = (String) authentication.getCredentials();

        DomainUserDetails domainUserDetails = (DomainUserDetails) userDetailsService.loadUserByUsername(loginId);

        if (isNotMatches(password, domainUserDetails.getPassword())) {
            throw new BadCredentialsException(loginId);
        }

        return new UsernamePasswordAuthenticationToken(domainUserDetails, domainUserDetails.getPassword(), domainUserDetails.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

    private boolean isNotMatches(String password, String encodePassword) {
        return !passwordEncoder.matches(password, encodePassword);
    }

}

supports 메소드의 경우 아래의 같이 공식 문서에 정의 되어있다.

AuthenticationProvider표시된 Authentication객체를 지원 하는지 여부를 반환 합니다.

return 된는 값이 true면 된다고 한다.

그렇다면 아래의 메소드는 무엇을 의미지..? 라고 느낄 수 있다.

대부분 여기를 그냥 ture 로 넘겨주거나 한다. 그래야 작동이 된다.

하지만 이 메소드의 경우 타입을 체크해주기 때문에 내가 사용하는 클래스 혹은 UsernamePasswordAuthenticationToken 인지 아닌지 유무를 체크를 해준다.

   @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

authenticate 메소드의 경우 위에서 짧게 설명을 하였지만 깊게 설명을 하자면

일단 인자의 경우 인증객체를 사용을 한다. 인증객체의 경우 인증 매니저 클래스에서 넘겨져온 인증 객체이다.

그렇다면 이 메소드의 경우 어떤걸 하나..?

이 메소드의 경우 DB에 인코딩된 비밀번호와 input 된 비밀번호를 비교를 해준다.

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String loginId = authentication.getName();

        String password = (String) authentication.getCredentials();

        DomainUserDetails domainUserDetails = (DomainUserDetails) userDetailsService.loadUserByUsername(loginId);

        if (isNotMatches(password, domainUserDetails.getPassword())) {
            throw new BadCredentialsException(loginId);
        }

        return new UsernamePasswordAuthenticationToken(domainUserDetails, domainUserDetails.getPassword(), domainUserDetails.getAuthorities());
    }

위의 코드를 보면 인증 객체의 경우 input id & password 를 가지고 있다.

이것을 input id 로 userDetailsSerivce 에서 db 정보를 가지고 온다.

그리고 load 된 유저 정보를 가지고 우리가 설정된 비밀번호 빈을 가지고 매칭을 시켜준다.

만약 틀린 비밀번호시 BadCredentialsException를 날려준다. 하지만 비밀번호가 정확하다면UsernamePasswordAuthenticationToken로 리턴시켜준다.

그리고 support 메소드를 통해서 클래스 타입에 대한 체크를 해준 후 다음으로 넘어가 인증을 진행 혹은 끝을 낸다.

Security Configuration

위의 코드의 경우 현재 @Component에 서 bean 이름을 정의 해주었기 때문에 자동으로 빈주입이 된다.

하지만 또 다른 방식으로 설정 class 에서 주입을 줄 수 도 있다.

@Configuration
@EnableWebSecurity
@ComponentScan("com.baeldung.security")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Autowired
    private DomainAuthenticationProvider authProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated()
            .and().httpBasic();
    }
}

configure 오버라이드 해서 커스텀한 클래스를 주입을 시켜주면 사용이 가능하다.


참조

profile
Back-End Engineer

2개의 댓글

comment-user-thumbnail
2022년 7월 8일

고맙습니다! 많은 도움이 되었습니다!

답글 달기
comment-user-thumbnail
2022년 11월 2일

혹시 해당 아이디나 패스워드 말고 게스트유무같은 다른 파라미터도 넘겨서 처리할 수있나요? httpservletrequest 를 사용해서 파라미터를 넘기려고 하는데 선언이 안되더라구요

답글 달기