spring security oauth2 资源服务/客户端无法正确获取权限
时间:2022-07-23
本文章向大家介绍spring security oauth2 资源服务/客户端无法正确获取权限,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
异常现象
当资源服务/客户端使用token-info-uri校验token时无法获取全部的授权权限,只能获取其中一个权限,使用user-info-uri则可以获取全部的授权权限
spring security 版本2.3.8
资源服务配置
security:
oauth2:
client:
client-id: client1
client-secret: client1pwd
access-token-uri: 'http://localhost:11000/oauth/token'
user-authorization-uri: 'http://localhost:11000/oauth/authorize'
scope: all
resource:
token-info-uri: 'http://localhost:11000/oauth/check_token'
user-info-uri: 'http://localhost:11000/oauth/check_user'
prefer-token-info: true
- prefer-token-info默认值为true,既优先使用token-info-uri校验token认证信息
- prefer-token-info设置为false,或不配置token-info-uri则会使用user-info-uri,适用于需要获取userdetails信息的场景
源码跟踪
1. 授权服务
- org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint
public class CheckTokenEndpoint {
@RequestMapping(value = "/oauth/check_token", method = RequestMethod.POST)
@ResponseBody
public Map<String, ?> checkToken(@RequestParam("token") String value) {
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
if (token == null) {
throw new InvalidTokenException("Token was not recognised");
}
if (token.isExpired()) {
throw new InvalidTokenException("Token has expired");
}
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
Map<String, Object> response = (Map<String, Object>)accessTokenConverter.convertAccessToken(token, authentication);
// gh-1070
response.put("active", true); // Always true if token exists and not expired
return response;
}
}
跟踪发现返回的信息中authorities字段是一个集合
2. 资源服务
使用token-info-uri
- 跟踪发现返回的认证信息中,集合全部被解析成了字符串
- 跟踪org.springframework.web.client.HttpMessageConverterExtractor 发现返回的响应信息为xml,其中authorities集合被序列化为多个<authorities>元素,而没有被正确反序列化为集合类型
- org.springframework.security.oauth2.provider.token.RemoteTokenServices
public class RemoteTokenServices implements ResourceServerTokenServices {
// 校验令牌获取认证信息
@Override
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
formData.add(tokenName, accessToken);
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret));
// 发送post请求调用token-info-uri,获取认证信息
Map<String, Object> map = postForMap(checkTokenEndpointUrl, formData, headers);
if (map.containsKey("error")) {
if (logger.isDebugEnabled()) {
logger.debug("check_token returned error: " + map.get("error"));
}
throw new InvalidTokenException(accessToken);
}
// gh-838
if (map.containsKey("active") && !"true".equals(String.valueOf(map.get("active")))) {
logger.debug("check_token returned active attribute: " + map.get("active"));
throw new InvalidTokenException(accessToken);
}
return tokenConverter.extractAuthentication(map);
}
// 发送post请求
private Map<String, Object> postForMap(String path, MultiValueMap<String, String> formData, HttpHeaders headers) {
if (headers.getContentType() == null) {
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
}
@SuppressWarnings("rawtypes")
Map map = restTemplate.exchange(path, HttpMethod.POST,
new HttpEntity<MultiValueMap<String, String>>(formData, headers), Map.class).getBody();
@SuppressWarnings("unchecked")
Map<String, Object> result = map;
// 返回令牌信息
return result;
}
}
使用user-info-url
- 跟踪发现返回的认证信息中,集合解析为ArrayList
- 跟踪org.springframework.web.client.HttpMessageConverterExtractor发现返回的响应信息为json
- org.springframework.boot.autoconfigure.security.oauth2.resourceUserInfoTokenServices
public class UserInfoTokenServices implements ResourceServerTokenServices {
@Override
public OAuth2Authentication loadAuthentication(String accessToken)
throws AuthenticationException, InvalidTokenException {
Map<String, Object> map = getMap(this.userInfoEndpointUrl, accessToken);
if (map.containsKey("error")) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("userinfo returned error: " + map.get("error"));
}
throw new InvalidTokenException(accessToken);
}
return extractAuthentication(map);
}
}
真相在这里
进一步跟踪发现: 请求user-info-url时header.Accept=“application/json” 请求token-info-url时header.Accept=“application/xml, text/xml, application/json, application/+xml, application/+json”,如果授权服务器支持xml格式contenttype则会有限返回xml格式
- org.springframework.boot.autoconfigure.security.oauth2.resource.DefaultUserInfoRestTemplateFactory
public class DefaultUserInfoRestTemplateFactory implements UserInfoRestTemplateFactory {
@Override
public OAuth2RestTemplate getUserInfoRestTemplate() {
...
// 此处加入了拦截器,为请求头加上Accept="application/json"
this.oauth2RestTemplate.getInterceptors()
.add(new AcceptJsonRequestInterceptor());
...
}
}
解决方案
以下三种都可以,按需选择
- 检查授权服务是否包含jackson-dataformat-xml依赖,删除此依赖则默认返回json数据
- 自定义资源服务RemoteTokenServices,header加上Accept=“application/json”
- 配置授权服务器默认ContentType
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
}
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇]
- Shell常用命令小结
- 插入法排序
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part II
- 震惊了!这样的js面试题让所有人-男默女泪
- 前端知识学了却不会用,都是没走心
- var a="xx";a=a+"ss";a的值变了,但"xx"字符串并没有变
- 先行者计划--1109微课总结 | 《通过二个demo初识webPack》
- 先行者计划--1107微课 《什么是Vuex?》| 文字简版
- 脱离前端菜鸟层次的二个关键点
- 【课堂笔记】先行者 3.0版本的vueJs课程的第三次课
- ASP.NET:创建Linked ValidationSummary, 深入理解ASP.NET的Validation
- 【课堂笔记】先行者 3.0版本的vueJs课程的第二次课
- 用Python做证券指数的三种策略分析
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释