使用 OAuth2 实现 Spring RESTful Web 服务 - 将 id 令牌保存为 id 会话

Posted

技术标签:

【中文标题】使用 OAuth2 实现 Spring RESTful Web 服务 - 将 id 令牌保存为 id 会话【英文标题】:Implement Spring RESTful Web Services with OAuth2 - Save id token as id session 【发布时间】:2018-10-29 03:27:27 【问题描述】:

我已经在带有 id 令牌的 Spring Boot 中使用 OAuth2 实现了 Spring RESTful Web 服务:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;


@Configuration
public class ResourceServerConfig 

    private static final String SERVER_RESOURCE_ID = "oauth2-server";
    private static final Logger LOG = LoggerFactory.getLogger(ResourceServerConfig.class);
    private static InMemoryTokenStore tokenStore = new InMemoryTokenStore();

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Configuration
    @EnableResourceServer
    @Order(2)
    protected class ResourceServer extends ResourceServerConfigurerAdapter 

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
            LOG.info("ResourceServerSecurityConfigurer");
            resources.tokenStore(tokenStore).resourceId(SERVER_RESOURCE_ID);
        

        @Override
        public void configure(HttpSecurity http) throws Exception 
            LOG.info("HttpSecurity");
            http.anonymous().disable().
                requestMatchers().antMatchers("/v1/**").
                and().authorizeRequests().antMatchers("/v1/**").authenticated();
        
    

    @Configuration
    @EnableAuthorizationServer
    @Order(1)
    protected class AuthConfig extends AuthorizationServerConfigurerAdapter 

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
            LOG.info("AuthorizationServerEndpointsConfigurer");
            endpoints.userDetailsService(userDetailsService)
            .authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
        

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
            LOG.info("ClientDetailsServiceConfigurer: ", clients);
            clients.inMemory().withClient("client").authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit", "client_credentials")
                    .authorities("ROLE_CLIENT").scopes("read").resourceIds(SERVER_RESOURCE_ID).secret("secret");
        


    

我正在使用负载平衡器来路由身份验证和调用请求,但所有服务器之间的 id_token 并不相同,因为 id_token 仅保存在内存中,并且当用户调用身份验证时,可能会从服务器 1 获取 id_token1 以及调用请求时负载均衡器路由到服务器 2,此处不存在 id_token1。解决此问题的一个选项是使用 Redis(例如)来保存 id_token,另一个选项也可以使用 JWT。由于我使用 WebSphere 作为服务器应用程序我想使用会话 id 来在所有服务器上保留 id_token,我认为这不是一个好主意,但我仍然想实现这个解决方案,有您有任何想法如何实施此解决方案?

【问题讨论】:

【参考方案1】:

您已经知道两种选择。如果您的想法是使应用程序无状态,我建议您使用 JWT。但是,它的代价是为 JWT 添加额外的安全性,这样您就不会暴露您的令牌,因为一切都将在客户端。使用 JWT 的一个主要优势是您不必担心需要提供给 redis 服务器的额外服务器/资源。

【讨论】:

我需要使用 session_id 而不是我说的两个选项。 您能解释一下您为什么要这样做吗?如果你真的必须这样做,你可以集群你的服务器并使用粘性会话。 由于Enterprise的内部政治,他们不想改变id token 而不是JWT,所以我需要使用session id。

以上是关于使用 OAuth2 实现 Spring RESTful Web 服务 - 将 id 令牌保存为 id 会话的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Spring Cloud Security 实现 OAuth2“令牌交换”

使用 Spring Security 实现 OAuth2 隐式授权

使用 Spring Boot 1.5 在 oauth2 实现中返回错误凭据

Spring安全中的Oauth2客户端

Spring Boot + OAuth2 + Google Login - 如何实现注销

OIDC - 使用 IdentityServer3 和 Spring Boot 实现 OAuth2