2023 11 23 : OAuth2 작동

XingXi·2023년 11월 23일
0

기록

목록 보기
4/33
post-thumbnail
사이드 프로젝트 중 회원 쪽을 맡아서 진행중 OAuth2 를 구현하고 있습니다. 잊어버리기 전에 SpringSecurity Filter 중 하나인 OAuth2UserService 에서 처리 방법을 정리 해보려 합니다. 자세한 내용은 추후에 올릴 예정입니다. 여기서는 간략히!

여기서는
Google 에서 Access Token 을 받을 때 까지를 기록하고 있습니다.
Front 와 Back 을 완전히 분리하기 위함입니다.

1. LoginPage

구글 OAuth2 를 예시로 들 예정입니다. 구글 서비스에 OAuth 등록을 완료하고
생성한 구글 서비스에서 필요한 값들을 서버에 완전히 등록한 이후 입니다.

<a href="/oauth2/authorization/google">구글</a>

2. SpringSecurityConfig

SecurityFilterChain 에서 다음과 같이 설정하여 소셜 로그인을 사용 시
사용자가 지정한 Service 가 작동되도록 합니다

SpringSecurityConfig.securityWebFilterChain

    @Bean
    public SecurityFilterChain securityWebFilterChain(HttpSecurity http) throws Exception {
        return http....
                                .oauth2Login()
                .userInfoEndpoint().userService(oAuthService).and().permitAll()
                .successHandler(oAuth2SuccessHandler)
                .failureHandler(oAuth2FailureHandler)
                .and()
                ....

3. OAuth2UserService

OAuth2UserService를 구현하며 SpringSecurityFilterChain 에 OAuth2 요청 시
작동하도록 사용자가 등록한 서비스 입니다.

OAuthService.loadUser

@Service
@RequiredArgsConstructor
@Log4j2
public class OAuthService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> 
{
    private final AuthRepository authRepository;
    private final AuthTokenService authTokenService;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException 
    {
        log.info("OAuthService Operation");
        OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService = new DefaultOAuth2UserService();
    
        OAuth2User oAuth2User = oAuth2UserService.loadUser(userRequest);
        log.info("oAuth2User : {}",oAuth2User.toString());

Log

2023-11-23 08:43:56.750  ...OAuthService             : OAuthService Operation
2023-11-23 08:43:56.863  ...OAuthService             : oAuth2User : Name: [**], 
Granted Authorities: [[OAUTH2_USER, SCOPE_https://www.googleapis.com/auth/userinfo.email,
SCOPE_https://www.googleapis.com/auth/userinfo.profile, SCOPE_openid]], 
User Attributes: [{sub=****, name==****, given_name==****, family_name==****, picture==****, email==****, 
email_verified=true, locale=ko}] Hibernate: 

OAuthService( SpringSecurity Filter Chain 에 추가한 class )
OAuth 로그인 이후의 결과 값을 얻을 수 있다는 것이 확인 되었습니다.

4. DefaultOAuth2UserService.class

public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

...


	@Override
	public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
		Assert.notNull(userRequest, "userRequest cannot be null");
		if (!StringUtils
				.hasText(userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri())) {
			OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_INFO_URI_ERROR_CODE,
					"Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: "
							+ userRequest.getClientRegistration().getRegistrationId(),
					null);
			throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
		}
		String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint()
				.getUserNameAttributeName();
		if (!StringUtils.hasText(userNameAttributeName)) {
			OAuth2Error oauth2Error = new OAuth2Error(MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE,
					"Missing required \"user name\" attribute name in UserInfoEndpoint for Client Registration: "
							+ userRequest.getClientRegistration().getRegistrationId(),
					null);
			throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
		}
		RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);
		ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request);
		Map<String, Object> userAttributes = response.getBody();
		Set<GrantedAuthority> authorities = new LinkedHashSet<>();
		authorities.add(new OAuth2UserAuthority(userAttributes));
		OAuth2AccessToken token = userRequest.getAccessToken();
		for (String authority : token.getScopes()) {
			authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
		}
		return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);
	}
		RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);
		ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request);
		Map<String, Object> userAttributes = response.getBody();
		Set<GrantedAuthority> authorities = new LinkedHashSet<>();
		authorities.add(new OAuth2UserAuthority(userAttributes));
		OAuth2AccessToken token = userRequest.getAccessToken();
		for (String authority : token.getScopes()) {
			authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
		}
		return new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);
	}

SpringSecurity 에서 제공하는 DefaultOAuth2UserService 의 loadUser 에서 이미 accessToken 을 사용하는 것을 확인할 수 있습니다.

자체적으로 행하는 다음 로직을 내부적으로 실행 중임으로 조금 더 분석해 보면 Front 와 Back 의 분리를 할 수 있을 것 같습니다

0개의 댓글