使用带有 OpenID Connect 提供程序的 spring-security-oauth2 客户端时如何访问“id_token”和“refresh_token”?

Posted

技术标签:

【中文标题】使用带有 OpenID Connect 提供程序的 spring-security-oauth2 客户端时如何访问“id_token”和“refresh_token”?【英文标题】:How to access the "id_token" and "refresh_token" when using spring-security-oauth2 client with OpenID Connect provider? 【发布时间】:2017-08-17 15:21:43 【问题描述】:

我已成功将 Spring Security OAuth2 与我的 Open ID Connect 提供程序 (Forgerock OpenAM) 集成。我可以看到正在检索访问令牌。如何访问id_tokenrefresh_token,它们是来自/token 端点的响应的一部分?

【问题讨论】:

【参考方案1】:

终于找到答案并发布,以防它对遇到同样问题的人有用。会话通过 Spring Security OAuth2 认证后,有一个 Authentication 对象设置。它需要转换为OAuth2Authentication 的实例。该对象具有令牌。

    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth instanceof OAuth2Authentication) 
        Object details = auth.getDetails();
        OAuth2AccessToken token = oauth2Ctx.getAccessToken();

        if (token != null && !token.isExpired()) 
            // Do Stuff
        

【讨论】:

【参考方案2】:

替代方法的完整示例(使用 Spring Boot 并禁用部分自动配置)。

application.properties:

security.oauth2.client.client-id=client-id
security.oauth2.client.client-secret=client-secret
security.oauth2.client.access-token-uri=http://my-oidc-provider/auth/oauth2/token
security.oauth2.client.user-authorization-uri=http://my-oidc-provider/auth/oauth2/authorize
security.oauth2.resource.token-info-uri=http://my-oidc-provider/auth/oauth2/check_token
security.oauth2.client.scope=openid,email,profile
security.oauth2.resource.jwk.key-set-uri=http://my-oidc-provider/auth/oidc/jwks
/**
 * Extending the AuthorizationServerEndpointsConfiguration disables the Spring
 * Boot ResourceServerTokenServicesConfiguration.
 */
@Configuration
@EnableOAuth2Sso
public class OAuth2Config extends AuthorizationServerEndpointsConfiguration 

    @Value("$security.oauth2.resource.jwk.key-set-uri")
    private String keySetUri;

    @Value("$security.oauth2.resource.token-info-uri")
    private String checkTokenEndpointUrl;

    @Value("$security.oauth2.client.client-id")
    private String clientId;

    @Value("$security.oauth2.client.client-secret")
    private String clientSecret;

    @Bean
    public RemoteTokenServices resourceServerTokenServices() 
        RemoteTokenServices tokenService = new RemoteTokenServices();

        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        accessTokenConverter.setUserTokenConverter(new CustomIdTokenConverter(keySetUri));
        tokenService.setAccessTokenConverter(accessTokenConverter);

        tokenService.setCheckTokenEndpointUrl(checkTokenEndpointUrl);
        tokenService.setClientId(clientId);
        tokenService.setClientSecret(clientSecret);

        return tokenService;
    

    @Bean
    public ClientDetailsService clientDetailsService() 
        return new InMemoryClientDetailsService();
    

    @Bean
    public UserInfoRestTemplateFactory userInfoRestTemplateFactory(
            ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers,
            ObjectProvider<OAuth2ProtectedResourceDetails> details,
            ObjectProvider<OAuth2ClientContext> oauth2ClientContext) 
        return new DefaultUserInfoRestTemplateFactory(customizers, details,
                oauth2ClientContext);
    

public class CustomIdTokenConverter extends DefaultUserAuthenticationConverter 

    private final JwkTokenStore jwkTokenStore;

    public CustomIdTokenConverter(String keySetUri) 
        this.jwkTokenStore = new JwkTokenStore(keySetUri);
    

    @Override
    public Authentication extractAuthentication(Map<String, ?> map) 

        String idToken = (String) map.get("id_token");

        OAuth2AccessToken token = jwkTokenStore.readAccessToken(idToken);

        Map<String, Object> claims = token.getAdditionalInformation();
        OAuth2RefreshToken refreshToken = token.getRefreshToken();

        String principal = (String) claims.get("sub");

        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_USER");

        return new CustomAuthenticationData(principal, claims, authorities);
    

public class CustomAuthenticationData extends UsernamePasswordAuthenticationToken 

    private final Map<String, Object> attributes;

    public CustomAuthenticationData(String username, Map<String, Object> attributes, Collection<? extends GrantedAuthority> authorities) 
        super(username, "N/A", authorities);
        this.attributes = attributes;
    

    public Map<String, Object> getAttributes() 
        return attributes;
    

【讨论】:

似乎这对于使用由 JWK 签名的 JWT 令牌非常具体。这也适用于不透明的令牌吗? 访问令牌和刷新令牌可能是不透明的令牌,但 OpenID Connect 将 id_token 定义为 JWT,这就是使用 JwkTokenStore 类对其进行解析的原因。

以上是关于使用带有 OpenID Connect 提供程序的 spring-security-oauth2 客户端时如何访问“id_token”和“refresh_token”?的主要内容,如果未能解决你的问题,请参考以下文章

现在他们正在弃用他们的 OpenID2 提供程序,因此与 Google 进行 OpenID Connect 委托?

OpenID Connect 提供程序实施

Python 中的 OpenID Connect 提供程序

如何使用 openid connect OIDC 加密 REST 正文

OpenID Connect 提供商 [关闭]

使用 OpenID Connect Gluu 身份验证提供程序来保护 Spring Boot Web App 客户端