You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义登录页结合UserDetailsService登录失效及控制器返回值疑问

问题排查与解决方案:自定义Spring Security登录表单不触发UserDetailsService

我来帮你梳理下问题所在,以及对应的解决办法:

一、为什么UserDetailsServiceImp没有被调用?

核心原因是你的登录请求没有进入Spring Security的认证流程:

  • 你自定义的表单提交到了/validate路径,但你自己写了一个处理该路径的控制器方法,导致请求被Spring MVC直接处理,完全跳过了Spring Security的认证逻辑,自然不会调用UserDetailsServiceImp的loadUserByUsername方法。
  • 你的WebSecurityConfig配置中,没有指定Spring Security要处理的登录请求路径(默认是/login),所以它不会拦截/validate的请求。

解决步骤:

  1. 修改Spring Security配置,让它接管/validate的登录请求
    在configure(HttpSecurity http)方法的formLogin部分,添加loginProcessingUrl("/validate"),告诉Spring Security这个路径是用来处理登录提交的:

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/") // 自定义登录页面路径
                .loginProcessingUrl("/validate") // 关键:指定表单提交的处理路径,由Spring Security处理
                .usernameParameter("username")
                .passwordParameter("password")
                .defaultSuccessUrl("/welcome")
                .failureUrl("/?error=true") // 认证失败后跳转回登录页面,带错误标识
                .permitAll()
            .and()
            .logout()
                .logoutUrl("/logOut")
                .permitAll();
    }
    
  2. 删除你自己写的/validate控制器方法
    这个请求不需要你手动处理,Spring Security会自动完成认证逻辑:成功就跳转到/welcome,失败就跳回登录页面并携带错误信息。

  3. 修复UserDetailsServiceImp中的密码处理问题
    你当前在loadUserByUsername中每次都对用户密码重新加密,这会导致密码比对失败(Spring Security会对前端传的密码再加密一次,相当于二次加密)。修改如下:

    @Component
    public class UserDetailsServiceImp implements UserDetailsService {
        private static Logger logger = LogManager.getLogger(UserDetailsServiceImp.class); // 这里要改成当前类,不是AuthService
        @Autowired
        EntityManager em;
        @Autowired
        private BCryptPasswordEncoder passwordEncoder;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = findUserbyUername(username);
            System.out.println("Called service");
            UserBuilder builder = null;
            if (user != null) {
                builder = org.springframework.security.core.userdetails.User.withUsername(username);
                // 直接使用用户的加密密码,不要再次加密
                builder.password(user.getPassword());
                builder.roles(user.getRoles());
            } else {
                throw new UsernameNotFoundException("User not found.");
            }
            return builder.build();
        }
    
        private User findUserbyUername(String username) {
            if (username.equalsIgnoreCase("admin")) {
                // 提前对明文密码加密,这里直接返回加密后的结果
                String encryptedPassword = passwordEncoder.encode("admin123");
                return new User(username, encryptedPassword, "ADMIN");
            }
            return null;
        }
    }
    

二、关于/validate路径的控制器返回值

结论是:你不需要自己写这个控制器。
Spring Security会自动处理/validate的POST请求:

  • 认证成功:自动跳转到你配置的defaultSuccessUrl("/welcome")
  • 认证失败:自动跳转到登录页面(你配置的loginPage("/")),并可以通过${SPRING_SECURITY_LAST_EXCEPTION.message}获取错误信息

你可以在登录JSP中添加错误信息展示:

<c:if test="${param.error != null}">
    <div class="alert alert-danger" id="message">
        <strong>Error!</strong> ${SPRING_SECURITY_LAST_EXCEPTION.message}
    </div>
</c:if>

内容的提问来源于stack exchange,提问作者amitabh pandey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:56:58