with secure mkyong example and spring spring-security spring-security-oauth2

secure - spring security oauth2 example mkyong



Protegiendo la API REST con OAuth2: Error al crear el bean con el nombre ''scopedTarget.oauth2ClientContext'': el alcance ''sesión'' no está activo (5)

Estoy probando el código que tengo ahora, pero de ninguna manera estoy casado con esta implementación. Si puedes mostrarme una manera radicalmente diferente de lograr lo que quiero lograr, excelente

Si su principal problema es implementar el servidor de recursos y además, está abierto a soluciones totalmente diferentes, puede usar las configuraciones automáticas del servidor de recursos de Spring Boot. De esta manera usted tendría una ResourceServerConfiguration servidor de ResourceServerConfiguration como la siguiente:

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

Con un archivo de configuración application.yml en su src/main/resources :

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

Debe agregar su client-id client-secret y token-info-uri allí. token-info-uri es el punto final en el Servidor de Autorización que nuestro servidor de recursos consultará sobre la validez de los Tokens de Acceso pasados.

Con estos arreglos, si el cliente envía una solicitud a, digamos, /api/greet API:

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

Nuestro servidor de recursos extraerá el token de acceso del portador de la solicitud y enviará la siguiente solicitud al servidor de autorización para validar el token de acceso:

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

Si el token era válido, el servidor de autorización envía una respuesta 200 OK con un cuerpo JSON como el siguiente:

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

De lo contrario, devolverá un 4xx Client Error .

Este fue un proyecto experto con un pom.xml como el siguiente:

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

Y una clase de Application típica:

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

Puede consultar la documentación de arranque de primavera en las configuraciones automáticas del servidor de recursos here .

He estado trabajando durante unos días para intentar implementar una protección auth2 en una API REST. He intentado un montón de configuraciones diferentes, pero todavía no he logrado que funcione.

Estoy probando el código que tengo ahora, pero de ninguna manera estoy casado con esta implementación. Si puedes mostrarme una manera radicalmente diferente de lograr lo que quiero lograr, genial.

Mi flujo se ve así:

  1. El cliente comprueba el servidor de autenticación, obtiene el token.
  2. El cliente envía el token al servidor de recursos.
  3. Resource Server usa Auth Server para asegurarse de que el token sea válido.

El Auth Server funciona bien. Estoy teniendo problemas para configurar el servidor de recursos.

Configuraciones en el servidor de recursos

Aquí están algunas de mis configuraciones. Tengo este frijol

Plantilla Ouath Rest

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

Configuración de seguridad

Yo uso ese bean en mi configuración de seguridad principal en el Servidor de recursos:

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

Cosas extra en la configuración de seguridad

Aaand, algunos de los frijoles que creo que son menos relevantes, pero aquí están en caso de que los necesite:

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

Error

El servidor de recursos se inicia muy bien. El cliente obtiene su token de autenticación del Servidor de autenticación.com y lo envía en el encabezado de la solicitud a una URL de la API. Y me sale el siguiente error:

Estado HTTP 500: error al crear bean con el nombre ''scopedTarget.oauth2ClientContext'': el ''sesión'' del alcance no está activo para el subproceso actual; considere la posibilidad de definir un proxy de ámbito para este bean si pretende referirse a él desde un singleton; la excepción anidada es java.lang.IllegalStateException: No se encontró una solicitud vinculada a un hilo: ¿Se refiere a solicitar atributos fuera de una solicitud web real o está procesando una solicitud fuera del hilo que originalmente recibió? Si realmente está operando dentro de una solicitud web y aún recibe este mensaje, es probable que su código se esté ejecutando fuera de DispatcherServlet / DispatcherPortlet: en este caso, use RequestContextListener o RequestContextFilter para exponer la solicitud actual.

Informe de excepciones

Error al crear el bean con el nombre ''scopedTarget.oauth2ClientContext'': el ámbito ''session'' no está activo para el hilo actual; considere la posibilidad de definir un proxy de ámbito para este bean si pretende referirse a él desde un singleton; la excepción anidada es java.lang.IllegalStateException: No se encontró una solicitud vinculada a un hilo: ¿Se refiere a solicitar atributos fuera de una solicitud web real o está procesando una solicitud fuera del hilo que originalmente recibió? Si realmente está operando dentro de una solicitud web y aún recibe este mensaje, es probable que su código se esté ejecutando fuera de DispatcherServlet / DispatcherPortlet: en este caso, use RequestContextListener o RequestContextFilter para exponer la solicitud actual.

El servidor encontró un error interno que le impidió cumplir con esta solicitud.

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)

He probado muchas configuraciones diferentes, busqué un montón de recursos en línea y no he llegado a ninguna parte. ¿Estoy usando las clases correctas? ¿Alguna idea de qué configuraciones podría necesitar cambiar?


Creo que la raíz del problema es que crea el OAuth2ClientAuthenticationProcessingFilter y OAuth2ClientContextFilter con el new operador.

Si nos fijamos en el 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)

hay una cadena de cómo va desde OAuth2ClientAuthenticationProcessingFilter a JdkDynamicAopProxy e intenta obtener el bean. Y puedo asumir que debido a que el bean se creó a partir del contenedor de Spring, no puede obtener el bean desde el alcance de la sesión.

Intente @Bean sus filtros en la anotación @Bean para ponerlos en contexto. Además, creo que vale la pena establecer el alcance correcto: la request coincidiría mejor aquí.



Terminé resolviendo esto después de mirar la documentación de Spring.

Resultó que el contexto del alcance no existía realmente en mi aplicación, porque no lo había inicializado.

Lo inicialicé agregando este oyente:

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


Una forma aún más fácil de habilitar la escucha de contexto de solicitud es agregar una anotación de bean en su aplicación.

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