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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 21:00:50