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

Spring Boot中POST/PUT/DELETE请求返回401 Unauthorized求助

Spring Boot Basic Auth:GET请求正常,POST/PUT/DELETE返回401 Unauthorized

问题描述

开发的Spring Boot应用采用Basic Authentication(账号+密码)认证方式,当前存在以下异常:

  • 携带认证头的GET请求可正常返回200状态码及数据
  • 发送POST、PUT、DELETE请求时,均返回401 Unauthorized

配置与代码

安全配置类

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends VaadinWebSecurity {

    private static final String LOGIN_URL = "/login";
    private static final String LOGIN_PROCESSING_URL = "/login";
    private static final String LOGIN_FAILURE_URL = "/login?error";
    private static final String LOGOUT_SUCCESS_URL = "/";
    private static final String DENIED_PAGE_URL = "/404";

    private final UserService userService;
    private final PasswordEncoder passwordEncoder;

    public SecurityConfiguration(UserService userService, PasswordEncoder passwordEncoder) {
        this.userService = userService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeHttpRequests(auth -> {
                    auth.requestMatchers("/login", "/register").permitAll();
                    auth.requestMatchers("/public/**").permitAll();
                    auth.requestMatchers("/icons/**").permitAll();
                    auth.requestMatchers("/images/**").permitAll();
                    auth.requestMatchers("/api/**").authenticated();
                    auth.requestMatchers("/private/**").authenticated();
                    auth.requestMatchers("/admin/**").hasAnyRole("ADMIN", "SUPER_ADMIN");
                })
                .formLogin(loginForm -> {
                    loginForm.loginPage(LOGIN_URL);
                    loginForm.loginProcessingUrl(LOGIN_PROCESSING_URL);
                    loginForm.failureUrl(LOGIN_FAILURE_URL);
                })
                .logout(logout -> logout.logoutSuccessUrl(LOGOUT_SUCCESS_URL))
                .exceptionHandling(e -> {
                    e.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
                    e.accessDeniedPage(DENIED_PAGE_URL);
                })
                .httpBasic();

        super.configure(http);
        setLoginView(http, LoginView.class);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        daoAuthenticationProvider.setUserDetailsService(userService);
        return daoAuthenticationProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

}

请求接口示例

@PostMapping("/save")
public Expense saveExpense(@RequestBody ExpenseRequest expenseRequest) {
    Expense expense = expenseConvertor.convertToExpense(expenseRequest);
    return expenseService.saveExpense(expense);
}

请求截图

  • POST请求:POST请求截图
  • GET请求:GET请求截图

补充信息

  • 所有带认证头的GET请求均正常工作
  • PUT、POST、DELETE请求统一返回401 Unauthorized

解决方案

问题根源

配置继承了VaadinWebSecurity,调用super.configure(http)和setLoginView(http, LoginView.class)时,Vaadin的安全机制会覆盖或添加额外的认证拦截逻辑,尤其是针对非GET请求的处理,导致Basic Auth凭证未被正确识别。

修复步骤

  1. 调整配置顺序
    将super.configure(http)和setLoginView的调用移到自定义配置之前,确保Basic Auth配置能覆盖Vaadin默认设置:
@Override
protected void configure(HttpSecurity http) throws Exception {
    // 先执行Vaadin基础配置
    super.configure(http);
    setLoginView(http, LoginView.class);

    // 再添加自定义安全规则
    http
            .csrf().disable()
            .authorizeHttpRequests(auth -> {
                auth.requestMatchers("/login", "/register").permitAll();
                auth.requestMatchers("/public/**").permitAll();
                auth.requestMatchers("/icons/**").permitAll();
                auth.requestMatchers("/images/**").permitAll();
                auth.requestMatchers("/api/**").authenticated();
                auth.requestMatchers("/private/**").authenticated();
                auth.requestMatchers("/admin/**").hasAnyRole("ADMIN", "SUPER_ADMIN");
            })
            .formLogin(loginForm -> {
                loginForm.loginPage(LOGIN_URL);
                loginForm.loginProcessingUrl(LOGIN_PROCESSING_URL);
                loginForm.failureUrl(LOGIN_FAILURE_URL);
            })
            .logout(logout -> logout.logoutSuccessUrl(LOGOUT_SUCCESS_URL))
            .exceptionHandling(e -> {
                e.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
                e.accessDeniedPage(DENIED_PAGE_URL);
            })
            .httpBasic();
}
  1. 排除API路径的Vaadin拦截
    如果接口使用/api/**前缀,在configure(WebSecurity web)中添加排除规则,让Spring Security的Basic Auth直接处理这些路径:
@Override
public void configure(WebSecurity web) throws Exception {
    super.configure(web);
    // 排除API路径,避免Vaadin安全逻辑干扰
    web.ignoring().requestMatchers("/api/**");
}
  1. 验证认证头格式
    确保POST/PUT/DELETE请求的Authorization头格式正确,应为Basic base64编码的用户名:密码,建议用Postman的Authorization标签选择Basic Auth自动生成,避免手动输入错误。

  2. 检查用户权限
    确认当前用户拥有对应接口的操作权限(你的配置中/api/**仅要求authenticated,这条可能性较低,但可排查验证)。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 11:07:15