Spring Boot中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请求:

- GET请求:

补充信息
- 所有带认证头的GET请求均正常工作
- PUT、POST、DELETE请求统一返回401 Unauthorized
解决方案
问题根源
配置继承了VaadinWebSecurity,调用super.configure(http)和setLoginView(http, LoginView.class)时,Vaadin的安全机制会覆盖或添加额外的认证拦截逻辑,尤其是针对非GET请求的处理,导致Basic Auth凭证未被正确识别。
修复步骤
- 调整配置顺序
将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(); }
- 排除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/**"); }
验证认证头格式
确保POST/PUT/DELETE请求的Authorization头格式正确,应为Basic base64编码的用户名:密码,建议用Postman的Authorization标签选择Basic Auth自动生成,避免手动输入错误。检查用户权限
确认当前用户拥有对应接口的操作权限(你的配置中/api/**仅要求authenticated,这条可能性较低,但可排查验证)。
内容的提问来源于stack exchange,提问作者AleXeNoN
相关产品推荐
相关产品推荐

