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

Spring Security中Postman Basic Auth调用Save接口遇401问题

Spring Security权限与认证问题排查修复

问题描述

在使用Spring Security时遇到以下问题:

  • 浏览器登录后可正常执行Get、Delete请求,但Save接口因需要请求体无法在浏览器直接操作;
  • 使用Postman配置Basic Auth后,请求仍跳转至授权页面;
  • 添加.httpBasic(Customizer.withDefaults())后,其他接口可通过Basic Auth正常调用,但Save接口返回空内容且报401错误。

相关代码

Controller代码

@RestController
@RequestMapping("/product")
@Slf4j
public class ProductController {

    private final ProductRepository productRepository;

    @Autowired
    public ProductController(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @GetMapping("/products")
    public List<Product> getAllProducts() {
        log.info("Receiving all products");
        return productRepository.findAll();
    }

    @GetMapping("/getProduct/{id}")
    public Product getProductById(@PathVariable Long id) {
        log.info("Receiving product by id");
        return productRepository.getReferenceById(id);
    }

    @PostMapping("/save")
    public Product saveProduct(@RequestBody Product product) {
        log.info("Save products");
        return productRepository.save(product);
    }

    @DeleteMapping("/remove/{id}")
    public void deleteProduct(@PathVariable Long id) {
        Product product = productRepository.getReferenceById(id);
        log.info("delete product by id");
        if (product != null) {
            productRepository.delete(product);
        }
    }
}

Security配置代码

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.builder()
                .username("user")
                .password(passwordEncoder().encode("user1Pass"))
                .roles(UserRole.USER.name())
                .build();
        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder().encode("admin1Pass"))
                .roles(UserRole.USER.name())
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authz) -> authz
                        .requestMatchers("/product/save").hasRole(UserRole.ADMIN.name())
                        .requestMatchers("/product/remove/**").hasRole(UserRole.ADMIN.name())
                        .requestMatchers("/product/products").hasAnyRole(UserRole.ADMIN.name(), UserRole.USER.name())
                        .requestMatchers("/product/getProduct/{id}").permitAll()
                        .requestMatchers("/product").permitAll()
                        .anyRequest().authenticated()

                ).formLogin(withDefaults());
        return http.build();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

问题分析

  1. 权限配置错误:admin用户的角色被设置为UserRole.USER.name(),但Save接口要求ADMIN角色,导致admin用户无权限访问Save接口,触发401错误;
  2. 认证跳转冲突:默认formLogin启用时,认证失败会触发页面跳转,导致Postman使用Basic Auth时仍被跳转;
  3. CSRF拦截:Rest接口的Post请求默认会被Spring Security的CSRF防护拦截,导致请求失败。

修复步骤

1. 修正用户角色配置

将admin用户的角色改为ADMIN,确保其拥有对应权限:

@Bean
public InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.builder()
            .username("user")
            .password(passwordEncoder().encode("user1Pass"))
            .roles(UserRole.USER.name())
            .build();
    UserDetails admin = User.builder()
            .username("admin")
            .password(passwordEncoder().encode("admin1Pass"))
            .roles(UserRole.ADMIN.name()) // 修正为ADMIN角色
            .build();
    return new InMemoryUserDetailsManager(user, admin);
}

2. 调整SecurityFilterChain配置

针对Rest接口场景,关闭CSRF防护,同时修改formLogin的处理逻辑,避免认证失败时跳转页面,保留Basic Auth功能:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
            .csrf(csrf -> csrf.disable()) // 关闭CSRF防护,适配Rest接口
            .authorizeHttpRequests(authz -> authz
                    .requestMatchers("/product/save").hasRole(UserRole.ADMIN.name())
                    .requestMatchers("/product/remove/**").hasRole(UserRole.ADMIN.name())
                    .requestMatchers("/product/products").hasAnyRole(UserRole.ADMIN.name(), UserRole.USER.name())
                    .requestMatchers("/product/getProduct/{id}").permitAll()
                    .requestMatchers("/product").permitAll()
                    .anyRequest().authenticated()
            )
            .formLogin(form -> form
                    .failureHandler((request, response, exception) -> {
                        // 认证失败返回JSON响应,不跳转
                        response.setContentType("application/json;charset=UTF-8");
                        response.setStatus(HttpStatus.UNAUTHORIZED.value());
                        response.getWriter().write("{\"code\":401,\"msg\":\"认证失败\"}");
                    })
                    .successHandler((request, response, authentication) -> {
                        // 登录成功返回JSON响应
                        response.setContentType("application/json;charset=UTF-8");
                        response.getWriter().write("{\"code\":200,\"msg\":\"登录成功\"}");
                    })
            )
            .httpBasic(Customizer.withDefaults()); // 启用Basic Auth

    return http.build();
}

3. 验证修复效果

  • 使用Postman调用/product/save接口,配置admin账号的Basic Auth,可正常提交请求并返回结果;
  • 使用user账号调用/product/save接口,会返回401错误,符合权限控制预期;
  • 浏览器登录后,可通过AJAX方式调用Save接口,认证成功/失败均返回JSON,无页面跳转。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 20:24:55