인프런 커뮤니티 질문&답변

kwj3591님의 프로필 이미지

작성한 질문수

스프링 시큐리티

4) DB 연동 인증 처리(1) : CustomUserDetailsService

Security 듣는 중 드리는 질문

작성

·

380

·

수정됨

0

매번 여쭤보기보다 누적했다가 한번에 질문드리는게 더 좋을 것 같은데 강의를 일일이 찾기 어려워 한번에 올리겠습니다.

1. MySecurityConfig.class에 붙은 @Configuration은 어떤 일을 하나요?
>> @Configuration은 내부에 있는 @Bean 메소드를 스프링 컨테이너에서 싱글톤으로 관리될 수 있게끔 보장해주는 Annotation인 것으로 알고 있습니다. 하지만 Spring Configuration 클래스 내부에는 별다른 Bean 등록 객체가 없는 것 같은데, 추상적으로 [Spring에게 이 Security Configuration을 사용할거야]라고 알려주는 것보다 조금 더 무슨 역할을 하는지 궁금해서 질문 드립니다.

 

 

2.UserDetailService, CustomAuthenticationProvider 등을 왜 직접 Custom화 하는건가요?
>> 그 때마다 앱 내에서 사용하는 Authorities들과 [사용자]로 쓰는 객체가 매번 달라서 그렇다고 이해하면 될까요? 가령, 앱에서는 [Member, Account] 등을 사용, Spring 에서는 UserDetails란 인터페이스를 지원

 

 

3. CustomAuthenticationProvider 만 따로 Bean 주입 메소드를 작성해준 이유가 있을까요?

@Bean
public AuthenticationProvider authenticationProvider() {
    return new CustomAuthenticationProvider();
}

>> AuthenticationProvider 역할의 구현체를 이 앱에선 CustomAuthProvider 구현체를 사용할 것이다라는 메소드를 통해 Bean 등록을 해주는 모습입니다. 하지만 CustomUserDetailService 는 @Service Annotation을 통해 바로 컨테이너에 넣었습니다. 이는 @Service 내에 포함되어 있는 @Component로 인해 Bean 등록이 되는 것으로 알고 있습니다. 이 때, CustomAuthenticationProvider는 @Component로 하지 않고 저렇게 메소드로 해주는 이유가 있을까요?

 

 

4. CustomAuthenticationProvider.class 내 authenticate()에 대해서

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

    String username = authentication.getName();
    String credentials = (String) authentication.getCredentials(); // 비번

    AccountContext userDetails = (AccountContext) userDetailsService.loadUserByUsername(username); // UDS에서 알맞은 유저 가져온다 (우리가 만들어준 객체를 사용할 것이라 등록함)

    if(!passwordEncoder.matches(credentials, userDetails.getAccount().getPassword())){
        String msg = "비밀번호 불일치, 인증 실패하였습니다";
        log.error("Error while handling CustomAuthenticationProvider authenticate(): {}", msg);
        throw new BadCredentialsException("Error while handling CustomAuthenticationProvider authenticate():: " + msg);
    }

    // 일치하면 인증 완료, Authentication 온전한 결과를 넣어줘야 한다
    // Form 에서 사용하는 Authentication 객체는 하위 (근데 새로 만들어줌?)
    UsernamePasswordAuthenticationToken authenticatedToken = new UsernamePasswordAuthenticationToken(userDetails.getAccount(), null, userDetails.getAuthorities());

    return authenticatedToken;
}

다음은 강의에서 사용하신 소스코드 입니다! 이전에 해주신 이론 설명을 바탕으로 해당 코드를 작성하면서 생긴 의문은 Authentication authentication 객체입니다. " AuthenticationProvider에서 받은 [인증 전] Authentication 객체와 내보낼 [인증 후] Authentication 객체가 같은 객체이다"라고 해주셨던 것 같은데, 해당 코드 아래에서 Authentication 인증 객체를 새로 생성하는 것 같은데, 왜 그런지 알 수 있을까요?

 

5. Bean 주입 관련

3번 질문과 연관된 질문입니다! 직접적으로 Security 관련이 아닌 Bean 주입 관련한 내용이라... 양해부탁드립니다 ㅠ.ㅠ

