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

Spring Boot3+Spring Security6中requestMatchers引发403 Forbidden问题求助

Spring Security 6 403 Forbidden问题排查与解决

我已多日尝试解决Spring Security 6的权限问题,查阅官方文档和多个教程仍未找到原因。目前访问/resource/**端点正常返回200,但访问/api/**、/db/**这类配置了权限的端点时,始终返回403 Forbidden。已确认从application.properties读取的账号密码正确,排除配置读取问题。


相关代码

WebSecurityConfigurer.class

package com.transfer.market.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;

@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer {

    private final String ADMIN;
    private final String ADMIN_PASS;
    private final String SUPER;
    private final String SUPER_PASS;

    @Autowired
    public WebSecurityConfigurer(AppSecurityExternalConfig appSecurityExternalConfig) {
        this.ADMIN = appSecurityExternalConfig.getUser().getAdmin();
        this.ADMIN_PASS = appSecurityExternalConfig.getPassword().getAdmin();
        this.SUPER = appSecurityExternalConfig.getUser().getSup();
        this.SUPER_PASS = appSecurityExternalConfig.getPassword().getSup();
    }

    @Bean
    public UserDetailsService users() {

        UserDetails admin = User.builder()
                .username(ADMIN)
                .password(encoder().encode(ADMIN_PASS))
                .roles("ADMIN")
                .build();

        UserDetails sup = User.builder()
                .username(SUPER)
                .password(encoder().encode(SUPER_PASS))
                .roles("ADMIN", "DBA")
                .build();

        return new InMemoryUserDetailsManager(admin, sup);
    }

    @Bean
    public SecurityFilterChain web(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests(auth -> auth
                                .requestMatchers("/resource/**").permitAll()
                                .requestMatchers("/api/**").hasAnyRole("ADMIN", "DBA")
                                .requestMatchers("/db/**")
            .access(new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasRole('DBA')"))
                                .anyRequest().denyAll()
                );


        return http.build();
    }

    @Bean
    public static BCryptPasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }
}

AppSecurityExternalConfig.class

package com.transfer.market.configuration;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Data
@Configuration
@ConfigurationProperties(prefix = "config.security")
public class AppSecurityExternalConfig {
    private User user;
    private Password password;

    @Data
    @Component
    @ConfigurationProperties(prefix = "user")
    public static class User {
        private String user;
        private String admin;
        private String sup;
    }

    @Data
    @Component
    @ConfigurationProperties(prefix = "password")
    public static class Password {
        private String user;
        private String admin;
        private String sup;
    }
}

application.properties

...
# Security:
config.security.user.admin=admin
config.security.password.admin=pass

config.security.user.sup=super
config.security.password.sup=pass
...

PlayerController.class

@RestController
@Validated
public class PlayerController {

    private final PlayerService playerService;

    @Autowired
    public PlayerController(PlayerService playerService) {
        this.playerService = playerService;
    }

    @PostMapping("/api/players")
    public ResponseEntity<Player> addSingle(@RequestBody @Valid Player player) {
        return new ResponseEntity<>(playerService.addSingle(player), HttpStatus.CREATED);
    }
...

问题原因及修复方案

1. 缺少认证方式配置

当前SecurityFilterChain仅定义了权限规则,但未指定任何认证机制(如HTTP Basic、表单登录)。Spring Security无法对请求用户进行身份验证,直接返回403。

修复:在web方法中添加HTTP Basic认证配置:

@Bean
public SecurityFilterChain web(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .authorizeHttpRequests(auth -> auth
                            .requestMatchers("/resource/**").permitAll()
                            .requestMatchers("/api/**").hasAnyRole("ADMIN", "DBA")
                            .requestMatchers("/db/**")
                .access(new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasRole('DBA')"))
                            .anyRequest().denyAll()
            )
            // 添加HTTP Basic认证
            .httpBasic(Customizer.withDefaults());

    return http.build();
}

2. 配置属性绑定错误

AppSecurityExternalConfig的内部类User和Password额外添加了@Component和@ConfigurationProperties,与外层类的prefix = "config.security"冲突,导致配置路径解析错误(例如config.security.user.admin会被解析为user.admin,无法正确读取)。

修复:移除内部类上的@Component和@ConfigurationProperties注解:

@Data
@Configuration
@ConfigurationProperties(prefix = "config.security")
public class AppSecurityExternalConfig {
    private User user;
    private Password password;

    @Data
    public static class User {
        private String user;
        private String admin;
        private String sup;
    }

    @Data
    public static class Password {
        private String user;
        private String admin;
        private String sup;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:15:33