spring security oauth2 资源服务器WebAsyncTask/DeferredResult接口调用报错InsufficientAuthenticationException
时间:2022-07-23
本文章向大家介绍spring security oauth2 资源服务器WebAsyncTask/DeferredResult接口调用报错InsufficientAuthenticationException,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
异常现象
- 访问非WebAsyncTask接口正常
- 访问WebAsyncTask/DeferredResult接口成功执行代码逻辑,但返回信息抛出异常InsufficientAuthenticationException
- 服务报错: Could not fetch user details: class org.springframework.beans.factory.BeanCreationException, Error creating bean with name ‘scopedTarget.oauth2ClientContext’: Scope ‘request’ is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
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: false
接口定义
@GetMapping("/result1")
public static Object result1() {
return new WebAsyncTask(() -> {
return "hello1";
});
}
@GetMapping("/result2")
public static Object result2() {
return "hello2";
}
源码跟踪
调用WebAsyncTask/DeferredResult接口时会进项两次认证:
- 处理传入请求时,可以正常完成认证获取用户信息。
- 处理请求响应时,由于使用了WebAsyncTask,响应处理使用了另一个线程,而非web请求处理线程,此线程中无法获取oauth2ClientContext。
- org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices
public class UserInfoTokenServices implements ResourceServerTokenServices {
...
private Map<String, Object> getMap(String path, String accessToken) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Getting user info from: " + path);
}
try {
OAuth2RestOperations restTemplate = this.restTemplate;
if (restTemplate == null) {
BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
resource.setClientId(this.clientId);
restTemplate = new OAuth2RestTemplate(resource);
}
// 在返回线程中这里将报错
OAuth2AccessToken existingToken = restTemplate.getOAuth2ClientContext()
.getAccessToken();
if (existingToken == null || !accessToken.equals(existingToken.getValue())) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(
accessToken);
token.setTokenType(this.tokenType);
restTemplate.getOAuth2ClientContext().setAccessToken(token);
}
return restTemplate.getForEntity(path, Map.class).getBody();
}
catch (Exception ex) {
this.logger.warn("Could not fetch user details: " + ex.getClass() + ", "
+ ex.getMessage());
return Collections.<String, Object>singletonMap("error",
"Could not fetch user details");
}
}
}
- org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration
public class OAuth2RestOperationsConfiguration {
@Configuration
@ConditionalOnMissingBean(OAuth2ClientConfiguration.class)
@Conditional({ OAuth2ClientIdCondition.class, NoClientCredentialsCondition.class })
@Import(OAuth2ProtectedResourceDetailsConfiguration.class)
protected static class RequestScopedConfiguration {
@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public DefaultOAuth2ClientContext oauth2ClientContext() {
DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext(
new DefaultAccessTokenRequest());
Authentication principal = SecurityContextHolder.getContext()
.getAuthentication();
if (principal instanceof OAuth2Authentication) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
Object details = authentication.getDetails();
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oauthsDetails = (OAuth2AuthenticationDetails) details;
String token = oauthsDetails.getTokenValue();
context.setAccessToken(new DefaultOAuth2AccessToken(token));
}
}
return context;
}
}
}
OAuth2ClientContextBean生命周期为request,因此在非request线程中无法获取OAuth2ClientContext
解决方案
- 使用token-info-url,并实现userDetailsService获取用户信息,而非user-info-url
- 不使用WebAsyncTask/DeferredResult
- Go-List
- 分享张戈博客自用的php网址在线转换二维码的API源码
- zabbix agentd客户端插件Shell一键自动安装脚本
- SendCloud邮件队列状态和已使用额度的Python监控脚本
- linux/scp命令报“bash: scp: command not found lost connection”错误的解决办法
- bat/cmd批处理连接SqlServer数据库查询脚本
- 一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎
- 解决mstsc无法连接问题:由于没有远程桌面授权服务器可以提供许可证…
- Apache/Nginx伪静态规则匹配http://出现的问题与解决
- 微信文件微起底
- Go语言TCP Socket编程--1
- Go语言TCP Socket编程--2
- 服务器 数据库设计技巧--1
- CVE-2015-0235:Linux glibc高危漏洞的检测及修复方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 面试官:你精通多少种语言的 Hello World?
- Redis安装(Windows和Linux)详细图解
- 史上最详细版 头文件biso.h,graphics.h,libbgi.a
- ZooKeeper入门,这一篇给你讲的明明白白
- 数论-GCD、LCM、扩展欧几里得
- “豪 横”版 channel_v3.json,你确定不需要?
- Redis-性能测试(redis-benchmark)
- 一条贪吃蛇的使命——零基础入门贪吃蛇游戏
- 数论-快速幂、矩阵快速幂
- 字节一面,面试官告诉我链表掌握的不熟练
- 好玩、有趣的 Linux 命令学习神器 kmdr!
- 基于web的机票管理系统设计与实现(二)
- 任意进制转换(2进制、8进制、16进制等)
- 动态规划-数位DP
- R海拾遗-table1绘制