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

如何用Spring Security自定义Spring Boot /actuator/loggers/*端点的访问权限

问题描述

我有一个Spring Boot应用,暴露了Spring Actuator等端点,需要通过自定义逻辑保护部分端点的访问权限。现有Spring Security配置代码如下:

fun filterChain(http: HttpSecurity): SecurityFilterChain? {
    val letRecoAuthenticationFilter = LetRecoAuthenticationFilter(authenticationManager, authenticationContextService)
    letRecoAuthenticationFilter.setAuthenticationFailureHandler(LetRecoAuthenticationFailureHandler())
    letRecoAuthenticationFilter.setAuthenticationSuccessHandler(successHandler())
    http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .exceptionHandling()
            .and()
            .addFilterBefore(letRecoAuthenticationFilter, AnonymousAuthenticationFilter::class.java)
            .authorizeHttpRequests { authorizeRequests ->
                authorizeRequests
                        
                        .requestMatchers(AntPathRequestMatcher("/swagger*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/login/form")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/login/connection")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/feature-toggles")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/feature-toggles*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/example*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/list")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/configuration")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/actuator/*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/admin")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/admin/*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/actuator/loggers/*")).access(AuthorizationManagers.allOf(hasRight()))
                        .anyRequest().authenticated()
            }
            .csrf()
            .disable()
            .formLogin().disable()
            .httpBasic().disable()
            .logout().disable()
    return http.build()
}

我希望当自定义方法hasRight()返回true时,允许访问/actuator/loggers/*端点,该方法代码如下:

private fun hasRight(): Boolean {
    // Implement your logic to call the service.hasright method
    return service.hasRight()
}

请问如何在Spring Boot和Spring Security中实现这一需求?

解决方案

核心问题分析

现有代码存在两个关键问题:

  1. 规则顺序错误:/actuator/*的permitAll()规则写在/actuator/loggers/*的权限检查规则前面,Spring Security会优先匹配更宽泛的路径,导致/actuator/loggers/*的权限规则永远不会生效。
  2. 权限校验方式错误:hasRight()直接返回Boolean值,这会在SecurityFilterChain初始化时就执行一次,而不是在每次请求时动态校验。access()方法需要的是AuthorizationManager<RequestAuthorizationContext>类型的参数,而非静态布尔值。

正确实现方式

方式一:自定义AuthorizationManager

创建一个自定义的AuthorizationManager,注入你的服务并实现动态权限校验逻辑:

@Component
class CustomLoggerAuthorizationManager(private val yourService: YourService) : AuthorizationManager<RequestAuthorizationContext> {
    override fun check(authentication: Authentication?, context: RequestAuthorizationContext): AuthorizationDecision {
        // 可根据请求上下文、认证信息执行自定义校验
        val hasPermission = yourService.hasRight()
        return AuthorizationDecision(hasPermission)
    }
}

在Security配置中注入该Manager,并调整规则顺序(具体路径放在宽泛路径前面):

@Autowired
private lateinit var customLoggerAuthorizationManager: CustomLoggerAuthorizationManager

fun filterChain(http: HttpSecurity): SecurityFilterChain? {
    val letRecoAuthenticationFilter = LetRecoAuthenticationFilter(authenticationManager, authenticationContextService)
    letRecoAuthenticationFilter.setAuthenticationFailureHandler(LetRecoAuthenticationFailureHandler())
    letRecoAuthenticationFilter.setAuthenticationSuccessHandler(successHandler())
    http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .exceptionHandling()
            .and()
            .addFilterBefore(letRecoAuthenticationFilter, AnonymousAuthenticationFilter::class.java)
            .authorizeHttpRequests { authorizeRequests ->
                authorizeRequests
                        // 先配置具体路径规则
                        .requestMatchers(AntPathRequestMatcher("/actuator/loggers/*")).access(customLoggerAuthorizationManager)
                        // 再配置宽泛路径规则
                        .requestMatchers(AntPathRequestMatcher("/swagger*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/login/form")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/login/connection")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/feature-toggles")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/feature-toggles*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/example*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/list")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/configuration")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/actuator/*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/admin")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/admin/*")).permitAll()
                        .anyRequest().authenticated()
            }
            .csrf()
            .disable()
            .formLogin().disable()
            .httpBasic().disable()
            .logout().disable()
    return http.build()
}

方式二:使用SpEL表达式

如果你的服务是Spring管理的Bean,可直接通过SpEL表达式调用其方法,同时调整规则顺序:

fun filterChain(http: HttpSecurity): SecurityFilterChain? {
    val letRecoAuthenticationFilter = LetRecoAuthenticationFilter(authenticationManager, authenticationContextService)
    letRecoAuthenticationFilter.setAuthenticationFailureHandler(LetRecoAuthenticationFailureHandler())
    letRecoAuthenticationFilter.setAuthenticationSuccessHandler(successHandler())
    http
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .exceptionHandling()
            .and()
            .addFilterBefore(letRecoAuthenticationFilter, AnonymousAuthenticationFilter::class.java)
            .authorizeHttpRequests { authorizeRequests ->
                authorizeRequests
                        .requestMatchers(AntPathRequestMatcher("/actuator/loggers/*")).access("@yourService.hasRight()")
                        .requestMatchers(AntPathRequestMatcher("/swagger*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/login/form")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/login/connection")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/feature-toggles")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/feature-toggles*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/example*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/list")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/configuration")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/cache/*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/actuator/*")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/admin")).permitAll()
                        .requestMatchers(AntPathRequestMatcher("/admin/*")).permitAll()
                        .anyRequest().authenticated()
            }
            .csrf()
            .disable()
            .formLogin().disable()
            .httpBasic().disable()
            .logout().disable()
    return http.build()
}

注:@yourService中的yourService是Spring容器中该服务Bean的名称(无自定义名称时默认是类名首字母小写)。

关键注意点

  • 规则顺序:Spring Security权限规则按顺序匹配,必须把更具体的路径规则放在宽泛路径规则前面,否则具体规则会被覆盖。
  • 动态校验:确保权限校验逻辑在每次请求时执行,而非初始化阶段,才能根据实时状态判断是否允许访问。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 16:25:36