You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何配置Spring Security的SecurityFilterChain实现默认禁止所有端点,仅通过方法注解控制访问权限?

如何配置Spring Security的SecurityFilterChain实现默认禁止所有端点,仅通过方法注解控制访问权限?

哈哈,这个问题我之前也踩过一模一样的坑!你遇到的核心问题其实是Spring Security过滤器链的授权规则优先级比方法级注解高——要么过滤器链直接把所有请求拒了,方法注解根本没机会生效;要么全放开导致未注解的端点暴露,完全违背了“默认禁止”的初衷。

别着急,我给你一套靠谱的解决方案,结合方法级安全的默认规则和过滤器链配置就能完美实现需求:

核心思路

要实现「默认禁止所有端点,仅通过@PreAuthorize等方法注解开放权限」,需要两步配合:

  1. 给所有未加注解的控制器方法设置默认拒绝的规则;
  2. 让过滤器链允许请求到达控制器,把授权判断交给方法级安全拦截器处理。

完整代码实现

1. 安全配置类WebSecurityConfig

这里关键是用@EnableMethodSecuritydefaultPreAuthorize属性设置全局默认规则,同时过滤器链只做基础的通行配置:

@EnableWebSecurity
// 启用方法级安全,并且给所有未加@PreAuthorize的方法默认设置denyAll()
@EnableMethodSecurity(prePostEnabled = true, defaultPreAuthorize = "denyAll()")
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain mainFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(authorize -> authorize
                        // 允许请求到达控制器,让方法注解处理授权逻辑
                        // 如果有静态资源(如css/js)需要开放,在这里单独配置:
                        // .mvcMatchers("/css/**", "/js/**").permitAll()
                        .anyRequest().authenticated()
                )
                // 配置表单登录,登录页面直接放开匿名访问
                .formLogin(form -> form
                        .loginPage("/login")
                        .permitAll()
                )
                .build();
    }

    // 示例:内存用户认证(实际项目换成数据库认证即可)
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails user = User.withUsername("user")
                .password("{noop}password") // {noop}表示不加密,仅测试用
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
}

2. 控制器类(保持你的原有代码即可)

只需要在需要开放的方法上添加@PreAuthorize注解,未注解的方法会自动被默认规则拒绝:

@Controller
@RequestMapping
public class MainController {

    // 允许所有人访问,覆盖默认的denyAll()
    @GetMapping
    @PreAuthorize("permitAll()")
    public String index() { return "index"; }

    // 仅允许匿名用户访问(登录页面)
    @GetMapping("/login")
    @PreAuthorize("isAnonymous()")
    public String login() { return "login"; }

    // 仅允许拥有ROLE_USER权限的登录用户访问
    @GetMapping("/private")
    @PreAuthorize("hasAuthority('ROLE_USER')")
    public String secrets() { return "private"; }

    // 这个端点没有加注解,会自动应用默认的denyAll(),任何人都无法访问
    @GetMapping("/unprotected")
    public String unprotected() { return "unprotected"; }
}

为什么这样能生效?

  • @EnableMethodSecurity(defaultPreAuthorize = "denyAll()")会自动给所有未标注@PreAuthorize的控制器方法加上默认授权规则,相当于给每个未注解的方法都加了@PreAuthorize("denyAll()")
  • 过滤器链中的anyRequest().authenticated()确保只有认证用户能进入控制器,但方法上的@PreAuthorize可以轻松覆盖这个规则(比如permitAll()允许所有人,isAnonymous()允许匿名);
  • 登录页面通过formLogin().permitAll()直接放开,避免默认规则影响,你也可以改用@PreAuthorize("isAnonymous()")来控制,效果完全一样。

额外小提示

如果不想在过滤器链中设置authenticated(),也可以改成anyRequest().permitAll()——这样所有请求都能到达控制器,完全由方法级的默认规则和注解来控制权限,效果是一致的,只是过滤器链层面不做认证检查而已。

备注:内容来源于stack exchange,提问作者Slevin

火山引擎 最新活动