SpringSecurity动态配置资源权限浅析
字数 1851 2025-08-18 11:36:48

Spring Security动态配置资源权限分析与安全审计指南

0x00 概述

Spring Security中实现通过数据库动态配置URL资源权限的核心机制是通过配置验证过滤器来实现资源权限的加载和验证。主要流程包括:

  1. 系统启动时从数据库加载系统资源权限列表
  2. 用户登录时将用户资源权限列表添加到用户信息中
  3. 请求访问时对比系统资源权限列表和用户资源权限列表进行权限判断

0x01 关键组件

1.1 核心配置项

实现动态资源权限配置需要以下关键组件:

  1. securityMetadataSource

    • 实现FilterInvocationSecurityMetadataSource接口
    • 负责加载资源权限配置
  2. accessDecisionManager

    • 自定义AccessDecisionManager
    • 决定用户是否有权限访问资源
  3. filterSecurityInterceptor

    • 继承AbstractSecurityInterceptor并实现Filter接口
    • 自定义验证过滤器替换默认验证过滤器
  4. WebSecurityConfig

    • 系统配置类
    • 启用filterSecurityInterceptor

注意:从Spring Security 6开始,FilterSecurityInterceptorAuthorizationFilter替代,主要通过access方法实现权限控制。

1.2 Spring Security 6+实现示例

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(register -> register.anyRequest().access((authentication, object) -> {
        // 检查请求URL是否匹配数据库中的配置
        boolean isMatch = false;
        String requestURI = object.getRequest().getRequestURI();
        List<MenuWithRoleVO> menuWithRole = menuService.getMenuWithRole();
        
        for (MenuWithRoleVO m : menuWithRole) {
            if (antPathMatcher.match(m.getUrl(), requestURI)) {
                isMatch = true;
                List<Role> roles = m.getRoles();
                Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
                
                for (GrantedAuthority authority : authorities) {
                    for (Role role : roles) {
                        if (authority.getAuthority().equals(role.getName())) {
                            return new AuthorizationDecision(true);
                        }
                    }
                }
            }
        }
        
        if (!isMatch) {
            // 未匹配的URL,只要登录就能访问
            if (authentication.get() instanceof AnonymousAuthenticationToken) {
                return new AuthorizationDecision(false);
            } else {
                return new AuthorizationDecision(true);
            }
        }
        return new AuthorizationDecision(false);
    }))
    .formLogin(form -> /*...*/)
    .csrf(csrf -> /*...*/)
    .exceptionHandling(e -> /*...*/)
    .logout(logout -> /*...*/);
    
    return http.build();
}

0x02 关键实现细节

2.1 FilterInvocation类

org.springframework.security.web.FilterInvocation封装HTTP请求和响应,在安全过滤链中传递请求上下文信息。关键方法:

  • getRequest():获取HttpServletRequest对象
  • getResponse():获取HttpServletResponse对象
  • getRequestUrl():获取请求URL

2.2 请求路径获取方式

主要通过org.springframework.security.web.util.UrlUtils工具类处理:

  1. buildFullRequestUrl

    • 通过requestURI获取完整URL
    • 包含协议、端口等额外信息
    • 不做过多处理
  2. buildRequestUrl

    • 优先使用servletPath获取请求路径
    • servletPath为空时使用requestURI
    • 对请求路径进行归一化处理

0x03 安全审计关键点

3.1 HttpFirewall防护

Spring Security提供HttpFirewall接口处理非法请求,默认使用StrictHttpFirewall

  • 校验URL是否规范
  • 拦截;//../等特殊字符
  • 高版本增加换行符等黑名单规则

3.2 常见绕过风险

3.2.1 ?参数绕过

UrlUtils#buildRequestUrl在query不为空时会拼接?和参数,可能导致绕过:

// 假设匹配逻辑
if (antPathMatcher.match("/admin/manage", fi.getRequestUrl())) {
    // 权限检查
}

// 绕过方式:访问/admin/manage?

3.2.2 尾部斜杠绕过

利用Spring Web的TrailingSlashMatch特性:

  • 低版本:TrailingSlashMatch
  • 高版本:matchOptionalTrailingSeparator属性

请求路径尾部存在额外斜杠可能绕过权限检查。

3.2.3 AntPathMatcher解析差异

不同版本AntPathMatcher/AntPathRequestMatcher存在差异:

  • 5.3.22版本前:Pattern不配置dotall模式,不匹配\r\n
  • 5.3.22版本后:配置dotall模式,.匹配任何字符包括行结束符

绕过示例

// 匹配逻辑
antPathMatcher.match("/admin/{name}", fi.getRequestUrl())

// 绕过方式:访问/admin/%0d%0a

3.3 安全实现建议

  1. 统一路径规范化处理
  2. 严格校验请求路径
  3. 使用最新版本Spring Security
  4. 自定义HttpFirewall增强防护
  5. 对未匹配路径采用默认拒绝策略

0x04 典型实现示例

4.1 自定义SecurityMetadataSource

