복붙노트

[SPRING] OAuth2로 REST API 보호 : 'scopedTarget.oauth2ClientContext'라는 이름으로 빈 생성 오류 : 'session'범위가 활성화되지 않았습니다.

SPRING

OAuth2로 REST API 보호 : 'scopedTarget.oauth2ClientContext'라는 이름으로 빈 생성 오류 : 'session'범위가 활성화되지 않았습니다.

나는 며칠 동안 REST API에서 oauth2 보호를 구현하려고 노력했다. 나는 다양한 구성을 시도했지만 여전히 작동하도록 관리하지 못했습니다.

나는 지금 당장 가지고있는 코드를 증명하고 있지만, 나는이 구현과 결코 결혼하지 않는다. 내가 성취하고자하는 것을 근본적으로 다른 방식으로 보여줄 수 있다면, 좋을 것입니다.

내 흐름은 다음과 같습니다.

인증 서버가 올바르게 작동합니다. 리소스 서버 구성에 문제가 있습니다.

다음은 내 구성 중 일부입니다. 나는이 콩을 가지고있다.

@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;
    }
}

리소스 서버의 기본 보안 구성에서 해당 콩을 사용합니다.

@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;
    }
}

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;
}

리소스 서버가 정상적으로 시작됩니다. 클라이언트는 theauthenticationServer.com에서 auth 토큰을 가져 와서 요청 헤더에서 api url로 전송합니다. 그리고 다음과 같은 오류가 발생합니다 :

        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)

나는 많은 다른 설정을 시도해 보았고, 온라인 자원을 엄청 많이 보았고, 나는 아무데도 가지 못했다. 올바른 수업을 사용하고 있습니까? 모든 설정을 변경해야 할 수도 있습니다 어떤 생각?

해결법

  1. ==============================

    1.요청 컨텍스트 수신기를 활성화하는 더욱 쉬운 방법은 앱에 bean 주석을 추가하는 것입니다.

    요청 컨텍스트 수신기를 활성화하는 더욱 쉬운 방법은 앱에 bean 주석을 추가하는 것입니다.

    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }
    
  2. ==============================

    2.스프링 문서를 살펴본 후에이 문제를 해결했습니다.

    스프링 문서를 살펴본 후에이 문제를 해결했습니다.

    그것은 초기화되지 않았기 때문에 스코프 컨텍스트가 실제로 내 앱에 존재하지 않는다는 것이 밝혀졌습니다.

    이 리스너를 추가하여 초기화했습니다.

    <listener>
     <listener-class>
            org.springframework.web.context.request.RequestContextListener
     </listener-class>
    </listener>
    
  3. ==============================

    3.주요 문제가 Resource Server를 구현하고 있으며 완전히 다른 솔루션을 사용할 수 있다면 Spring Boot의 리소스 서버 자동 구성을 사용할 수 있습니다. 이 방법을 사용하면 다음과 같은 ResourceServerConfiguration을 갖게됩니다.

    주요 문제가 Resource Server를 구현하고 있으며 완전히 다른 솔루션을 사용할 수 있다면 Spring Boot의 리소스 서버 자동 구성을 사용할 수 있습니다. 이 방법을 사용하면 다음과 같은 ResourceServerConfiguration을 갖게됩니다.

    @Configuration
    @EnableResourceServer
    public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                        .anyRequest().authenticated();
            // you can put your application specific configurations here
            // here i'm just authenticating every request
        }
    }
    

    src / main / resources에 application.yml 설정 파일을 가지고 :

    security:
      oauth2:
        client:
          client-id: client
          client-secret: secret
        resource:
          token-info-uri: http://localhost:8888/oauth/check_token
    

    client-id, client-secret 및 token-info-uri를 거기에 추가해야합니다. token-info-uri는 자원 서버가 전달 된 액세스 토큰의 유효성에 대해 문의 할 인증 서버의 엔드 포인트입니다.

    이러한 준비를 통해 클라이언트가 / api / greet API에 요청을하면 :

    GET /api/greet HTTP/1.1
    Host: localhost:8080
    Authorization: bearer cef63a29-f9aa-4dcf-9155-41fb035a6cdb
    

    Google의 리소스 서버는 요청에서 Bearer 액세스 토큰을 추출하고 액세스 토큰의 유효성을 검사하기 위해 다음 요청을 인증 서버에 보냅니다.

    GET /oauth/check_token?token=cef63a29-f9aa-4dcf-9155-41fb035a6cdb HTTP/1.1
    Host: localhost:8888
    Authorization: basic base64(client-id:client-secret)
    

    토큰이 유효하면 Authorization Server는 다음과 같이 JSON 본문과 함께 200 OK 응답을 보냅니다.

    {"exp":1457684735,"user_name":"me","authorities":["ROLE_USER"],"client_id":"client","scope":["auth"]}
    

    그렇지 않으면 4xx 클라이언트 오류가 반환됩니다.

    이것은 다음과 같은 pom.xml이있는 maven 프로젝트입니다.

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
        </dependency>
    </dependencies>
    

    그리고 일반적인 Application 클래스 :

    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    리소스 서버 자동 구성에 대한 스프링 부트 설명서를 여기에서 확인할 수 있습니다.

  4. ==============================

    4.문제의 근본 원인은 새 연산자로 OAuth2ClientAuthenticationProcessingFilter 및 OAuth2ClientContextFilter를 만드는 것입니다.

    문제의 근본 원인은 새 연산자로 OAuth2ClientAuthenticationProcessingFilter 및 OAuth2ClientContextFilter를 만드는 것입니다.

    stacktrace를 보면

    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)
    

    OAuth2ClientAuthenticationProcessingFilter에서 JdkDynamicAopProxy로 이동하여 빈을 얻으려는 체인이 있습니다. 그리고 그 bean이 Spring 컨테이너에서 생성 되었기 때문에 세션 범위에서 bean을 가져올 수 없다고 생각할 수 있습니다.

    @Bean 어노테이션에 필터를 래핑하여 문맥에 넣으십시오. 또한, 나는 그것이 올바른 범위를 설정 가치가 있다고 생각 : 요청은 여기에 가장 일치하는 것입니다.

  5. ==============================

    5.나는 spock-spring 1.1-groovy-2.4-rc-2와 함께 spring-boot 1.4.1을 사용할 때도 같은 문제에 직면했다. 그것을 고치는 가장 쉬운 방법은 Spock 1.0을 사용하는 것입니다.

    나는 spock-spring 1.1-groovy-2.4-rc-2와 함께 spring-boot 1.4.1을 사용할 때도 같은 문제에 직면했다. 그것을 고치는 가장 쉬운 방법은 Spock 1.0을 사용하는 것입니다.

    이미보고 된 버그가 있습니다. https://github.com/spockframework/spock/issues/655

  6. from https://stackoverflow.com/questions/35875098/protecting-rest-api-with-oauth2-error-creating-bean-with-name-scopedtarget-oau by cc-by-sa and MIT license