Spring-Security-4

akanana·2023년 2월 28일
0

Spring-Security

목록 보기
5/8

목표

  • 구글 OAuth 를 통해 로그인
  • Facebook OAuth 를 통해 로그인
  • Naver OAuth 를 통해 로그인
  • Kakao OAuth 를 통해 로그인

OAuth

OAuth에 관한 설명 ~~

Google API Key 생성

구글APIs
위 링크로 접근후
1

2

3

4

5

6

7

/login/oauth2/code는 고정

OAuth2 적용

dependency 추가

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-client</arti
	<version>3.0.2</version>
</dependency>

application.yml 설정

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: {클라이언트 ID}
            client-secret: {클라이언트 보안 비밀번호}
            scope:
            - email
            - profile

FilterChain 설정

.oauth2Login(form->form
    .loginPage("/login")
    .userInfoEndpoint()
    .userService(principalOauth2UserService)
);

이때 principalOauth2UserService에서 loadUser 메소드를 실행시킨다

Authentication

Spring SecuriyAuthentication라는 세션정보를 지니고 있다
이때 일반 User는 UserDetails를, OAuth User는 OAuth2User를 반환한다

User인 경우

UserDetailsService를 상속받은 클래스에서 loadUserByUsername(String usernaame) 이실행되며, 이 메소드에서 UserDetails를 리턴한다
PrincipalDetailsService

@Service
@Log4j2
public class PrincipalDetailsService implements UserDetailsService{
    @Autowired
    private UserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) 
    		throws UsernameNotFoundException {
        log.info("loadUserByUsername : "+usernmae);
        User user = userRepository.findByUsername(username);
        log.info(user);
        return user;
    }
}
OAuth인 경우

DefaultOAuth2UserService를 상속받은 클래스에서 loadUser(OAuth2UserRequest userRequest)이 실행되며, 이 메소드에서 OAuth2User가 리턴된다

@Service
@Log4j2
public class PrincipalOauth2UserService extends DefaultOAuth2UserService {
    @Autowired
	private PasswordEncoder passwordEncoder;
    @Autowired
    private UserRepository userRepository;
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) 
    		throws OAuth2AuthenticationException {
        log.info("loadUser : "+userRequest);
        OAuth2User oAuth2User = super.loadUser(userRequest);
        log.info(oAuth2User.getAttributes());
        return oAuth2User ;
    }        
}
문제점

이때, 접속한 User가 일반 User냐, OAuth User냐에 따라 별도의 작업을 수행하게 된다.
이를 위해 Authentication에 주입 가능한, UserDetailsOAuth2User를 둘 다 상속받은 별도의 객체를 생성하여 전달해주었다
이전에 사용한 PrincipalDetails를 사용해보자
PrincipalDetails

@Data
public class PrincipalDetails implements UserDetails, OAuth2User{

    private User user;
    private Map<String,Object> attributes;

    // 일반 로그인
    public PrincipalDetails(User user){
        this.user = user;
    }
    // OAuth 로그인
    public PrincipalDetails(User user, Map<String,Object> attributes){
        this.user = user;
        this.attributes = attributes;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        // User의 권한을 리턴
        Collection<GrantedAuthority> collect = new ArrayList<>();
        collect.add(new GrantedAuthority() {
            @Override
            public String getAuthority(){
                return user.getRole();
            }
        });
        return collect;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    @Override
    public String getName() {
        return null;
    }
}

두 인터페이스를 상속받은 해당 메소드는 이제 Authentication에 주입이 가능해진다
이를 위해 loadUserByUsernameloadUser 메소드에서 PrincipalDetails를 리턴하였다

return user;
=> return new PrincipalDetails(user);
return oAuth2User;
=> return new PrincipalDetails(user,oAuth2User.getAttributes());

0개의 댓글