使用OAuth2保护REST API:创建名为"scopedTarget.oauth2ClientContext"的bean时出错:范围"会话"未激活

Joe*_*oey 19 spring spring-security spring-security-oauth2

我已经工作了几天,试图在REST API上实现oauth2保护.我已经尝试了很多不同的配置,但仍然没有设法让它工作.

我正在证明我现在的代码,但我与这个实现没有任何结合.如果你能告诉我一些完全不同的方式来实现我想要完成的目标,那就太好了.

我的流程如下:

  1. 客户端检查Auth Server,获取令牌.
  2. 客户端将令牌发送到Resource Server.
  3. Resource Server使用Auth Server确保令牌有效.

Auth服务器运行正常.我在配置资源服务器时遇到问题.

资源服务器上的配置

这是我的一些配置.我有这个豆子:

誓言模板

@EnableOAuth2Client
@Configuration
@Import({PropertiesConfig.class}) //Imports properties from properties files.
public class OauthRestTemplateConfig {



 @Bean
    public OAuth2RestTemplate oAuth2RestTemplate(OAuth2ClientContext oauth2ClientContext) {
        OAuth2RestTemplate template = new OAuth2RestTemplate(oauth2ResourceDetails(), oauth2ClientContext);
        return template;
    }

    @Bean
    OAuth2ProtectedResourceDetails oauth2ResourceDetails() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setId("theOauth");
        details.setClientId("clientID");
        details.setClientSecret("SecretKey");
        details.setAccessTokenUri("https://theAuthenticationServer.com/oauthserver/oauth2/token");
        details.setUserAuthorizationUri("https://theAuthenticationServer.com/oauthserver/oauth2/token");
        details.setTokenName("oauth_token");
        details.setPreEstablishedRedirectUri("http://localhost/login");
        details.setUseCurrentUri(true);
        return details;
    }
}
Run Code Online (Sandbox Code Playgroud)

安全配置

我在Resource Server的主安全配置中使用该bean:

@Slf4j
@Configuration
@EnableWebSecurity
@EnableOAuth2Client
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true, proxyTargetClass = true)
@Import({PropertiesConfig.class, OauthRestTemplateConfig.class})
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("oAuth2RestTemplate")
    private OAuth2RestTemplate oAuth2RestTemplate;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests()
                .accessDecisionManager(accessDecisionManager()) //This is a WebExpressionVoter. I don't think it's related to the problem so didn't include the source.
                    .antMatchers("/login").permitAll()      
                .antMatchers("/api/**").authenticated()
                .anyRequest().authenticated();
        http
                .exceptionHandling()
                .authenticationEntryPoint(delegatingAuthenticationEntryPoint());
        http
                .addFilterBefore(new OAuth2ClientContextFilter(), BasicAuthenticationFilter.class)
                .addFilterAfter(oauth2ClientAuthenticationProcessingFilter(), OAuth2ClientContextFilter.class)
        ;
    }

    private OAuth2ClientAuthenticationProcessingFilter oauth2ClientAuthenticationProcessingFilter() {
        OAuth2ClientAuthenticationProcessingFilter
                daFilter = new OAuth2ClientAuthenticationProcessingFilter("/api/**");
        daFilter.setRestTemplate(oAuth2RestTemplate);
        daFilter.setTokenServices(inMemoryTokenServices());
        return daFilter;
    } 

    private DefaultTokenServices inMemoryTokenServices() {
        InMemoryTokenStore tok = new InMemoryTokenStore();
        DefaultTokenServices tokenService = new DefaultTokenServices();
        tokenService.setTokenStore(tok);

        return tokenService;
    }
}
Run Code Online (Sandbox Code Playgroud)

安全配置中的额外内容

Aaand,我认为其中一些豆类不太相关,但在这里它们以防万一你需要它们:

