Comment définir manuellement un utilisateur authentifié dans Spring Security / SpringMVC

Lorsqu’un nouvel utilisateur soumet un formulaire “Nouveau compte”, je souhaite le connecter manuellement afin de ne pas avoir à se connecter à la page suivante.

La page de connexion de formulaire normale passant par l’intercepteur de sécurité Spring fonctionne très bien.

Dans le contrôleur de formulaire de nouveau compte, je crée un nom d’utilisateurPasswordAuthenticationToken et le configure manuellement dans SecurityContext:

SecurityContextHolder.getContext().setAuthentication(authentication); 

Sur cette même page, je vérifie plus tard que l’utilisateur est connecté avec:

 SecurityContextHolder.getContext().getAuthentication().getAuthorities(); 

Cela renvoie les permissions que j’ai définies plus tôt dans l’authentification. Tout est bien.

Mais lorsque ce même code est appelé à la page suivante que je charge, le jeton d’authentification est simplement UserAnonymous.

Je ne sais pas pourquoi il n’a pas conservé l’authentification que j’ai définie sur la demande précédente. Des pensées?

  • Pourrait-il avoir à faire avec les identifiants de session ne sont pas configurés correctement?
  • Y a-t-il quelque chose qui écrase mon authentification?
  • Peut-être ai-je juste besoin d’une autre étape pour enregistrer l’authentification?
  • Ou est-ce qu’il y a quelque chose que je dois faire pour déclarer l’authentification sur toute la session plutôt qu’une seule demande?

Je cherche juste des idées qui pourraient m’aider à voir ce qui se passe ici.

