【Spring Security】 Oauth2 resourceId
【Spring Security】 Oauth2 resourceId
Metadata
title: 【Spring Security】 Oauth2 resourceId
date: 2023-02-05 15:51
tags:
- 行动阶段/完成
- 主题场景/组件
- 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
- 细化主题/Module/SpringSecurity
categories:
- SpringSecurity
keywords:
- SpringSecurity
description: 【Spring Security】 Oauth2 resourceId
【Spring Security】 Oauth2 resourceId
在 oauth_client_details(Oauth2 客户端信息)表中,有一个 resource_ids 字段,这个字段是干嘛的呢?
作用
resource_ids 字段表示资源服务器 id 集合,也就是表示当前客户端可以访问哪些资源服务器。
如果设置了客户端的 resourceId,就表示只能访问这些 resourceId 标识的资源服务器,可以达到资源服务服务级别的访问控制,之前我们没有设置资源 ID,默认为空时不会校验,也就是没有配置时可以访问所有的资源服务器。
案例演示
1. 设置资源服务器 resourceId
配置相当简单,只需要在 ResourceServerSecurityConfigurer 配置中,添加一个字符串就可以了,一般使用 spring boot 应用名【spring.application.name】。
@Configuration
@EnableResourceServer//标识为资源服务
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MyResourceServerConfig extends ResourceServerConfigurerAdapter {
public MyResourceServerConfig() {
super();
}
@Value("${spring.application.name}")
private String appName;
@Autowired
MyAuthenticationEntryPoint myAuthenticationEntryPoint;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(myAuthenticationEntryPoint);
resources.resourceId(appName); // 设置resourceId 默认为spring.application.name
}
@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
}
2. 授权服务器添加 resourceId 集合
授权服务器也非常简单 ,如果使用的是数据库查询客户端信息,那么只要在 oauth_client_details 表中设置 resource_ids 就可以了。演示使用的是内存存储,则只需要设置 resourceIds 即可,这里表示 client 客户端只能访问 id 为【oauth2-resource-server001-test】的资源服务器。
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置客户端
clients
// 使用内存设置
.inMemory()
// client_id
.withClient("client")
// client_secret
.secret(passwordEncoder.encode("secret"))
// 授权类型: 授权码、刷新令牌、密码、客户端、简化模式、短信验证码
.authorizedGrantTypes("authorization_code", "refresh_token", "password", "client_credentials", "implicit", "sms_code")
// 授权范围,也可根据这个范围标识,进行鉴权
.scopes("app")
.accessTokenValiditySeconds(60*30)
.refreshTokenValiditySeconds(24*60*60)
// 授权码模式 授权页面是否自动授权
//.autoApprove(false)
// 拥有的权限
.authorities("add:user")
// 允许访问的资源服务 ID
.resourceIds("oauth2-resource-server001-test")
// 注册回调地址
.redirectUris("http://localhost:20000/code", "http://localhost:9001/resource001/login");
}
3. 测试
获取令牌,然后访问资源服务器,然后发生了异常,提示当前令牌没有访问当前资源服务的权限。
源码分析
那么对于 resourceId 的校验是在哪里进行的呢?
之前了解过资源服务器对令牌进行认证是在 OAuth2AuthenticationProcessingFilter 过滤器中进行的,在该过滤器中会使用 OAuth2AuthenticationManager 管理器对令牌进行认证校验。
在 OAuth2AuthenticationManager 中的 authenticate 方法完成了对 resourceId 的校验,具体处理逻辑如下
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null) {
throw new InvalidTokenException("Invalid token (token not found)");
} else {
String token = (String)authentication.getPrincipal();
// 1. 存储中获取令牌对应的认证信息
OAuth2Authentication auth = this.tokenServices.loadAuthentication(token);
if (auth == null) {
throw new InvalidTokenException("Invalid token: " + token);
} else {
// 2. 认证信息中,获取当前客户端的resourceId集合
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
// 3. 如果客户端、资源服务器配置了resourceId,则校验当前资源服务器的resourceId是否包含在客户端的resourceId中,没有则抛出 AccessDenied异常
if (this.resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(this.resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + this.resourceId + ")");
} else {
this.checkClientDetails(auth);
if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails)authentication.getDetails();
if (!details.equals(auth.getDetails())) {
details.setDecodedDetails(auth.getDetails());
}
}
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
return auth;
}
}
}
}