sessions remember management expired example control java spring-security oauth-2.0 spring-oauth2

java - remember - spring session management example



El ejemplo oficial de seguridad de Spring oauth2 no funciona debido a un conflicto de cookies(mecanismo de código de autorización) (3)

Según el tutorial Spring Boot y OAuth2.

Tengo la siguiente estructura de proyecto:

Y siguiente código fuente:

Aplicación Social.clase:

@SpringBootApplication @RestController @EnableOAuth2Client @EnableAuthorizationServer @Order(200) public class SocialApplication extends WebSecurityConfigurerAdapter { @Autowired OAuth2ClientContext oauth2ClientContext; @RequestMapping({ "/user", "/me" }) public Map<String, String> user(Principal principal) { Map<String, String> map = new LinkedHashMap<>(); map.put("name", principal.getName()); return map; } @Override protected void configure(HttpSecurity http) throws Exception { // @formatter:off http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest() .authenticated().and().exceptionHandling() .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout() .logoutSuccessUrl("/").permitAll().and().csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and() .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class); // @formatter:on } @Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http.antMatcher("/me").authorizeRequests().anyRequest().authenticated(); // @formatter:on } } public static void main(String[] args) { SpringApplication.run(SocialApplication.class, args); } @Bean public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) { FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<OAuth2ClientContextFilter>(); registration.setFilter(filter); registration.setOrder(-100); return registration; } @Bean @ConfigurationProperties("github") public ClientResources github() { return new ClientResources(); } @Bean @ConfigurationProperties("facebook") public ClientResources facebook() { return new ClientResources(); } private Filter ssoFilter() { CompositeFilter filter = new CompositeFilter(); List<Filter> filters = new ArrayList<>(); filters.add(ssoFilter(facebook(), "/login/facebook")); filters.add(ssoFilter(github(), "/login/github")); filter.setFilters(filters); return filter; } private Filter ssoFilter(ClientResources client, String path) { OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter( path); OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext); filter.setRestTemplate(template); UserInfoTokenServices tokenServices = new UserInfoTokenServices( client.getResource().getUserInfoUri(), client.getClient().getClientId()); tokenServices.setRestTemplate(template); filter.setTokenServices(new UserInfoTokenServices( client.getResource().getUserInfoUri(), client.getClient().getClientId())); return filter; } } class ClientResources { @NestedConfigurationProperty private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails(); @NestedConfigurationProperty private ResourceServerProperties resource = new ResourceServerProperties(); public AuthorizationCodeResourceDetails getClient() { return client; } public ResourceServerProperties getResource() { return resource; } }

index.html:

<!doctype html> <html lang="en"> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <title>Demo</title> <meta name="description" content=""/> <meta name="viewport" content="width=device-width"/> <base href="/"/> <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css"/> <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script> <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script> </head> <body> <h1>Login</h1> <div class="container unauthenticated"> With Facebook: <a href="/login/facebook">click here</a> </div> <div class="container authenticated" style="display: none"> Logged in as: <span id="user"></span> <div> <button onClick="logout()" class="btn btn-primary">Logout</button> </div> </div> <script type="text/javascript" src="/webjars/js-cookie/js.cookie.js"></script> <script type="text/javascript"> $.ajaxSetup({ beforeSend: function (xhr, settings) { if (settings.type == ''POST'' || settings.type == ''PUT'' || settings.type == ''DELETE'') { if (!(/^http:.*/.test(settings.url) || /^https:.*/ .test(settings.url))) { // Only send the token to relative URLs i.e. locally. xhr.setRequestHeader("X-XSRF-TOKEN", Cookies.get(''XSRF-TOKEN'')); } } } }); $.get("/user", function (data) { $("#user").html(data.userAuthentication.details.name); $(".unauthenticated").hide(); $(".authenticated").show(); }); var logout = function () { $.post("/logout", function () { $("#user").html(''''); $(".unauthenticated").show(); $(".authenticated").hide(); }); return true; } </script> </body> </html>

application.yml:

server: port: 8080 security: oauth2: client: client-id: acme client-secret: acmesecret scope: read,write auto-approve-scopes: ''.*'' facebook: client: clientId: 233668646673605 clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d accessTokenUri: https://graph.facebook.com/oauth/access_token userAuthorizationUri: https://www.facebook.com/dialog/oauth tokenName: oauth_token authenticationScheme: query clientAuthenticationScheme: form resource: userInfoUri: https://graph.facebook.com/me github: client: clientId: bd1c0a783ccdd1c9b9e4 clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1 accessTokenUri: https://github.com/login/oauth/access_token userAuthorizationUri: https://github.com/login/oauth/authorize clientAuthenticationScheme: form resource: userInfoUri: https://api.github.com/user logging: level: org.springframework.security: DEBUG

Pero cuando abro el navegador y trato de golpear http://localhost:8080

En la consola del navegador veo:

(index):44 Uncaught TypeError: Cannot read property ''details'' of undefined at Object.success ((index):44) at j (jquery.js:3073) at Object.fireWith [as resolveWith] (jquery.js:3185) at x (jquery.js:8251) at XMLHttpRequest.<anonymous> (jquery.js:8598)

en codigo:

$.get("/user", function (data) { $("#user").html(data.userAuthentication.details.name); $(".unauthenticated").hide(); $(".authenticated").show(); });

Ocurre porque /user respuesta del /user con el código de estado 302 y la devolución de llamada js intenta analizar el resultado de localhost:8080 :

No entiendo por qué sucede esta redirección. ¿Puedes explicar este comportamiento y ayudar a solucionarlo?

ACTUALIZAR

Tomé este código de https://github.com/spring-guides/tut-spring-boot-oauth2

importante:

Se reproduce solo después de iniciar la aplicación cliente.

PD

Cómo reproducir:

Para probar las nuevas funciones, puede ejecutar ambas aplicaciones y visitar localhost: 9999 / client en su navegador. La aplicación cliente se redireccionará al Servidor de Autorización local, que luego le da al usuario la opción habitual de autenticación con Facebook o Github. Una vez que se completa, el control vuelve al cliente de prueba, se otorga el token de acceso local y se completa la autenticación (debería ver un mensaje de "Hola" en su navegador). Si ya está autenticado con Github o Facebook, es posible que ni siquiera note la autenticación remota.

RESPONDER:

https://stackoverflow.com/a/50349078/2674303


Finalmente he encontrado el problema. Veo este comportamiento debido al hecho de que las cookies chocan para el cliente y el servidor si inicia ambas aplicaciones en localhost.

Ocurre debido al hecho de la propiedad incorrecta de uso para contexto.

Así que para corregir la aplicación necesitas reemplazar:

server: context-path: /client

con

server: servlet: context-path: /client

PD

He creado el problema en github:

https://github.com/spring-guides/tut-spring-boot-oauth2/issues/80

e hizo la solicitud de extracción:

https://github.com/spring-guides/tut-spring-boot-oauth2/pull/81

PS2

Finalmente, mi solicitud de extracción se fusionó: https://github.com/spring-guides/tut-spring-boot-oauth2/pull/81


Veo dos consultas en tu post.

UNO-

(index):44 Uncaught TypeError: Cannot read property ''details'' of undefined

Esto estaba sucediendo porque quizás estaba ejecutando un proyecto incorrecto (es decir, servidor de autenticación) que tiene un error. El repositorio contiene otros proyectos similares también sin error. Si ejecuta el manual del proyecto o github este error no aparecerá. En estos proyectos, el código javascript está manejando correctamente los datos que devuelve el servidor después de la autenticación.

DOS-

/user Respuesta del /user con código de estado 302:

Para comprender por qué sucede esto, veamos la configuración de seguridad de esta aplicación.

Los puntos finales "/" , "/login**" y "/logout" son accesibles para todos. Todos los demás puntos finales, incluido "/user" requieren autenticación porque ha utilizado

.anyRequest().authenticated().and().exceptionHandling() .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))

