使用 Spring 安全默认登录和身份验证将用户放入 HttpSession

Put user in HttpSession with Spring Security default login and authenticate

我明确表示我是 Java 开发人员一年级的法国学生。

我正在开发一个小型多模块应用程序,使用:Spring 引导、Spring 安全、Hibernate、Spring 数据、Spring MVC 和 Thymeleaf。

我想在会话中设置用户,或者至少在登录时设置用户 ID。这样我就不必每次需要时都手动将其放入会话或模型中。

但是由于我使用默认的Spring安全登录和身份验证配置,我真的不知道如何或在哪里调用这样的方法:

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

我每次需要时都能做到,但我发现不在登录时这样做非常丑陋!

以下是我认为您可能需要帮助我的东西(那太棒了!!!:)

我的 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
    }

}

我的 UserDetailsS​​erviceImpl 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);
    }
}

我的简单登录控制器:

@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";

    }

那么,有没有一种简单的方法可以在登录时将用户或 userId 放入 httpSession 中?

非常感谢你们!!!

解决方案

创建 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" );
    }
}

然后自动装配这个 class 并将其添加到 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
    }

}

假设您想在成功登录时将用户添加到会话中,您可以创建如下所示的 AuthenticationSuccessHandler 并使用 successHandler(new AuthenticationSuccessHandlerImpl())

进行注册

更新: 如果我们创建对象 AuthenticationSuccessHandlerImpl,它将不会被 spring 管理,因此 autowire 进入您的 Securityconfig 并如下所示使用它。

此处自动装配 WebSecurityConfig

中的 AuthenticationSuccessHandler
@Autowired
AuthenticationSuccessHandler authenticationSuccessHandler;

并使用它 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();
}

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

    }

}

希望这对您有所帮助。

我补充一下以上两种方案。我的经验表明以下语句在异常下启动:

session.setAttribute("userId", userName);

异常:

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread?

研究https://html.developreference.com/article/22570246/后,我能够将其删除。也就是说,我已经覆盖了 class 中的 onStartup 方法,它扩展了 AbstractAnnotationConfigDispatcherServletInitializer class.

@Override
public void onStartup(ServletContext servletContext) 
        throws ServletException {
    super.onStartup(servletContext);
    servletContext.addListener(new RequestContextListener());
}