How to manually install an authenticated user in Spring Security / SpringMVC

After the new user submits the "New Account" form, I want to manually register this user so that he can not enter the next page.

The login page of the regular form passing through the spring security interceptor is working fine.

In the new-account-form controller, I create a UserPasswordAuthenticationToken and manually set it to SecurityContext:

SecurityContextHolder.getContext().setAuthentication(authentication); 

On the same page, I will later verify that the user is logged in with:

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

This returns the credentials that I set earlier in authentication. All right.

But when the same code is called on the very next page that I load, the authentication token is just UserAnonymous.

I do not understand why he did not save the authentication set in the previous request. Any thoughts?

  • Can this be done if the session ID is not configured correctly?
  • Is there something that can somehow rewrite my authentication?
  • Maybe I need one more step to save authentication?
  • Or is there something I need to do to declare authentication throughout the session, and not on any request?

Just find some thoughts that can help me see what is happening here.

+85
java authentication spring-mvc spring-security
Jan 12 '11 at 2:44
source share
6 answers

I had the same problem as you. I don’t remember the details, but the following code made me work. This code is used in the Spring Webflow stream, hence the RequestContext and ExternalContext classes. But the most important part for you is the doAutoLogin method.

 public String registerUser(UserRegistrationFormBean userRegistrationFormBean, RequestContext requestContext, ExternalContext externalContext) { try { Locale userLocale = requestContext.getExternalContext().getLocale(); this.userService.createNewUser(userRegistrationFormBean, userLocale, Constants.SYSTEM_USER_ID); String emailAddress = userRegistrationFormBean.getChooseEmailAddressFormBean().getEmailAddress(); String 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(String username, String 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); } } 
+58
Jan 12 '11 at 17:48
source share

I could not find any other complete solutions, so I thought that I would send mine. This may be a bit hacky, but it solved the problem for the above problem:

 public void login(HttpServletRequest request, String userName, String 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.setAttribute("SPRING_SECURITY_CONTEXT", securityContext); } 
+53
Dec 01
source share

Ultimately figured out the root of the problem.

When I create a security context manually, a session object is not created. Only when processing completes processing does Spring's security mechanism realize that the session object is null (when it tries to maintain a security context in the session after processing the request).

At the end of the Spring request, Security creates a new session object and session ID. However, this new session identifier never hits the browser, as it occurs at the end of the request after a response has been made to the browser. This results in the loss of the new session identifier (and therefore the security context that contains my user manually) when the next request contains the previous session identifier.

+15
Jan 13 2018-11-11T00:
source share

Turn on debug logging to get a better idea of ​​what's going on.

You can find out if session cookies are set using the browser-side debugger to view the headers returned in HTTP responses. (There are other ways.)

One possibility is that SpringSecurity sets up secure session cookies, and your next page has an “http” URL instead of an “https” URL. (The browser will not send a secure cookie for the URL "http".)

+5
Jan 12 '11 at 3:10
source share

The new filtering feature in Servlet 2.4 basically eliminates the restriction that filters can only use in the request flow before and after the actual processing of the actual request by the application server. Instead, Servlet 2.4 filters can now interact with the query manager at each send point. This means that when a web resource redirects a request to another resource (for example, a servlet redirecting a request to a JSP page in the same application), the filter can work before the request is processed by the target resource. This also means that if a web resource includes output or a function from other web resources (for example, a JSP page, including output from several other JSP pages), Servlet 2.4 filters can work before and after each of the included resources.,

To enable this feature, you need to:

web.xml

  <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/<strike>*</strike></url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> 

RegistrationController

  return "forward:/login?j_username=" + registrationModel.getUserEmail() + "&j_password=" + registrationModel.getPassword(); 
+4
Nov 01 '11 at
source share

I tried to test the extjs application, and after successfully installing testAuthenticationToken, it suddenly stopped working for no apparent reason.

I could not get the above answers to work, so my solution was to skip this spring bit in a test environment. I introduced a seam around spring as follows:

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

User is a custom type.

Then I wrap it in a class that has only an option for test code to switch spring.

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

The test version is as follows:

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

In the calling code, I still use the correct user loaded from the database:

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

Obviously, this is not suitable if you really need to use security, but I run the installation without protection to deploy testing. I thought someone else would face a similar situation. This is the pattern I used to mock static dependencies. Another alternative is that you can keep the wrapper class static, but I prefer this because the code dependencies are more explicit, since you need to pass CurrentUserAccessor to the classes where it is needed.

+1
Jun 19 '14 at 11:17
source share



All Articles