J’ai eu le même problème que toi il y a quelque temps. Je ne me souviens plus des détails, mais le code suivant a fonctionné pour moi. Ce code est utilisé dans un stream Spring Webflow, donc les classes RequestContext et ExternalContext. Mais la partie la plus pertinente pour vous est la méthode doAutoLogin.

 public Ssortingng registerUser(UserRegistrationFormBean userRegistrationFormBean, RequestContext requestContext, ExternalContext externalContext) { try { Locale userLocale = requestContext.getExternalContext().getLocale(); this.userService.createNewUser(userRegistrationFormBean, userLocale, Constants.SYSTEM_USER_ID); Ssortingng emailAddress = userRegistrationFormBean.getChooseEmailAddressFormBean().getEmailAddress(); Ssortingng password = userRegistrationFormBean.getChoosePasswordFormBean().getPassword(); doAutoLogin(emailAddress, password, (HttpServletRequest) externalContext.getNativeRequest()); return "success"; } catch (EmailAddressNotUniqueException e) { MessageResolver messageResolvable = new MessageBuilder().error() .source(UserRegistrationFormBean.PROPERTYNAME_EMAIL_ADDRESS) .code("userRegistration.emailAddress.not.unique") .build(); requestContext.getMessageContext().addMessage(messageResolvable); return "error"; } } private void doAutoLogin(Ssortingng username, Ssortingng password, HttpServletRequest request) { try { // Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); token.setDetails(new WebAuthenticationDetails(request)); Authentication authentication = this.authenticationProvider.authenticate(token); logger.debug("Logging in with [{}]", authentication.getPrincipal()); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (Exception e) { SecurityContextHolder.getContext().setAuthentication(null); logger.error("Failure in autoLogin", e); } } 

Je n’ai pas pu trouver d’autres solutions complètes alors j’ai pensé poster le mien. Cela peut être un peu un piratage, mais il a résolu le problème du problème ci-dessus:

 public void login(HttpServletRequest request, Ssortingng userName, Ssortingng password) { UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userName, password); // Authenticate the user Authentication authentication = authenticationManager.authenticate(authRequest); SecurityContext securityContext = SecurityContextHolder.getContext(); securityContext.setAuthentication(authentication); // Create a new session and add the security context. HttpSession session = request.getSession(true); session.setAtsortingbute("SPRING_SECURITY_CONTEXT", securityContext); } 

Finalement, a compris la racine du problème.

Lorsque je crée le contexte de sécurité manuellement, aucun object de session n’est créé. Ce n’est que lorsque la requête a terminé le traitement que le mécanisme de sécurité Spring réalise que l’object de session est null (lorsqu’il tente de stocker le contexte de sécurité dans la session après le traitement de la demande).

À la fin de la requête, Spring Security crée un nouvel object de session et un nouvel identifiant de session. Cependant, ce nouvel identifiant de session ne parvient jamais au navigateur car il se produit à la fin de la demande, une fois la réponse au navigateur établie. Cela entraîne la perte du nouvel identifiant de session (et donc du contexte de sécurité contenant mon utilisateur connecté manuellement) lorsque la demande suivante contient l’ID de session précédent.

Activez la journalisation du débogage pour obtenir une meilleure image de ce qui se passe.

Vous pouvez savoir si les cookies de session sont définis en utilisant un débogueur côté navigateur pour examiner les en-têtes renvoyés dans les réponses HTTP. (Il y a d’autres façons aussi.)

Une possibilité est que SpringSecurity définisse des cookies de session sécurisés et que votre prochaine page demandée ait une URL “http” au lieu d’une URL “https”. (Le navigateur n’envoie pas de cookie sécurisé pour une URL “http”.)

La nouvelle fonctionnalité de filtrage de Servlet 2.4 permet de limiter la ressortingction selon laquelle les filtres ne peuvent fonctionner que dans le stream de requêtes avant et après le traitement de la demande par le serveur d’applications. Au lieu de cela, les filtres Servlet 2.4 peuvent désormais interagir avec le répartiteur de requêtes à chaque point d’expédition. Cela signifie que lorsqu’une ressource Web transfère une demande à une autre ressource (par exemple, une servlet transmettant la demande à une page JSP dans la même application), un filtre peut être utilisé avant que la requête ne soit traitée par la ressource ciblée. Cela signifie également que si une ressource Web inclut la sortie ou la fonction d’autres ressources Web (par exemple, une page JSP contenant la sortie de plusieurs autres pages JSP), les filtres Servlet 2.4 peuvent fonctionner avant et après chacune des ressources incluses. .

Pour activer cette fonctionnalité, vous avez besoin de:

web.xml

  springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy   springSecurityFilterChain /* REQUEST FORWARD  

RegistrationController

 return "forward:/login?j_username=" + registrationModel.getUserEmail() + "&j_password=" + registrationModel.getPassword(); 

J’essayais de tester une application extjs et après avoir configuré avec succès testingAuthenticationToken, cela a soudainement cessé de fonctionner sans cause évidente.

Je ne pouvais pas obtenir les réponses ci-dessus pour travailler, donc ma solution était de sauter ce bout de spring dans l’environnement de test. J’ai introduit une couture autour du spring comme ceci:

 public class SpringUserAccessor implements UserAccessor { @Override public User getUser() { SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); return (User) authentication.getPrincipal(); } } 

L’utilisateur est un type personnalisé ici.

Je l’enroule ensuite dans une classe qui a simplement une option pour que le code de test permette de faire un saut.

 public class CurrentUserAccessor { private static UserAccessor _accessor; public CurrentUserAccessor() { _accessor = new SpringUserAccessor(); } public User getUser() { return _accessor.getUser(); } public static void UseTestingAccessor(User user) { _accessor = new TestUserAccessor(user); } } 

La version de test ressemble à ceci:

 public class TestUserAccessor implements UserAccessor { private static User _user; public TestUserAccessor(User user) { _user = user; } @Override public User getUser() { return _user; } } 

Dans le code d’appel, j’utilise toujours un utilisateur approprié chargé depuis la firebase database:

  User user = (User) _userService.loadUserByUsername(username); CurrentUserAccessor.UseTestingAccessor(user); 

Évidemment, cela ne conviendra pas si vous avez réellement besoin d’utiliser la sécurité, mais je suis en train d’exécuter une configuration sans sécurité pour le déploiement du test. Je pensais que quelqu’un d’autre pourrait se retrouver dans une situation similaire. Ceci est un modèle que j’ai utilisé pour se moquer des dépendances statiques auparavant. L’autre alternative est que vous pouvez conserver la statique de la classe wrapper mais je préfère celle-ci car les dépendances du code sont plus explicites puisque vous devez passer CurrentUserAccessor dans les classes où il est requirejs.