@Bean
public DelegatingAuthenticationEntryPoint delegatingAuthenticationEntryPoint() {
    LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> matchers =
            Maps.newLinkedHashMap();

    //Match all HTTP methods
    matchers.put(new RegexRequestMatcher("\\/api\\/v\\d+\\/.*", null), oAuth2AuthenticationEntryPoint());
    matchers.put(AnyRequestMatcher.INSTANCE, casAuthenticationEntryPoint());

    DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(matchers);
    entryPoint.setDefaultEntryPoint(casAuthenticationEntryPoint());

    return entryPoint;
}
@Bean(name = "casEntryPoint")
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
    CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
    casAuthenticationEntryPoint.setLoginUrl(casUrl + "/login");
    casAuthenticationEntryPoint.setServiceProperties(serviceProperties());

    return casAuthenticationEntryPoint;
}
Run Code Online (Sandbox Code Playgroud)

错误

资源服务器启动就好了.客户端从AuthenticationServer.com获取其身份验证令牌,并将其在请求标头中发送到api URL.我收到以下错误:

HTTP状态500 - 创建名为'scopedTarget.oauth2ClientContext'的bean时出错:当前线程的作用域'session'无效; 考虑为这个bean定义一个范围代理,如果你想从一个单例引用它; 嵌套异常是java.lang.IllegalStateException:找不到线程绑定请求:您是指在实际Web请求之外的请求属性,还是在最初接收线程之外处理请求?如果您实际在Web请求中操作并仍然收到此消息,则您的代码可能在DispatcherServlet/DispatcherPortlet之外运行:在这种情况下,请使用RequestContextListener或RequestContextFilter来公开当前请求.

异常报告

创建名为'scopedTarget.oauth2ClientContext'的bean时出错:当前线程的作用域'session'无效; 考虑为这个bean定义一个范围代理,如果你想从一个单例引用它; 嵌套异常是java.lang.IllegalStateException:找不到线程绑定请求:您是指在实际Web请求之外的请求属性,还是在最初接收线程之外处理请求?如果您实际在Web请求中操作并仍然收到此消息,则您的代码可能在DispatcherServlet/DispatcherPortlet之外运行:在这种情况下,请使用RequestContextListener或RequestContextFilter来公开当前请求.

服务器遇到内部错误,导致无法完成此请求.

        org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' 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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:355)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
    com.sun.proxy.$Proxy26.getAccessToken(Unknown Source)
    org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:169)
    org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:94)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

root cause
        <pre>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/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
    org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
    org.springframework.web.context.request.SessionScope.get(SessionScope.java:91)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
    org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:187)
    com.sun.proxy.$Proxy26.getAccessToken(Unknown Source)
    org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:169)
    org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.attemptAuthentication(OAuth2ClientAuthenticationProcessingFilter.java:94)
    org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter.doFilter(OAuth2ClientContextFilter.java:60)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
Run Code Online (Sandbox Code Playgroud)

我已经尝试了很多不同的配置,在线查看了大量的资源,而我却无处可去.我使用正确的课程吗?知道我可能需要改变哪些配置?

Luc*_*olt 18

启用请求上下文侦听器的更简单方法是在应用程序中添加bean注释.

@Bean
public RequestContextListener requestContextListener() {
    return new RequestContextListener();
}
Run Code Online (Sandbox Code Playgroud)

  • 我遇到了与提问者相同的尝试设置的相同异常,除了我还声明了一个 `@Bean RequestContextListener`。这个声明应该在哪里?我已经在 Oauth 配置、调用 `configure(HttpSecurity http)` 的主 SecurityConfig 和包含一堆额外 bean 定义的辅助配置中尝试过它。 (3认同)

Joe*_*oey 5

I ended up resolving this thing after looking into Spring documentation.

It turned out that the scope context didn't actually exist in my app, because I hadn't initialized it.

I initialized it by adding this listener:

<listener>
 <listener-class>
        org.springframework.web.context.request.RequestContextListener
 </listener-class>
</listener>
Run Code Online (Sandbox Code Playgroud)