前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring security oauth2 资源服务器WebAsyncTask/DeferredResult接口调用报错InsufficientAuthenticationException

spring security oauth2 资源服务器WebAsyncTask/DeferredResult接口调用报错InsufficientAuthenticationException

作者头像
路过君
发布2020-08-11 14:54:35
2.3K0
发布2020-08-11 14:54:35
举报

异常现象

  • 访问非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

资源服务配置

代码语言:javascript
复制
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

接口定义

代码语言:javascript
复制
@GetMapping("/result1")
public static Object result1() {
    return new WebAsyncTask(() -> {
        return "hello1";
    });
}
@GetMapping("/result2")
public static Object result2() {
    return "hello2";
}

源码跟踪

调用WebAsyncTask/DeferredResult接口时会进项两次认证:

  1. 处理传入请求时,可以正常完成认证获取用户信息。
  2. 处理请求响应时,由于使用了WebAsyncTask,响应处理使用了另一个线程,而非web请求处理线程,此线程中无法获取oauth2ClientContext
  • org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices
代码语言:javascript
复制
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
代码语言:javascript
复制
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

解决方案

  1. 使用token-info-url,并实现userDetailsService获取用户信息,而非user-info-url
  2. 不使用WebAsyncTask/DeferredResult
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 异常现象
  • spring security 版本2.3.8
  • 资源服务配置
  • 接口定义
  • 源码跟踪
  • 解决方案
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档