이 콘텐츠는 오래되었으며 레거시 OAuth 스택을 사용하고 있습니다. Spring Security의 최신 OAuth 지원을 살펴보십시오 .

1. 개요

 
이 빠른 사용방법(예제)에서는 Spring Security OAuth2 구현으로 OpenID Connect를 설정하는 데 중점을 둘 것입니다.

OpenID Connect

는 OAuth 2.0 프로토콜 위에 구축된 간단한 ID 계층입니다.그리고 보다 구체적

으로 Google

OpenID Connect 구현을 사용하여 사용자를 인증하는 방법을 배웁니다 .

2. 메이븐 설정

 
먼저 Spring Boot 애플리케이션에 다음 의존성을 추가해야 합니다.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>

3. 아이디 토큰

 
구현 세부 사항을 살펴보기 전에 OpenID가 작동하는 방식과 OpenID와 상호 작용하는 방식을 간단히 살펴보겠습니다.이 시점에서 OpenID는 OAuth를 기반으로 하기 때문에 OAuth2를 이미 이해하고 있는 것이 중요합니다.먼저 ID 기능을 사용하기 위해

openid

라는 새로운 OAuth2 범위를 사용할 것입니다 .

그러면 액세스 토큰에 " id_token " 이라는 추가 필드가 생깁니다 .

id_token는

(우리의 경우 구글에서) ID 공급자의 서명이 사용자에 대한 식별 정보를 포함하는 JWT (JSON 웹 토큰)입니다.마지막으로

server(Authorization Code)

묵시적

흐름은 모두

id_token

을 얻는 데 가장 일반적으로 사용되는 방법입니다 . 이 예에서는

서버 흐름

을 사용 합니다 .

3. OAuth2 클라이언트 설정

 
다음으로 OAuth2 클라이언트를 다음과 같이 구성해 보겠습니다.
@Configuration
@EnableOAuth2Client
public class GoogleOpenIdConnectConfig {
    @Value("${google.clientId}")
    private String clientId;

    @Value("${google.clientSecret}")
    private String clientSecret;

    @Value("${google.accessTokenUri}")
    private String accessTokenUri;

    @Value("${google.userAuthorizationUri}")
    private String userAuthorizationUri;

    @Value("${google.redirectUri}")
    private String redirectUri;

    @Bean
    public OAuth2ProtectedResourceDetails googleOpenId() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setClientId(clientId);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        details.setUserAuthorizationUri(userAuthorizationUri);
        details.setScope(Arrays.asList("openid", "email"));
        details.setPreEstablishedRedirectUri(redirectUri);
        details.setUseCurrentUri(false);
        return details;
    }

    @Bean
    public OAuth2RestTemplate googleOpenIdTemplate(OAuth2ClientContext clientContext) {
        return new OAuth2RestTemplate(googleOpenId(), clientContext);
    }
}
다음은

application.properties입니다

.
google.clientId=<your app clientId>
google.clientSecret=<your app clientSecret>
google.accessTokenUri=https://www.googleapis.com/oauth2/v3/token
google.userAuthorizationUri=https://accounts.google.com/o/oauth2/auth
google.redirectUri=http://localhost:8081/google-login
참고:
  • 먼저 Google 개발자 콘솔 에서 Google 웹 앱에 대한 OAuth 2.0 자격 증명을 가져와야 합니다 .
  • id_token 을 얻기 위해 openid 범위 를 사용 했습니다 .
  • 또한 id_token ID 정보 에 사용자 이메일을 포함 하기 위해 추가 범위 이메일사용했습니다 .
  • 리디렉션 URI http://localhost:8081/google-login 은 Google 웹 앱에서 사용되는 것과 동일합니다.

4. 사용자 정의 OpenID 연결 필터

 
이제 다음과 같이 id_token 에서 인증을 추출하기 위해 사용자 지정

OpenIdConnectFilter

를 만들어야 합니다.
public class OpenIdConnectFilter extends AbstractAuthenticationProcessingFilter {

    public OpenIdConnectFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
        setAuthenticationManager(new NoopAuthenticationManager());
    }
    @Override
    public Authentication attemptAuthentication(
      HttpServletRequest request, HttpServletResponse response) 
      throws AuthenticationException, IOException, ServletException {
        OAuth2AccessToken accessToken;
        try {
            accessToken = restTemplate.getAccessToken();
        } catch (OAuth2Exception e) {
            throw new BadCredentialsException("Could not obtain access token", e);
        }
        try {
            String idToken = accessToken.getAdditionalInformation().get("id_token").toString();
            String kid = JwtHelper.headers(idToken).get("kid");
            Jwt tokenDecoded = JwtHelper.decodeAndVerify(idToken, verifier(kid));
            Map<String, String> authInfo = new ObjectMapper()
              .readValue(tokenDecoded.getClaims(), Map.class);
            verifyClaims(authInfo);
            OpenIdConnectUserDetails user = new OpenIdConnectUserDetails(authInfo, accessToken);
            return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        } catch (InvalidTokenException e) {
            throw new BadCredentialsException("Could not obtain user details from token", e);
        }
    }
}
다음은 간단한

OpenIdConnectUserDetails입니다

.
public class OpenIdConnectUserDetails implements UserDetails {
    private String userId;
    private String username;
    private OAuth2AccessToken token;

