Put user in HttpSession with Spring Security default login and authenticate

Tags: , , , ,



I precise that I am a french student in 1st year of Java Developper.

I’m developing a little multi-module app using: Spring Boot, Spring security, Hibernate, Spring Data, Spring MVC and Thymeleaf.

I would like to set the User in the session, or at least the userId, at login. This way I don’t have to put it manually in the session or in the model each time I need it.

But as I use the default Spring Security login and authentication configuration, I really don’t know how or where to call such a method:

void putUserInHttpSession( HttpSession httpSession ) {
        httpSession.setAttribute( "user" , getManagerFactory().getUserManager().findByUserName( SecurityContextHolder.getContext().getAuthentication().getName()) );
    }

I can do it eahc time I need it but I find it pretty ugly not to just do this at login in!

Here are what I think you might need to help me (that would be AWESOME !!! 🙂

My WebSecurityConfig class:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private DataSource dataSource;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        // Setting Service to find User in the database.
        // And Setting PassswordEncoder
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

    }


    @Override
    protected void configure( HttpSecurity http ) throws Exception {

        http.csrf().disable();


        // /userInfo page requires login as ROLE_USER or ROLE_ADMIN.
        // If no login, it will redirect to /login page.
        http.authorizeRequests().antMatchers(
                "/user/**")
                .access("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')");

        // For ADMIN only.
        http.authorizeRequests().antMatchers(
                "/admin/**")
                .access("hasRole('ROLE_ADMIN')");

        // When the user has logged in as XX.
        // But access a page that requires role YY,
        // AccessDeniedException will be thrown.
        http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/public/403");

        // Config for Login Form
        http.authorizeRequests().and().formLogin()//
                // Submit URL of login page.
                .loginProcessingUrl("/j_spring_security_check") // Submit URL
                .loginPage("/public/login").defaultSuccessUrl("/public/showAtlas")//
                .failureUrl("/public/login?error=true")//
                .usernameParameter("username")//
                .passwordParameter("password")
                //Config for Logout Page
                .and()
                .logout().logoutUrl("/public/logout").logoutSuccessUrl("/public/logoutSuccessful");

        http.authorizeRequests().antMatchers(
                "/public/**").permitAll();
        // The pages does not require login
    }

}

My UserDetailsServiceImpl class:

@Service
public class UserDetailsServiceImpl implements UserDetailsService{

    @Autowired
    private ManagerFactory managerFactory;

//  private HttpSession httpSession;