Por lo tanto, cualquier solicitud que no esté autenticada se redirigirá al punto de entrada de autenticación, es decir, "/" , solicitando al usuario la autenticación. No depende de si su aplicación cliente se inicia o no. Mientras la solicitud no esté autenticada, será redirigida a "/" . Esta es la razón por la que el controlador Spring responde con el estado 302. Una vez que se haya autenticado con Facebook o github , las solicitudes posteriores al punto final "/user" responderán correctamente con 200.

Y DESPUÉS-

El punto final "/me" en su aplicación está protegido como un recurso seguro con @EnableResourceServer . Dado que ResourceServerConfiguration tiene una prioridad más alta (ordenada 3 por defecto) que WebSecurityConfigurerAdapter (por defecto 100, de todos modos ya ha ordenado explícitamente menor que 3 con @Order anotación en el código), por lo que ResourceServerConfiguration se aplicará para este punto final. Eso significa que si la solicitud no está autenticada, entonces no se redirigirá al punto de entrada de autenticación, sino que devolverá una respuesta 401 . Una vez que esté autenticado, tendrá éxito en la respuesta con 200.

Espero que esto aclare todas sus preguntas.

ACTUALIZACIÓN - para responder a su pregunta

El enlace del repositorio que ha proporcionado en su publicación contiene muchos proyectos. Los proyectos auth-server , manual y github son todos similares (proporcionan la misma funcionalidad, es decir, autenticación con facebook y github). Solo el index.html en auth-server projet tiene un error. Si corrige este error que se reemplaza

