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

使用Spring Security FormLogin时遭遇HttpRequestMethodNotSupportedException问题

问题原因分析
  1. Spring Security 拦截优先级问题:你配置的formLogin().loginProcessingUrl("/user/login")会让Spring Security的过滤器链优先接管该端点的POST请求,你自己编写的@PostMapping("/user/login")控制器方法根本不会被执行。
  2. 请求格式不匹配:Spring Security的formLogin默认期望接收x-www-form-urlencoded格式的表单数据,但你的Angular代码发送的是JSON格式请求,导致Security无法解析用户名密码,认证失败后触发默认重定向到/login?error(你未配置该端点,进而引发异常)。
  3. 无状态配置与formLogin冲突:你设置了SessionCreationPolicy.STATELESS(无状态),但formLogin是基于session的认证机制,二者逻辑矛盾,进一步加重了问题。
解决方案

根据你的场景需求,可选择以下两种方案:

方案一:适配formLogin的请求格式(保留Spring Security默认认证流程)

  1. 修改Angular请求格式:将JSON请求改为表单格式
public login(loginInfo: any): Observable<Response> {
    const url: string = environment.apiUrl + "user/login";
    // 转换为x-www-form-urlencoded格式
    const formParams = new URLSearchParams();
    formParams.set('username', loginInfo.username);
    formParams.set('password', loginInfo.password);
    formParams.set('rememberMe', loginInfo.rememberMe);
    
    return this.http.post<Response>(url, formParams.toString(), {
        headers: new HttpHeaders({'Content-Type': 'application/x-www-form-urlencoded'})
    });
}
  1. 移除自定义登录控制器:删除你编写的@PostMapping("/user/login")方法,通过Security配置自定义认证响应
.formLogin(http -> http
    .loginProcessingUrl("/user/login")
    // 自定义登录成功响应
    .successHandler((request, response, authentication) -> {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        HashMap<String, String> body = new HashMap<>();
        body.put("message", "User logged in successfully");
        Response res = new Response("5", body);
        new ObjectMapper().writeValue(response.getWriter(), res);
    })
    // 自定义登录失败响应
    .failureHandler((request, response, exception) -> {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        HashMap<String, String> body = new HashMap<>();
        body.put("message", "Login failed: " + exception.getMessage());
        Response res = new Response("1", body);
        new ObjectMapper().writeValue(response.getWriter(), res);
    })
)
  1. 注意:如果坚持使用SessionCreationPolicy.STATELESS,formLogin并不适合,因为它依赖session机制,建议改用方案二。

方案二:关闭formLogin,完全自定义登录逻辑(适配无状态场景)

  1. 移除Security中的formLogin配置:删除formLogin(...)相关代码
// 去掉该行配置
// .formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer.loginProcessingUrl("/user/login"))
  1. 完善自定义登录控制器:在控制器中实现认证逻辑
@Autowired
private AuthenticationManager authenticationManager;

@PostMapping("/user/login")
public Response login(@RequestBody HashMap<String, String> request) {
    try {
        // 构建认证请求
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                request.get("username"),
                request.get("password")
        );
        // 执行Spring Security认证
        Authentication authentication = authenticationManager.authenticate(authToken);
        
        // 认证成功:这里可生成JWT令牌(无状态场景)
        HashMap<String, String> body = new HashMap<>();
        body.put("message", "User logged in successfully");
        return new Response("5", body);
    } catch (AuthenticationException e) {
        // 认证失败
        HashMap<String, String> body = new HashMap<>();
        body.put("message", "Login failed: " + e.getMessage());
        return new Response("1", body);
    }
}
  1. Angular代码无需修改:保持原JSON格式请求即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:30:13