    /**
     * The authentication method uses the user email, since it is easier to remember for most users
     * @param input
     * @return a UserDetails object
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername( String input) throws UsernameNotFoundException {

        User user = new User();

        if( input.contains( "@" )){
            user =  this.managerFactory.getUserManager().findByEmail( input );
        }
        else {
            user =  this.managerFactory.getUserManager().findByUserName( input );
        }


        if (user == null) {
            throw new UsernameNotFoundException( "User with email " + input + " was not found in the database" );
        }

        // [ROLE_USER, ROLE_ADMIN,..]
        List<String> roleNames = this.managerFactory.getRoleManager().findRoleByUserName(user.getUserName());

        List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
        if (roleNames != null) {
            for (String role : roleNames) {
                // ROLE_USER, ROLE_ADMIN,..
                GrantedAuthority authority = new SimpleGrantedAuthority(role);
                grantList.add(authority);
            }
        }

        return (UserDetails) new org.springframework.security.core.userdetails.User(user.getUserName(),
                user.getPassword(), grantList);
    }
}

My simple LoginController:

@Controller
public class LoginController{

    @GetMapping("/public/login")
    public String login(Model model ){


        return "view/login";
    }

    @GetMapping("/public/logoutSuccessful")
    public String logout(Model model) {

        return "view/logoutSuccessful";

    }

So, is there a simple way to put the user or userId in the httpSession at login?

Thank you very much guys!!!

THE SOLUTION

Create a CustomAuthenticationSuccessHandler

@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private ManagerFactory managerFactory;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request,
                                        HttpServletResponse response,
                                        Authentication authentication)
            throws IOException, ServletException {

        String userName = "";
        HttpSession session = request.getSession();
        Collection< GrantedAuthority > authorities = null;
        if(authentication.getPrincipal() instanceof Principal ) {
            userName = ((Principal)authentication.getPrincipal()).getName();
            session.setAttribute("role", "none");
        }else {
            User userSpringSecu = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            session.setAttribute("role", String.valueOf( userSpringSecu.getAuthorities()));
            session.setAttribute( "connectedUser" , managerFactory.getUserManager().findByUserName( userSpringSecu.getUsername() ) );
        }
        response.sendRedirect("/public/showAtlas" );
    }
}

Then Autowired this class and add it in the WebSecurityConfigurerAdapter

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Autowired
    private DataSource dataSource;


    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        // Setting Service to find User in the database.
        // And Setting PassswordEncoder
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());

    }


    @Override
    protected void configure( HttpSecurity http ) throws Exception {

        http.csrf().disable();


        // /userInfo page requires login as ROLE_USER or ROLE_ADMIN.
        // If no login, it will redirect to /login page.
        http.authorizeRequests().antMatchers(
                "/user/**")
                .access("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')");

        // For ADMIN only.
        http.authorizeRequests().antMatchers(
                "/admin/**")
                .access("hasRole('ROLE_ADMIN')");
//      http.exceptionHandling().accessDeniedPage( "/error/403" );

        // When the user has logged in as XX.
        // But access a page that requires role YY,
        // AccessDeniedException will be thrown.
        http.authorizeRequests().and().exceptionHandling().accessDeniedPage("/public/403");

        // Config for Login Form
        http.authorizeRequests().and().formLogin()//
                // Submit URL of login page.
                .loginProcessingUrl("/j_spring_security_check") // Submit URL
                .loginPage("/public/login")
                .defaultSuccessUrl("/public/showAtlas")//
                .successHandler( customAuthenticationSuccessHandler )
                .failureUrl("/public/login?error=true")//
                .usernameParameter("username")//
                .passwordParameter("password")
                //Config for Logout Page
                .and()
                .logout().logoutUrl("/public/logout").logoutSuccessUrl("/public/logoutSuccessful");

        http.authorizeRequests().antMatchers(
                "/public/**").permitAll();
        // The pages does not require login
    }

}

Answer

Assuming you wanted to add user to session on seccessful login, You can create the AuthenticationSuccessHandler like below and register using successHandler(new AuthenticationSuccessHandlerImpl())

Update: If we create the object AuthenticationSuccessHandlerImpl, it will not be spring mananged and hence autowire into your Securityconfig and use it like shown below.

Here autowire the AuthenticationSuccessHandler in your WebSecurityConfig

@Autowired
AuthenticationSuccessHandler authenticationSuccessHandler;

and use it WebSecurityConfig.java

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
                .antMatchers("/resources/**", "/registration").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll().successHandler(authenticationSuccessHandler) // See here
                .and()
            .logout()
                .permitAll();
}

The AuthenticationSuccessHandlerImpl.java

import java.io.IOException;
import java.security.Principal;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import com.techdisqus.auth.repository.UserRepository;

@Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler{

    @Autowired HttpSession session; //autowiring session

    @Autowired UserRepository repository; //autowire the user repo


    private static final Logger logger = LoggerFactory.getLogger(AuthenticationSuccessHandlerImpl.class);
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        // TODO Auto-generated method stub
        String userName = "";
        if(authentication.getPrincipal() instanceof Principal) {
             userName = ((Principal)authentication.getPrincipal()).getName();

        }else {
            userName = ((User)authentication.getPrincipal()).getUsername();
        }
        logger.info("userName: " + userName);
        //HttpSession session = request.getSession();
        session.setAttribute("userId", userName);

    }

}

Hope this helps.



Source: stackoverflow