$("#user").html(data.userAuthentication.details.name);

con

$("#user").html(data.name);

También funcionará bien. Los tres proyectos darán el mismo resultado.


Actualización: 15-mayo-2018

Como ya descubrió la solución, el problema se produce porque se sobrescribe el JSESSIONID

Actualización: 10-mayo-2018

Bueno, su persistencia con la tercera recompensa finalmente ha dado sus frutos. Comencé a profundizar en lo que era diferente entre los dos ejemplos que tenías en el repositorio.

Si nos fijamos en el repositorio manual y /user mapeo de /user .

@RequestMapping("/user") public Principal user(Principal principal) { return principal; }

Como puede ver, aquí está devolviendo el principal , obtiene más detalles del mismo objeto. Ahora en su código que ejecuta desde la carpeta auth-server

@RequestMapping({ "/user", "/me" }) public Map<String, String> user(Principal principal) { Map<String, String> map = new LinkedHashMap<>(); map.put("name", principal.getName()); return map; }

Como puede ver, solo devolvió el name en la asignación de /user y su lógica de UI se ejecuta a continuación

$.get("/user", function(data) { $("#user").html(data.userAuthentication.details.name); $(".unauthenticated").hide(); $(".authenticated").show(); });

Por lo tanto, la respuesta json devuelta de /user api que se espera que tenga userAuthentication.details.name en la interfaz de usuario no tiene esos detalles. Ahora si actualizo el método como abajo en el mismo proyecto.

@RequestMapping({"/user", "/me"}) public Map<String, Object> user(Principal principal) { Map<String, Object> map = new LinkedHashMap<>(); map.put("name", principal.getName()); OAuth2Authentication user = (OAuth2Authentication) principal; map.put("userAuthentication", new HashMap<String, Object>(){{ put("details", user.getUserAuthentication().getDetails()); }}); return map; }

Y luego verifica la aplicación, funciona.

Respuesta original

Entonces el problema es que estás ejecutando un proyecto incorrecto desde el repositorio. El proyecto que está ejecutando es auth-server que es para iniciar su propio servidor oauth . El proyecto que necesita ejecutar está dentro de manual carpeta manual .

Ahora si nos fijamos en el siguiente código

OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter( "/login/facebook"); OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext); facebookFilter.setRestTemplate(facebookTemplate); UserInfoTokenServices tokenServices = new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()); tokenServices.setRestTemplate(facebookTemplate); facebookFilter.setTokenServices( new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId())); return facebookFilter;

Y el código real que ejecuta tiene

private Filter ssoFilter(ClientResources client, String path) { OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter( path); OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext); filter.setRestTemplate(template); UserInfoTokenServices tokenServices = new UserInfoTokenServices( client.getResource().getUserInfoUri(), client.getClient().getClientId()); tokenServices.setRestTemplate(template); filter.setTokenServices(tokenServices); return filter; }

En tu actual los userdetails del userdetails del facebook no se recogen. Es por eso que ves un error.

Porque cuando inició sesión en el usuario, no recopiló sus detalles de usuario. Entonces cuando accedes a los detalles, no está ahí. Y de ahí te sale un error.

Si ejecuta la carpeta manual correcta, funciona