다름이 아니라 직접 Bean 주입을 3번과 같이 하는게 아니라 @Component로 한다면, 자동으로 해당 구현객체를 쓴다는 점이 궁금합니다! 가령, 위에 AuthenticationProvider 의 구현체를 CustomAuthentication 으로 사용한다고 직접적으로 Bean 주입을 해줬기 때문에 해당 구현체를 사용하는 것이 이해가 가지만, CustomUserDetailsService 클래스 같은 경우 @Service annotation 으로 빈 등록을 하게 해줬습니다.

이럴 때는 기존에 사용하고 있던 UserDetailsService 구현체는 어떻게 사용하지 않는다는걸 스프링이 알게 될까요? @Service 를 사용하는 것은 자동주입이고, 기존에 Security 가 사용하는 구현체는 수동 주입했을 것 같은데, 이럴 경우 수동 주입이 우선권을 가지게 되지 않나 싶어서 문의드립니다!

 

감사합니다.

답변 1

1

정수원님의 프로필 이미지
정수원
지식공유자

1. MySecurityConfig.class에 붙은 @Configuration은 어떤 일을 하나요?

네 맞습니다.
이 클래스는 설정 클래스로 시큐리티에 필요한 빈을 정의하는 역할을 하게 됩니다.
현재 챕터에서는 빈을 정의하는 것이 없을 수 있는데 일반적으로 여러 빈들을 정의하는 경우가 많습니다.

2.UserDetailService, CustomAuthenticationProvider 등을 왜 직접 Custom화 하는건가요?
반드시 커스텀 할 필요는 없습니다. 다만 커스텀이 필요한 상황이 발생할 경우 예제를 보여드린 것입니다. 그리고 실무에서도 시스템마다 인증하는 방법들이 다를 수 있기 때문에 커스텀 클래스를 만들어 사용하고 있습니다.

3.CustomAuthenticationProvider 만 따로 Bean 주입 메소드를 작성해준 이유가 있을까요?
이건 특별한 이유가 있는 것은 아닙니다. 스프링에서 빈을 정의하는 방법이 몇가지가 있는데 그 중 하나를 선택한 것 뿐입니다. 어떤 방식을 사용하든 상관없이 원하시는 대로 정의하시면 됩니다.

4. CustomAuthenticationProvider.class 내 authenticate()에 대해서?
일반적으로 인증을 완료하게 되면 인증받은 사용자의 정보를 담은 User 객체, 그리고 권한 정보 등을 Authentication 객체에 저장하게 됩니다. 이 때 새로운 Authentication 객체를 생성해서 저장하고 있는데 실제 스프링 시큐리티에서도 새로운 Authentication 객체에 저장하고 있습니다.
Authentication 에 사용자 정보를 저장할 때는 생성자로 값을 전달하고 있는데 인증 받은 이전과 인증 받은 이후의 생성자가 다릅니다.
그래서 인증 받은 이후에는 새로운 생성자로 Authentication 객체를 만들어서 사용한다 보시면 됩니다.
UsernamePasswordAuthenticationToken 의 생성자는 다음과 같습니다.

public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
   super(null);
   this.principal = principal;
   this.credentials = credentials;
   setAuthenticated(false);
}


public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
      Collection<? extends GrantedAuthority> authorities) {
   super(authorities);
   this.principal = principal;
   this.credentials = credentials;
   super.setAuthenticated(true); // must use super, as we override
}

위가 인증받기 전이고 아래가 인증받은 이후의 생성자입니다.
그래서 인증 받은 다음에는 두번째 생성자를 사용해서 새로운 Authentication 객체를 생성한다고 보시면 됩니다.

5. Bean 주입 관련
네 일단 시큐리티에서 기본적으로 제공하는 UserDetailsService 구현체는 빈으로 만들지 않습니다.
일반 객체로 생성을 해서 사용하고 있다고 보시면 됩니다.
만약 시큐리티에서 빈으로 먼저 생성이 되어 있는 경우 우리가 직접 커스텀하게 빈으로 또 생성한다면 동일한 빈이 두개이기 때문에 타입 중복 오류가 발생하게 될 것입니다.
만약 그렇다면 빈을 참조할 때 인터페이스 타입이 아닌 구체적인 빈 이름으로 참조해야 할 것입니다.
특별한 경우를 제외하고는 시큐리티에서 객체를 생성할 때 빈으로 생성하기 보다는 일반 객체로 생성하는 경우도 많으니 커스텀하게 빈으로 정의했는데 실행시 특별한 문제가 생기지 않으면 시큐리티에서 빈으로 생성한 동일한 타입이 없다고 보시면 됩니다.

kwj3591님의 프로필 이미지

작성한 질문수

질문하기