    public OpenIdConnectUserDetails(Map<String, String> userInfo, OAuth2AccessToken token) {
        this.userId = userInfo.get("sub");
        this.username = userInfo.get("email");
        this.token = token;
    }
}
참고:
  • Spring Security JwtHelper는 디코딩 id_token .
  • id_token은 항상 사용자의 고유 식별자인 " sub" 필드를 포함 합니다.
  • id_token요청에 이메일 범위를 추가함에 따라 " email " 필드 도 포함합니다 .

4.1. ID 토큰 확인

 
위의 예에서는 사용

decodeAndVerify ()

방법

JwtHelper를

으로부터 발췌 정보에

id_token,

또한이를 확인하기 위해.이를 위한 첫 번째 단계는

Google Discovery

문서에 지정된 인증서 중 하나로 서명되었는지 확인하는 것 입니다.이러한 변경 사항은 하루에 한 번 정도이므로

jwks-rsa

라는 유틸리티 라이브러리를 사용 하여 읽을 것입니다.
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>jwks-rsa</artifactId>
    <version>0.3.0</version>
</dependency>
인증서가 포함된 URL을

application.properties

파일에 추가해 보겠습니다 .
google.jwkUrl=https://www.googleapis.com/oauth2/v2/certs
이제 이 속성을 읽고

RSAVerifier

객체를 빌드할 수 있습니다 .
@Value("${google.jwkUrl}")
private String jwkUrl;    

private RsaVerifier verifier(String kid) throws Exception {
    JwkProvider provider = new UrlJwkProvider(new URL(jwkUrl));
    Jwk jwk = provider.get(kid);
    return new RsaVerifier((RSAPublicKey) jwk.getPublicKey());
}
마지막으로 디코딩된 ID 토큰의 클레임도 확인합니다.
public void verifyClaims(Map claims) {
    int exp = (int) claims.get("exp");
    Date expireDate = new Date(exp * 1000L);
    Date now = new Date();
    if (expireDate.before(now) || !claims.get("iss").equals(issuer) || 
      !claims.get("aud").equals(clientId)) {
        throw new RuntimeException("Invalid claims");
    }
}

verifyClaims ()

메소드는 ID 토큰이 구글에서 발급 한 것을 확인하고이 만료 아니에요.
이에 대한 자세한 내용은

Google 설명서

에서 찾을 수 있습니다 .

5. Security 구성

 
다음으로 Security 구성에 대해 논의해 보겠습니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private OAuth2RestTemplate restTemplate;

    @Bean
    public OpenIdConnectFilter openIdConnectFilter() {
        OpenIdConnectFilter filter = new OpenIdConnectFilter("/google-login");
        filter.setRestTemplate(restTemplate);
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .addFilterAfter(new OAuth2ClientContextFilter(), 
          AbstractPreAuthenticatedProcessingFilter.class)
        .addFilterAfter(OpenIdConnectFilter(), 
          OAuth2ClientContextFilter.class)
        .httpBasic()
        .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/google-login"))
        .and()
        .authorizeRequests()
        .anyRequest().authenticated();
    }
}
참고:
  • OAuth2ClientContextFilter 다음에 맞춤형 OpenIdConnectFilter를 추가 했습니다.
  • 간단한 Security 구성을 사용하여 사용자를 " /google-login "으로 리디렉션 하여 Google의 인증을 받았습니다.

6. 사용자 컨트롤러

 
다음은 앱을 테스트하기 위한 간단한 컨트롤러입니다.
@Controller
public class HomeController {
    @RequestMapping("/")
    @ResponseBody
    public String home() {
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        return "Welcome, " + username;
    }
}
샘플 응답(앱 권한을 승인하기 위해 Google로 리디렉션한 후):
Welcome, example@gmail.com

7. 샘플 OpenID 연결 프로세스

 
마지막으로 샘플 OpenID Connect 인증 프로세스를 살펴보겠습니다.먼저

인증 요청

을 보낼 것입니다 .
https://accounts.google.com/o/oauth2/auth?
    client_id=sampleClientID
    response_type=code&
    scope=openid%20email&
    redirect_uri=http://localhost:8081/google-login&
    state=abc
응답(

사용자 승인 후

)은 다음으로 리디렉션됩니다.
http://localhost:8081/google-login?state=abc&code=xyz
다음 으로 Access Token과 id_token에 대한

코드

를 교환할 것입니다 .
POST https://www.googleapis.com/oauth2/v3/token 
    code=xyz&
    client_id= sampleClientID&
    client_secret= sampleClientSecret&
    redirect_uri=http://localhost:8081/google-login&
    grant_type=authorization_code
다음은 샘플 응답입니다.
{
    "access_token": "SampleAccessToken",
    "id_token": "SampleIdToken",
    "token_type": "bearer",
    "expires_in": 3600,
    "refresh_token": "SampleRefreshToken"
}
마지막으로 실제

id_token

의 정보는 다음과 같습니다.
{
    "iss":"accounts.google.com",
    "at_hash":"AccessTokenHash",
    "sub":"12345678",
    "email_verified":true,
    "email":"example@gmail.com",
     ...
}
따라서 토큰 내부의 사용자 정보가 자체 애플리케이션에 ID 정보를 제공하는 데 얼마나 유용한지 즉시 알 수 있습니다.

8. 결론

 
이 빠른 소개 사용방법(예제)에서는 Google의 OpenID Connect 구현을 사용하여 사용자를 인증하는 방법을 배웠습니다.그리고 언제나처럼

GitHub

에서 소스 코드 찾을 수 있습니다 .
Generic footer banner