Spring Boot项目登录失败,凭证验证返回无效提示问题咨询
登录失败问题修复
首先排查当前认证不通过的核心问题,按优先级修改:
- 修复
Policy单例并发异常
你通过@Autowired注入的Policy默认是单例全局对象,每次请求调用policy.getPredicates().add()都会往同一个对象里累加谓词规则,多次请求后规则会重复混乱,必然导致认证失败。同时多线程场景下不同用户的请求会互相修改Policy参数,也会触发认证错误。
修改方案:认证时使用局部Policy实例,不要复用全局注入的对象:
@PostMapping("/authenticate") public RedirectView authenticate(@Valid @ModelAttribute LoginRequest loginRequest, BindingResult bindingResult, Model model, HttpServletRequest request) throws AuthenticationFailedException, TokenGenerationException { if (bindingResult.hasErrors()) { // 校验用户名密码是否为空,直接返回登录失败 return new RedirectView("/loginFailed", true); } try { // 新建局部Policy实例,避免多线程并发问题 Policy localPolicy = new Policy(); localPolicy.setPolicyId((String) request.getSession().getAttribute("nonce")); localPolicy.getPredicates().add(new Predicate("audience", Operation.REVEAL, new Attribute("olympus-service-provider"))); String token = userClient.authenticate(loginRequest.getUsername(), loginRequest.getPassword(), localPolicy, null, "NONE"); // 认证成功后存储登录态到session request.getSession().setAttribute("loginUser", loginRequest.getUsername()); // 解析token的过期时间存入session,后续校验用 request.getSession().setAttribute("tokenExpire", parseExpireTimeFromToken(token)); String redirectUrl = (String) request.getSession().getAttribute("redirectUrl"); String state = (String) request.getSession().getAttribute("state"); return new RedirectView(redirectUrl + "#state=" + state + "&id_token=" + token + "&token_type=bearer"); } catch (Exception e) { e.printStackTrace(); if (ExceptionUtils.indexOfThrowable(e, AuthenticationFailedException.class) != -1) { return new RedirectView("/loginFailed", true); } else { throw e; } } finally { // 确认UserClient是请求作用域Bean再保留该逻辑,否则会清空所有用户的会话 // userClient.clearSession(); } }
- 补充请求参数绑定注解
给LoginRequest参数加@ModelAttribute注解,确保表单提交的账号密码能正确绑定到对象,避免参数为空导致的认证失败。
需求实现
1. 访问站点先完成身份校验
通过Spring Boot拦截器实现全局登录校验:
首先编写登录拦截器:
@Component public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String uri = request.getRequestURI(); // 放行登录相关的公开接口 if (uri.startsWith("/login") || uri.startsWith("/authenticate") || uri.startsWith("/loginPage") || uri.startsWith("/loginFailed")) { return true; } // 校验session中是否存在有效登录态 Object loginUser = request.getSession().getAttribute("loginUser"); Long tokenExpire = (Long) request.getSession().getAttribute("tokenExpire"); if (loginUser == null || System.currentTimeMillis() > tokenExpire) { // 未登录/身份过期,跳转到登录页 response.sendRedirect("/loginPage"); return false; } return true; } }
注册拦截器到Spring容器:
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private AuthInterceptor authInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor).addPathPatterns("/**"); } }
2. 身份失效触发登出逻辑
新增登出接口,清理会话状态:
@RequestMapping("/logout") public RedirectView logout(HttpServletRequest request) { // 销毁当前用户的session request.getSession().invalidate(); // 清理用户在Olympus客户端的会话 userClient.clearSession(); // 跳转到登录页 return new RedirectView("/loginPage?logout=success"); }
拦截器已经内置了token过期自动触发登出的逻辑,也可以在前端添加定时器定时校验token有效性,过期主动调用/logout接口完成登出。
内容的提问来源于stack exchange,提问作者user14485278
相关产品推荐
相关产品推荐

