Spring Security OAuth2를 이용한 로그인 구현 - 사용자 정보 가져오기

목차

2. Resource Server로부터 사용자 정보 가져오기

Spring Security 설정하기

OAuth2 인증을 마친 후에 Resource Server로부터 사용자 정보를 가져오기 위해서는 UserInfo EndPoint에 접근할 필요가 있다. HttpSecurity객체의 oauth2Login().userInfoEndpoint().userService() 메소드를 이용해서 UserInfo EndPoint로부터 사용자 정보를 가져올 Service를 등록한다.

@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final CustomOAuth2UserService customOAuth2UserService;

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**").authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated();

http
.oauth2Login()
.userInfoEndpoint()
.userService(customOAuth2UserService);
}
}

스프링에서 기본적으로 제공하는 OAuth2Provider

Google, Facebook, Github 등을 통해 OAuth2인증하기 위한 기본적인 설정값들이 들어가 있다. 해당 정보를 통해 ClientRegistration 객체를 생성한다.

  • authorizationUri
    • 인증을 진행하기 위해 사용하는 URI
  • tokenUri
    • Resource server로부터 access token을 받기 위해 사용하는 URI
  • jwkSetUri
    • token의 유효성(서명)을 확인할 수 있는 public key(JSON Web Key)를 받을 수 있는 URI
  • userInfoUri
    • Resource Server(ex. Google)로부터 사용자 정보를 가져오기 위한 URI
  • userNameAttributeName
    • Resource server가 제공하는 id값
  • clientName
    • Resource Server 이름(client Name을 사용할 때도 있다.)

CommonOAuth2Provider.enum

public enum CommonOAuth2Provider {
GOOGLE {
@Override
public Builder getBuilder(String registrationId) {
ClientRegistration.Builder builder = getBuilder(registrationId, ClientAuthenticationMethod.BASIC,
DEFAULT_REDIRECT_URL);
builder.scope("openid", "profile", "email");
builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs");
builder.issuerUri("https://accounts.google.com");
builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo");
builder.userNameAttributeName(IdTokenClaimNames.SUB);
builder.clientName("Google");
return builder;
}
},

...

private static final String DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}";

protected final ClientRegistration.Builder getBuilder(String registrationId, ClientAuthenticationMethod method,
String redirectUri) {
ClientRegistration.Builder builder = ClientRegistration.withRegistrationId(registrationId);
builder.clientAuthenticationMethod(method);
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
builder.redirectUri(redirectUri);
return builder;
}
}

Service 생성하기

아래 서비스는 DefaultOAuth2UserService 에 구현된 코드들을 가져와 다시 정의 했다.

Spring Boot에서 OAuth2 인증 방식을 사용하기 위해서는 OAuth2UserService인터페이스를 구현해 loadUser 메소드를 정의해야 한다.

OAuth2UserService 인터페이스를 구현해 사용자가 원하는 서비스를 만든다. DefaultOAuth2UserService 는 스프링에서 제공하는 OAuth2UserService 를 구현한 기본 Class이다.

CustomOAuth2UserService.class

@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

private final OAuth2Repository oAuth2Repository;

private RestTemplate restTemplate = new RestTemplate();
private final Converter<OAuth2UserRequest, RequestEntity<?>> requestEntityConverter
= new OAuth2UserRequestEntityConverter();


@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
String userNameAttributeName = userRequest
.getClientRegistration()
.getProviderDetails()
.getUserInfoEndpoint()
.getUserNameAttributeName();

// userRequest객체를 request객체로 변환해 Resouce Server에 요청을 보내 User 정보를 가져온다.
RequestEntity<?> request = this.requestEntityConverter.convert(userRequest);
ResponseEntity<Map<String, Object>> response
= restTemplate.exchange(request, new ParameterizedTypeReference<Map<String, Object>>() {});

Map<String, Object> userAttributes = response.getBody();

// 사용자 권한 목록을 저장한다.
Set<GrantedAuthority> authorities = new LinkedHashSet<>();
authorities.add(new OAuth2UserAuthority(userAttributes));

// token값으로 접근 가능한 scope를 같이 저장한다.
OAuth2AccessToken token = userRequest.getAccessToken();
for (String authority : token.getScopes()) {
authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
}

OAuth2User oAuth2User = new DefaultOAuth2User(authorities, userAttributes, userNameAttributeName);
return oAuth2User;
}
}

OAuth2UserServiceclinetRegistration 객체와 accessToken 을 가지고 있는 OAuth2UserRequest 를 이용해 UserInfo EndPoint로부터 사용자 정보를 가져올 수 있다.

디버깅을 통해 Spring OAuth2 자세히 살펴보기

  • userNameAttributeName

    • 사용자를 인식할 수 있는 field
    • 구글에서는 sub필드를 이용한다.
  • OAuth2User

    • 스프링에서는 기본 구현체로 DefaultOAuth2User 객체를 제공한다.
    • authorities, userAttributes, userNameAttributeName 3가지 속성을 가진 OAuth2 User객체이다.

  • OAuth2AccessToken
    • OAuth2 Access Token을 표현하기 위한 객체
    • tokenType : OAuth 인증을 위한 Bearer Token 타입임을 나타낸다.
    • scope : 해당 토큰 값으로 접근할 수 있는 정보들을 나타낸다.
    • toeknValue : 토큰 값이 들어 있다.

  • OAuth2UserRequest
    • OAuth2UserRequest객체는 ClientRegistration 객체와, OAuth2AccessToken 객체 그리고 추가적인 데이터를 담기 위한 Map<String, Object> 객체가 존재한다.
    • ClientRegistration 객체에는 Resource Server에 등록 정보Resource에 접근하기 위한 URI들이 있다.
    • OAuth2AccessToken 객체에는 Resource Server로부터 데이터를 가져오기 위해 필요한 Token값이 담겨 있다.

  • request 객체를 보면 Header값에 Access Token값이 있다.
  • UserInfo URI에 접근해 Access Token을 이용해 인증을 받고 사용자 정보를 받아올 수 있다.

  • OAuth2UserRequest 객체내 additionalParamater 에 id_token으로 JWT(JSON Web Token) 이 들어있는 것을 확인할 수 있다.
  • JWT 에 대한 유효성 검사는 jwkSetUri 에서 Public Key 값을 가져와 유효성 검사를 진행한다.
  • https://jwt.io/ 사이트에 전달 받은 JWT 와 Public Key를 이용하면 유효성 검증과 JWT 내용을 볼 수 있다.

response 객체를 살펴 보면 UserInfo URI로 접근해 인증을 성공적으로 마쳤을 때 사용자 정보를 가져옴을 확인 할 수 있다. 가져오는 사용자 정보는 scope로 명시한 데이터만 가져온다.

Share