@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    String requestUrl = ((FilterInvocation) object).getRequestUrl();
    List<Menu> menus = menuService.getAllMenusWithRole();
    
    for (Menu menu : menus) {
        if (antPathMatcher.match(menu.getUrl(), requestUrl)) {
            List<Role> roles = menu.getRoles();
            String[] str = new String[roles.size()];
            for (int i = 0; i < roles.size(); i++) {
                str[i] = roles.get(i).getName();
            }
            return SecurityConfig.createList(str);
        }
    }
    return SecurityConfig.createList("ROLE_LOGIN");
}

4.2 路径匹配增强

// 增强的路径匹配逻辑
private boolean securePathMatch(String pattern, String path) {
    // 1. 规范化路径
    path = StringUtils.cleanPath(path);
    
    // 2. 移除查询参数
    if (path.contains("?")) {
        path = path.substring(0, path.indexOf("?"));
    }
    
    // 3. 处理尾部斜杠
    if (!pattern.endsWith("/") && path.endsWith("/")) {
        path = path.substring(0, path.length() - 1);
    }
    
    // 4. 使用最新版匹配器
    return antPathMatcher.match(pattern, path);
}

0x05 总结

动态配置URL资源权限时,审计重点应关注:

  1. 路径获取和匹配的实现方式
  2. 不同版本间的解析差异
  3. 特殊字符和边缘情况的处理
  4. 默认策略的安全性
  5. 与框架其他安全组件的协同工作

通过全面考虑这些方面,可以有效避免权限绕过等安全问题。

Spring Security动态配置资源权限分析与安全审计指南 0x00 概述 Spring Security中实现通过数据库动态配置URL资源权限的核心机制是通过配置验证过滤器来实现资源权限的加载和验证。主要流程包括: 系统启动时从数据库加载系统资源权限列表 用户登录时将用户资源权限列表添加到用户信息中 请求访问时对比系统资源权限列表和用户资源权限列表进行权限判断 0x01 关键组件 1.1 核心配置项 实现动态资源权限配置需要以下关键组件: securityMetadataSource : 实现 FilterInvocationSecurityMetadataSource 接口 负责加载资源权限配置 accessDecisionManager : 自定义 AccessDecisionManager 决定用户是否有权限访问资源 filterSecurityInterceptor : 继承 AbstractSecurityInterceptor 并实现 Filter 接口 自定义验证过滤器替换默认验证过滤器 WebSecurityConfig : 系统配置类 启用 filterSecurityInterceptor 注意:从Spring Security 6开始, FilterSecurityInterceptor 被 AuthorizationFilter 替代,主要通过 access 方法实现权限控制。 1.2 Spring Security 6+实现示例 0x02 关键实现细节 2.1 FilterInvocation类 org.springframework.security.web.FilterInvocation 封装HTTP请求和响应,在安全过滤链中传递请求上下文信息。关键方法: getRequest() :获取HttpServletRequest对象 getResponse() :获取HttpServletResponse对象 getRequestUrl() :获取请求URL 2.2 请求路径获取方式 主要通过 org.springframework.security.web.util.UrlUtils 工具类处理: buildFullRequestUrl : 通过requestURI获取完整URL 包含协议、端口等额外信息 不做过多处理 buildRequestUrl : 优先使用servletPath获取请求路径 servletPath为空时使用requestURI 对请求路径进行归一化处理 0x03 安全审计关键点 3.1 HttpFirewall防护 Spring Security提供 HttpFirewall 接口处理非法请求,默认使用 StrictHttpFirewall : 校验URL是否规范 拦截 ; 、 // 、 ../ 等特殊字符 高版本增加换行符等黑名单规则 3.2 常见绕过风险 3.2.1 ?参数绕过 UrlUtils#buildRequestUrl 在query不为空时会拼接 ? 和参数,可能导致绕过: 3.2.2 尾部斜杠绕过 利用Spring Web的TrailingSlashMatch特性: 低版本: TrailingSlashMatch 高版本: matchOptionalTrailingSeparator 属性 请求路径尾部存在额外斜杠可能绕过权限检查。 3.2.3 AntPathMatcher解析差异 不同版本 AntPathMatcher / AntPathRequestMatcher 存在差异: 5.3.22版本前:Pattern不配置dotall模式,不匹配 \r 、 \n 5.3.22版本后:配置dotall模式, . 匹配任何字符包括行结束符 绕过示例 : 3.3 安全实现建议 统一路径规范化处理 严格校验请求路径 使用最新版本Spring Security 自定义 HttpFirewall 增强防护 对未匹配路径采用默认拒绝策略 0x04 典型实现示例 4.1 自定义SecurityMetadataSource 4.2 路径匹配增强 0x05 总结 动态配置URL资源权限时,审计重点应关注: 路径获取和匹配的实现方式 不同版本间的解析差异 特殊字符和边缘情况的处理 默认策略的安全性 与框架其他安全组件的协同工作 通过全面考虑这些方面,可以有效避免权限绕过等安全问题。