向 Google 进行身份验证时,从 Spring OAuth2 授权服务器发出 JWT 令牌

Posted

技术标签:

【中文标题】向 Google 进行身份验证时,从 Spring OAuth2 授权服务器发出 JWT 令牌【英文标题】:Issue JWT tokens from Spring OAuth2 Authorization Server when authenticating with Google 【发布时间】:2017-07-30 09:54:14 【问题描述】:

我想使用 Spring Oauth 创建一个授权服务器,它能够发布它自己的 JWT 令牌。授权服务器必须将身份验证委托给 Google。我一直在关注本教程,它几乎可以满足我的所有需求:https://spring.io/guides/tutorials/spring-boot-oauth2/

我能够将 Google 添加为身份验证提供程序,但我在 JWT 部分遇到了困难。

这是我的授权服务器配置:

@SpringBootApplication 
@EnableOAuth2Client
@EnableAuthorizationServer
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class MsAuthorizationGmailApplication extends WebSecurityConfigurerAdapter 

    @Autowired
    OAuth2ClientContext oauth2ClientContext;

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
            .authenticated().and().exceptionHandling()
            .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login/gmail")).and().logout()
            .logoutSuccessUrl("/").permitAll().and().csrf()
            .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
            .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
    

    @Bean
    @ConfigurationProperties("gmail")
    public ClientResources gmail() 
        return new ClientResources();
    

    private Filter ssoFilter() 
        CompositeFilter filter = new CompositeFilter();
        List<Filter> filters = new ArrayList<>();
        filters.add(ssoFilter(gmail(), "/login/gmail"));
        filter.setFilters(filters);
        return filter;
    

    private Filter ssoFilter(ClientResources client, String path) 
        OAuth2ClientAuthenticationProcessingFilter filter = new     OAuth2ClientAuthenticationProcessingFilter(
            path);
        OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
        filter.setRestTemplate(template);
        filter.setTokenServices(JwtConfig.tokenServices());
        return filter;
    

    public static void main(String[] args) 
        SpringApplication.run(MsAuthorizationGmailApplication.class, args);
    


在 JWT 配置中,我不想做任何花哨的事情,只是想暂时让它通过:

public final class JwtConfig 

    private static final String KEY = "123";

    private JwtConfig() 
    

    private static JwtAccessTokenConverter accessTokenConverter() 
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(KEY);
        return converter;
    

    private static TokenStore tokenStore() 
        return new JwtTokenStore(accessTokenConverter());
    

    public static DefaultTokenServices tokenServices() 
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    

我得到以下异常:

org.springframework.security.authentication.BadCredentialsException: Could not obtain user details from token
at org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:122) ~[spring-security-oauth2-2.0.12.RELEASE.jar:na]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) ~[spring-security-web-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:112) [spring-web-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:73) [spring-web-4.3.6.RELEASE.jar:4.3.6.RELEASE]
....
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.11.jar:8.5.11]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
Caused by: org.springframework.security.oauth2.common.exceptions.InvalidTokenException: Cannot convert access token to JSON
at org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter.decode(JwtAccessTokenConverter.java:287) ~[spring-security-oauth2-2.0.12.RELEASE.jar:na]
at org.springframework.security.oauth2.provider.token.store.JwtTokenStore.convertAccessToken(JwtTokenStore.java:88) ~[spring-security-oauth2-2.0.12.RELEASE.jar:na]
at org.springframework.security.oauth2.provider.token.store.JwtTokenStore.readAccessToken(JwtTokenStore.java:80) ~[spring-security-oauth2-2.0.12.RELEASE.jar:na]
at org.springframework.security.oauth2.provider.token.DefaultTokenServices.loadAuthentication(DefaultTokenServices.java:229) ~[spring-security-oauth2-2.0.12.RELEASE.jar:na]
at org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:112) ~[spring-security-oauth2-2.0.12.RELEASE.jar:na]
... 62 common frames omitted
Caused by: java.lang.IllegalArgumentException: JWT must have 3 tokens
at org.springframework.security.jwt.JwtHelper.decode(JwtHelper.java:49) ~[spring-security-jwt-1.0.0.RELEASE.jar:na]
at org.springframework.security.jwt.JwtHelper.decodeAndVerify(JwtHelper.java:74) ~[spring-security-jwt-1.0.0.RELEASE.jar:na]
at org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter.decode(JwtAccessTokenConverter.java:277) ~[spring-security-oauth2-2.0.12.RELEASE.jar:na]
... 66 common frames omitted

我对此的理解:看起来当 Google 发出访问令牌时,授权服务器(作为 Google OAuth 的客户端)尝试将访问令牌解码为 JWT,并抛出异常,因为 Google 的令牌不是有效的 JWT(它只是一个访问令牌)。

我想创建一个 JWT,其中包含访问令牌(将用于访问 Google API)和一些有关用户的其他信息。我还希望能够在访问令牌过期时刷新 JWT 令牌。有什么方法可以实现吗?

【问题讨论】:

【参考方案1】:

我不确定GMail,但对于您自己的授权服务器,您可以添加一个令牌增强器JwtAccessTokenConverter,它将您的令牌转换为JWT。

样品请参考oauth2-spring-boot-mongo-jwt-sample

一般情况下,普通令牌有效负载属于以下类型


    "access_token": "bc9c021f-b5ae-43af-9746-737b533f9bc5",
    "token_type": "bearer",
    "refresh_token": "fee7a2a1-eff9-4757-8dd3-5392ee225bea",
    "expires_in": 43199,
    "scope": "read-foo" 

而 JWT 看起来像这样


    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZm9vIl0sInVzZXJfbmFtZSI6InVzZXIiLCJzY29wZSI6WyJyZWFkLWZvbyJdLCJleHAiOjE1MTQ3ODMwNTIsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiJlMjM4MDg1YS0xZjFjLTQ5ZWQtODNiMC1iN2Q1MjI5OWUwZjYiLCJjbGllbnRfaWQiOiJ3ZWItY2xpZW50In0.-OSw1Vr4o1dnAQL3n7QFGG6UOXr4itc0Kp8dugyT4zU",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZm9vIl0sInVzZXJfbmFtZSI6InVzZXIiLCJzY29wZSI6WyJyZWFkLWZvbyJdLCJhdGkiOiJlMjM4MDg1YS0xZjFjLTQ5ZWQtODNiMC1iN2Q1MjI5OWUwZjYiLCJleHAiOjE1MTczMzE4NTIsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiIzYTA2OTZmMy1mYzg1LTQ2YTEtYjVlMC01NmQ2OGVmYTJhMmUiLCJjbGllbnRfaWQiOiJ3ZWItY2xpZW50In0.jSBriPfM-rSgHHLyifIuBHwrwCkyb5I2u2AKa8kQUUU",
    "expires_in": 43199,
    "scope": "read-foo",
    "jti": "e238085a-1f1c-49ed-83b0-b7d52299e0f6"

【讨论】:

以上是关于向 Google 进行身份验证时,从 Spring OAuth2 授权服务器发出 JWT 令牌的主要内容,如果未能解决你的问题,请参考以下文章

使用 google firebase 和 spring 进行身份验证

Spring Boot OAuth2 - 使用本地用户数据库向 Facebook 进行身份验证

使用 Spring Boot OAuth2 针对 Google 进行身份验证时如何将 google 用户电子邮件发送到白名单用户

如何使用 spring security 和 spring boot 对 Google 用户进行身份验证,将 mongoDB 作为存储库?

如何在弹出窗口中通过 OAuth 2.0 向 Google 进行身份验证?

Google Cloud Tasks 无法向 Cloud Run 进行身份验证