Spring Security实现OAuth2.0——资源服务
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security实现OAuth2.0——资源服务相关的知识,希望对你有一定的参考价值。
参考技术A在上一篇文章中,我们已经实现了授权服务器,拿到访问token已经不是问题了。本文主要讲述如何搭建一个资源服务器,根据第三方客户端访问请求和token来实现资源的权限控制。
资源服务器的核心配置类就是需要继承 ResourceServerConfigurerAdapter ,并重写其中的资源配置方法和安全鉴权方法。
token校验有两种方式:
我们该案例中资源服务器和授权服务器是不同的机器,大多数实际业务场景中也是不同的,所以我们以 RemoteTokenServices 为例进行演示,这里放到和资源服务器配置类一块:
资源服务器也是一个独立的应用,所以也需要增加安全访问的配置,这个在先前讲Spring Security的时候已经详细介绍过了。
最后,增加我们的访问资源即可,这里就是Controller。
此时,我们就完成了资源服务器的所有内容。
我们以 客户端模式 启动上一节的授权服务器后,通过HTTP工具(我这里使用IDEA的插件RestClient)来进行如下的测试:
首先访问如下请求以获取访问token:
得到的内容如下:
此时我们先不使用资源服务器,我们使用如下的请求来模拟资源服务器的验证token,以此来判断授权服务器是否能正常工作:
得到的返回内容是:
可以看到,允许访问的资源ID是 user 、访问的资源域是 all ,拥有的权限是 user:query ,供client_id为 iSchool 的第三方应用访问,这和我们前面配置授权服务器时的内容一致,因此授权服务是正常的。
然后,我们就启动资源服务器,现在来请求资源服务器的资源,并带上我们从授权服务器那里得到的token:
得到的返回结果为:
但是,如果我们访问另外一个接口:
得到的结果就是:
很明显,当资源服务请求授权服务的时候,授权服务告诉资源服务,当前这个访问token只有 user:query 权限,所以需要其它权限才能访问的资源就被禁止。
如果我们的资源服务器域名称不是 all ,而是改成了 user ,那么访问资源就会报错如下信息:
在实际的使用场景中,一个访问token并不仅仅限制只能访问拥有权限范围内的接口,还会限制只能访问某个客户的信息,因此我们还需要在授权中增加用户的身份信息。待研究。
如何使用 Spring Cloud Security 实现 OAuth2“令牌交换”
【中文标题】如何使用 Spring Cloud Security 实现 OAuth2“令牌交换”【英文标题】:How to implement OAuth2 "Token Exchange" with Spring Cloud Security 【发布时间】:2016-04-26 15:05:11 【问题描述】:我想知道是否有人有一个例子来看看如何使用 Spring Cloud Security(使用 OAuth2)实现“令牌交换”技术。
目前我已经在微服务环境中实现了“令牌中继”技术,使用 ZuulProxy 来“中继”OAuth2 令牌并实现 SSO。这很好,但意味着每个微服务都使用相同的 clientId(在 ZuulProxy 设置中指定,因为 ZuulProxy 仅使用 authentication_code 授权类型和提供的 clientId 中继令牌)。 但是,对于内部微服务调用,我想“交换”令牌。这意味着在某些情况下,ZuulProxy 中继的令牌不是我需要用来验证/授权微服务 A 作为微服务 B 的客户端的令牌。
Spring Cloud 参考文档目前说:“基于 Spring Boot 和 Spring Security OAuth2,我们可以快速创建实现常见模式的系统,例如单点登录、令牌中继和令牌交换。” (http://cloud.spring.io/spring-cloud-security/spring-cloud-security.html)
我想参考文档中的“令牌交换”是指 OAuth2 扩展的实现,在本规范中进行了解释,这基本上是我需要的: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-token-exchange-03
正如我所说,我了解如何使用 SSO 和令牌中继,但我无法在参考文档中看到有关如何实现“令牌交换”的进一步说明。我也找不到实现示例。
有人知道我在哪里可以找到更多信息或示例吗?
非常感谢!
【问题讨论】:
同意。在所有微服务中重新使用 client_id 似乎是错误的。最糟糕的部分(在我看来)是它阻止了微服务被其他客户端使用。假设您有另一个具有自己的 client_id 的 sso 客户端......那么您不能使用任何现有的微服务?希望这得到解决。似乎已经完成了一些代币交换工作,但尚未完成/合并github.com/spring-projects/spring-security-oauth/pull/957 乍一看,我同意你的看法。但从您的客户的角度来看,他们将您的 API 网关视为一个奇点。从他们的角度来看,只有一个 API。您背后拥有多项服务这一事实是一个实施细节,不一定是您想向客户提供的服务。你决定将一个服务分成两个服务会发生什么?您是否必须让每个人重新发行授予新资源 ID 的令牌? 【参考方案1】:我很好奇为什么您需要“交换”令牌以从微服务 A 向微服务 B 进行调用,以及为什么中继还不够?您通过为服务间请求交换令牌来尝试实现什么?
我们的设置与Nordic APIs entry 中描述的非常相似。简短的版本是外部调用者使用不透明的令牌,但是一旦请求通过我们的网关,每个微服务都会获得相同令牌的 JWT 表示。我们必须实现一个自定义端点来执行对 JWT 的不透明交换。当服务需要相互交互时,当 A 需要调用 B 时,我们不交换令牌,我们只是中继令牌。 RestTemplate 或 Feign 客户端都会自动将令牌从 A 转发到 B。因此,上下文不会丢失。
现在,如果我们想控制访问,JWT 可以指定一组受众值,或者我们可以通过范围强制访问。我们实际上是根据用例将两者结合起来。
交换令牌并不是一项廉价的操作,事实上它在规模上相当昂贵,并且应该真正考虑为什么需要进行令牌交换以进行服务内通信。如果您的每个 API 请求都会导致服务 A 调用服务 B,并且您必须进行令牌交换,那么您将确保您的授权服务可以处理这种类型的工作负载。最后,IETF 令牌交换仍处于草案状态,并且在其演变过程中发生了相当大的变化,因此在规范接近最终确定之前,我不会对实施建议的方式抱有太大期望。
【讨论】:
【参考方案2】:我认为这是你可以尝试的。
在我的项目中,我们还使用 OAuth2、Eureka、Ribbon 让微服务相互通信。为了将 Ribbon 与 OAuth2 一起使用,我们采用的方法有点不同。
首先我们保持 restTemplate 不变。
@LoadBalanced
@Bean
public RestTemplate restTemplate()
但是,我们创建了 FeignClientIntercepter 实现 RequestIntercepter,它在通过 restTemplate 发出请求时为 OAuth 设置授权令牌。
@Component
public class FeignClientInterceptor implements RequestInterceptor
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_TOKEN_TYPE = "Jwt";
@Override
public void apply(RequestTemplate template)
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null && authentication
.getDetails() instanceof OAuth2AuthenticationDetails)
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication
.getDetails();
template.header(AUTHORIZATION_HEADER,
String.format("%s %s", BEARER_TOKEN_TYPE, details.getTokenValue()));
【讨论】:
以上是关于Spring Security实现OAuth2.0——资源服务的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security实现OAuth2.0——资源服务
spring security oauth2.0 实现 解决url-pattern .do .action
Spring Security 入门(1-3)Spring Security oauth2.0 指南