微服务和 Spring Security OAuth2

Posted

技术标签:

【中文标题】微服务和 Spring Security OAuth2【英文标题】:Microservices and Spring Security OAuth2 【发布时间】:2014-12-27 11:34:21 【问题描述】:

我已经在另一个项目中运行了 OAuth2 授权服务器。现在我需要使用 OAuth2 保护几个简单的 spring-boot 休息服务器。但我发现 Spring documentation 在分离授权和资源服务器方面确实非常有限。

我还发现了几个问题,the answer 一直是“只要它们共享相同的 tokenStore 数据源,它们就可以是不同的盒子”。这真的是真的吗?这怎么可能适用于微服务?每个 REST 服务都需要实现自己的 OAuth 授权服务器,这似乎是一件非常奇怪的事情。

那么,如何为引用远程 oauth 授权服务器(甚至可能不是用 Spring 编写)的 spring-boot rest-endpoints 设置 Oauth2.0 安全性?

有一个叫做RemoteTokenServices 的东西看起来很有希望,但它根本没有真正记录在案。

【问题讨论】:

【参考方案1】:

RemoteTokenService 的工作示例可以在 here 找到。

更多关于check_tokenapi的详细信息,可以参考org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint.java

在资源服务器端,OAuth2AuthenticationProcessingFilter 通过调用 OAuth2AuthenticationManager.authenticate() 方法验证 OAuth2 令牌,该方法调用 RemoteTokenServices.loadAuthentication() 以验证来自 Auth 服务器的令牌。

【讨论】:

【参考方案2】:

在配置您的 auh 服务器时::

ClientDetailsServiceConfigurer 中为资源服务器创建一个新的clientDetails。将用于配置RemoteTokenService

在资源服务器中配置 Spring Security OAuth2:

创建一个使用@EnableWebSecurity@Configuration 注释并扩展WebSecurityConfigurerAdapter 的类。

@Configuration
@EnableWebSecurity
protected static class ResourceConfiguration extends WebSecurityConfigurerAdapter 
  // methods        

创建一个带有@Bean 注解的方法,该方法将返回TokenService 的实例,该实例将用于创建AuthenticationManager

在这个方法中创建一个RemoteTokenService的实例并设置clientId、client_secret、checkTokenEndpointUrl和DefaultAccessTokenConverterWithClientRoles(这个类是我们在验证accessToken时获取client_authority的实现在 OAuth2 服务器中。)

@Bean
public ResourceServerTokenServices tokenService() 
    RemoteTokenServices tokenServices = new RemoteTokenServices();
    tokenServices.setClientId("resource_id");
    tokenServices.setClientSecret("resource_secret");
    tokenServices.setCheckTokenEndpointUrl("http://<server-url>: <port>/oauth/check_token");
    return tokenServices;

覆盖authenticationManagerBean() 方法并使用@Bean 对其进行注释,并返回一个OAuth2AuthenticationManager 的实例并注入TokenService

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception 
    OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
    authenticationManager.setTokenServices(tokenService());
    return authenticationManager;

创建一个用@EnableResourceServer@Configuration注解的类并扩展ResourceServerConfigurerAdapter

@Configuration
@EnableResourceServer
protected static class ResourceServerConfig extends ResourceServerConfigurerAdapter 
  // Mehotds

覆盖超类的配置方法来配置资源服务器。 不同的配置器来配置资源服务器。

ResourceServerSecurityConfigurer:配置Resource_id。

HttpSecurity:这将配置安全过滤器,告诉它用户需要对受保护的 URL (API) 进行身份验证。

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
    resources.resourceId("resource_id");


@Override
public void configure(HttpSecurity http) throws Exception 
    // @formatter:off
    http
     .authorizeRequests()
     .antMatchers("/**").authenticated()
     .and()
     .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    // @formatter:on

.antMatcher("/**").authenticated() 这一行将保护资源服务器的每个 api url。 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 不会创建会话。

PS:: 如果有什么不对的,请告诉我。

【讨论】:

此设置是否在真实应用中测试过?其中大部分看起来与我在项目中所做的相同。但是,我在无状态配置方面遇到了麻烦。登录和授权页面之间丢失了一些东西。我认为 OAuth2 实现不支持无状态设置,因为 RequestCache 接口的唯一实现需要一个 http 会话来存储请求信息(HttpSessionRequestCache)。无状态策略会将此请求数据存储回响应中(可能在 cookie 中),因此任何其他副本都可以继续授权【参考方案3】:

如果资源服务器有某种方法可以验证访问令牌是否真实(由授权服务器颁发)以及某种解码方法(可能需要例如,了解它授予的范围)。 OAuth2 规范没有说明如何实现这一点。

如果资源与授权服务器在同一个进程中,那么共享数据是一个简单的选择。否则,它需要某种方式来翻译令牌。如果令牌只是一个不透明的随机字节串,那么显然它必须用它来交换真实的信息。这就是RemoteTokenServices 所做的。授权服务器公开一个/check_token 端点以允许对令牌进行解码。

另一种方法是对令牌中的信息进行实际编码,并让授权服务器对其进行数字签名。然后资源服务器可以解码和验证令牌本身,只要它理解格式。

我建议您查看 Cloudfoundry UAA,它提供了一个开箱即用的授权服务器,该服务器使用签名的 JWT 令牌实现(它还公开了 /check_token 端点)。 Tokens overview 和 API docs 可能是一个很好的起点。

【讨论】:

tokenServices.setCheckTokenEndpointUrl("http://&lt;server-url&gt;: &lt;port&gt;/oauth/check_token"); 有没有办法避免硬编码的网址?我想直接调用任何一个身份验证服务器实例(从注册表服务器获取运行时 url)或从 redis 检查令牌。 (在 auth 中使用 redis 令牌存储)这有可能吗?

以上是关于微服务和 Spring Security OAuth2的主要内容,如果未能解决你的问题,请参考以下文章