Spring Boot Security:登录错误返回JSON而非HTML页面的问题
解决Spring Boot Security登录凭证错误在HTML页面显示问题
问题分析
你当前的AuthenticationEntryPoint会在认证失败时统一返回JSON,但使用HTML表单登录时,这个逻辑会让页面直接拿到JSON而非回到登录页展示错误。同时,控制器中的authenticationManager.authenticate()方法抛出的AuthenticationException未被捕获,直接被Spring Security的异常处理机制接管,触发了返回JSON的流程。
解决方案1:在登录控制器中捕获认证异常
修改登录控制器,直接捕获AuthenticationException,将错误信息存入Model后返回登录页面:
@PostMapping("/login") @Transactional public String login(@Valid @ModelAttribute("login") LoginRequest loginRequest, BindingResult result, HttpServletResponse response, Model model) { // 先处理字段验证错误 if (result.hasErrors()) { model.addAttribute("login", loginRequest); return "login_form"; } try { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()) ); SecurityContextHolder.getContext().setAuthentication(authentication); UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal(); ResponseCookie jwtCookie = jwtUtils.generateJwtCookie(user); response.addHeader(HttpHeaders.SET_COOKIE, jwtCookie.toString()); return "redirect:/api/test/homePage"; } catch (AuthenticationException e) { // 捕获认证错误,存入Model model.addAttribute("login", loginRequest); model.addAttribute("errorMessage", "登录凭证错误:" + e.getMessage()); return "login_form"; } }
在你的login_form.html页面中添加错误信息展示(以Thymeleaf为例):
<div th:if="${errorMessage}" class="alert alert-danger"> <span th:text="${errorMessage}"></span> </div>
解决方案2:区分请求类型,动态处理认证失败
如果需要同时支持API(返回JSON)和HTML表单登录(返回页面),可以修改AuthenticationEntryPoint的逻辑,根据请求的Accept头判断返回方式:
@Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { logger.error("Unauthorized error: {}", authException.getMessage()); // 判断是否为HTML请求 String acceptHeader = request.getHeader("Accept"); boolean isHtmlRequest = acceptHeader != null && acceptHeader.contains("text/html"); if (isHtmlRequest) { // 重定向到登录页面并携带错误参数 response.sendRedirect("/login?error=" + URLEncoder.encode(authException.getMessage(), StandardCharsets.UTF_8)); } else { // 返回JSON错误 response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); final Map<String, Object> body = new HashMap<>(); body.put("status", HttpServletResponse.SC_UNAUTHORIZED); body.put("error", "Unauthorized"); body.put("message", authException.getMessage()); body.put("path", request.getServletPath()); final ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(response.getOutputStream(), body); } }
然后修改登录控制器,接收错误参数并放入Model:
@GetMapping("/login") public String showLoginPage(@RequestParam(value = "error", required = false) String error, Model model) { if (error != null) { model.addAttribute("errorMessage", "登录凭证错误:" + URLDecoder.decode(error, StandardCharsets.UTF_8)); } model.addAttribute("login", new LoginRequest()); return "login_form"; }
同样在login_form.html中添加错误展示代码即可。
注意事项
- 确保登录表单提交路径与控制器的
@PostMapping("/login")一致,避免路径不匹配导致异常处理逻辑混乱。 - 使用重定向方式时,记得对错误信息进行URL编码和解码,避免特殊字符引发问题。
内容的提问来源于stack exchange,提问作者asu
相关产品推荐
相关产品推荐

