72

The opposite of: How to manually log out a user with spring security?

In my app I have register new user screen, which posts to a controller which creates a new user within db (and does a few obvious checks).I then want this new user to be automatically logged in ... I kind of want somethign like this :

SecurityContextHolder.getContext().setPrincipal(MyNewUser);

Edit Well I have almost implemented based on the answer to How to programmatically log user in with Spring Security 3.1

 Authentication auth = new UsernamePasswordAuthenticationToken(MyNewUser, null);
 SecurityContextHolder.getContext().setPrincipal(MyNewUser);

However, when deployed the jsp can not access my MyNewUser.getWhateverMethods() whereas it does when normal login procedure followed. the code that works nomrally, but throws an error when logged in like above is below :

<sec:authentication property="principal.firstname" /> 
Gonen
  • 4,005
  • 1
  • 31
  • 39
NimChimpsky
  • 46,453
  • 60
  • 198
  • 311

4 Answers4

51

In my controller i have this, which logs user in as normal :

Authentication auth = 
  new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(auth);

Where user is my custom user object(implementing UserDetails) that is newly created. The getAuthorities() method does this (just because all my users have the same role):

public Collection<GrantedAuthority> getAuthorities() {
        //make everyone ROLE_USER
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
        GrantedAuthority grantedAuthority = new GrantedAuthority() {
            //anonymous inner type
            public String getAuthority() {
                return "ROLE_USER";
            }
        }; 
        grantedAuthorities.add(grantedAuthority);
        return grantedAuthorities;
    }
Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
  • 9
    That is not a login, that is "placing a user object into security context without authenticating it" – dube Apr 20 '12 at 10:41
  • 16
    You don't need to authenticate in the question's described scenario. The user just registered. That was the "authentication". It's just that you need to programmatically add the user to the context so Spring knows the user is authenticated. –  Jun 06 '12 at 04:04
  • 2
    Adding a GrantedAuthority as a third arg to UsernamePasswordAuthenticationToken was what I needed – sdoxsee Aug 15 '16 at 18:11
  • @dube I see your point. Would a combination of the accepted answer and Simeon's answer satisfy a real login? – LukeSolar Aug 07 '19 at 08:18
  • @LukeSolar Simeon's answer is basically the same, just less obvious. It does not call the normal chain of actions, neglecting listeners and possibly creates a weird context. The better but more complicated approach would be to call the `AuthenticationManager` or `ProviderManager` in combination with a PreAuth token, e.g. `PreAuthenticatedAuthenticationToken` to generate an `Authentication` object before setting it in the context. – dube Aug 08 '19 at 12:44
  • I tried your method but SecurityUtils.getCurrentUserLogin() still prints anonymous@localhost. What might be a reason for that? – lightning mcqueen Sep 19 '20 at 18:06
33

You can also inject your spring security configured UserDetailsManager to your controller and use that to get the UserDetails which holds the principal and authorities to avoid duplicate code:

// inject

@Autowired
private UserDetailsManager manager; 

// use in your method

UserDetails userDetails = manager.loadUserByUsername (token.getUsername ());
Authentication auth = new UsernamePasswordAuthenticationToken (userDetails.getUsername (),userDetails.getPassword (),userDetails.getAuthorities ());
SecurityContextHolder.getContext().setAuthentication(auth);
okrunner
  • 3,083
  • 29
  • 22
  • 1
    I did not have the UserDatailsManager but the org.springframework.security.core.userdetails.UserDetailsService works too. – tibi Oct 02 '19 at 18:51
11

From the spring security source AbstractAuthenticationProcessingFilter:

protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
        Authentication authResult) throws IOException, ServletException {

    if (logger.isDebugEnabled()) {
        logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
    }

    // you need this
    SecurityContextHolder.getContext().setAuthentication(authResult);

    rememberMeServices.loginSuccess(request, response, authResult);

    if (this.eventPublisher != null) {
        eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
    }

    successHandler.onAuthenticationSuccess(request, response, authResult);
}

Note however that the SecurityContextHolder is usually cleared upon completion of the filter chain.

Simeon
  • 7,582
  • 15
  • 64
  • 101
  • 1
    I don't understand, where is authResult instantiated ? – NimChimpsky Oct 26 '11 at 13:29
  • @NimChimpsky You have to instantiate it, as in you have to have your implementation of it and then fill it with data relevant to your user. Have a look at the interface. – Simeon Oct 26 '11 at 13:42
0

For anyone trying to do this with Reactive Spring Security, this is what I did and it seemed to work.

private Mono<Authentication> authenticateUser(ServerWebExchange exchange, UserDetails userDetails,String rawPassword) 
    {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails.getUsername(),rawPassword);
        
        return reactiveAuthenticationManager.authenticate(token).filter(auth -> auth.isAuthenticated()).flatMap(auth ->
        {
            SecurityContextImpl securityContext = new SecurityContextImpl();
            securityContext.setAuthentication(auth);
            return securityContextRepository.save(exchange,securityContext).then(Mono.just(auth));
        });
    }
Shy Albatross
  • 103
  • 11