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

Spring Boot中基于JWT的角色权限与地域访问控制实现咨询

Spring Boot 角色+地域权限系统设计方案

一、数据库模型设计

核心表结构

1. 用户表(users)

存储用户基础信息:

  • id:主键,自增
  • username:用户名,唯一
  • password_hash:加密后的密码
  • email:邮箱,可选
  • created_at:创建时间

2. 角色表(roles)

定义系统角色:

  • id:主键,自增
  • role_name:角色标识(admin/supervisor/customer),唯一
  • description:角色描述,可选

3. 地域表(regions)

用自关联实现州→城市→街道的层级结构:

  • id:主键,自增
  • name:地域名称(如“华盛顿州”“西雅图市”)
  • parent_id:父地域ID(州的parent_id为null,城市关联对应州ID,街道关联对应城市ID)
  • region_level:层级标识(1=州,2=城市,3=街道)

4. 用户权限关联表(user_role_regions)

记录用户对应角色的地域权限范围:

  • id:主键,自增
  • user_id:关联users.id
  • role_id:关联roles.id
  • region_id:关联regions.id
  • 联合唯一约束:(user_id, role_id, region_id),避免重复授权

表结构说明

  • 地域表通过parent_id和region_level实现层级递归,方便查询某一地域的所有下级节点(比如州下辖的所有城市、街道)。
  • 用户权限关联表支持用户拥有多角色+多地域的组合权限(比如一个用户既是A州的admin,又是B城市的supervisor)。

二、登录后地域权限校验逻辑(结合JWT)

1. 登录流程优化

  • 用户提交用户名密码验证通过后,查询该用户的所有user_role_regions记录,提取角色标识、授权地域ID、授权地域层级,将这些信息存入JWT的Payload中,示例Payload:
    {
      "sub": "user_001",
      "exp": 1728000000,
      "permissions": [
        {"role": "admin", "region_id": 1, "region_level": 1},
        {"role": "supervisor", "region_id": 5, "region_level": 2}
      ]
    }
    
  • 无需在登录阶段提前过滤地域,只需把权限元数据存入JWT,后续请求时做实时校验。

2. 请求拦截与权限校验

基于Spring Security实现拦截逻辑,步骤如下:

(1)解析JWT获取权限信息

自定义JWT过滤器,在请求到达Controller前解析JWT,将用户权限信息存入Authentication对象的principal中(可扩展UserDetails类,新增permissions字段)。

(2)地域权限校验逻辑

针对每个业务请求,提取请求中涉及的目标地域ID(比如查询某城市数据时的城市ID),执行以下校验:

  • 步骤1:查询目标地域的完整层级链:通过递归SQL获取目标地域及其所有上级地域的ID(比如街道→城市→州)。
    递归SQL示例:
    WITH RECURSIVE region_hierarchy AS (
        SELECT id, parent_id FROM regions WHERE id = ?
        UNION ALL
        SELECT r.id, r.parent_id FROM regions r JOIN region_hierarchy rh ON r.id = rh.parent_id
    )
    SELECT id FROM region_hierarchy;
    
  • 步骤2:匹配用户权限:遍历用户的权限列表,检查是否存在任意一条权限满足:
    1. 目标地域的层级链包含该权限的region_id;
    2. 角色对应的权限范围覆盖目标地域层级(比如admin角色授权州级地域时,允许访问该州及下辖所有城市、街道;customer角色若授权街道级地域,则仅允许访问该街道)。

(3)代码实现示例

自定义Spring Security权限表达式,方便在Controller中直接使用:

@Component
public class RegionSecurityExpressions {
    private final RegionRepository regionRepository;

    public RegionSecurityExpressions(RegionRepository regionRepository) {
        this.regionRepository = regionRepository;
    }

    public boolean hasRegionAccess(Authentication auth, Long targetRegionId) {
        CustomUserDetails user = (CustomUserDetails) auth.getPrincipal();
        Set<Long> targetHierarchy = regionRepository.findRegionHierarchyIds(targetRegionId);

        for (UserPermission perm : user.getPermissions()) {
            Set<Long> allowedRegions = regionRepository.findSubtreeIds(perm.getRegionId());
            // 检查目标地域是否在授权地域的子树中
            if (allowedRegions.contains(targetRegionId)) {
                // 可针对不同角色添加额外限制,比如customer仅允许访问街道级
                if ("customer".equals(perm.getRole()) && perm.getRegionLevel() != 3) {
                    continue;
                }
                return true;
            }
        }
        return false;
    }
}

在Controller中使用:

@RestController
@RequestMapping("/api/streets")
public class StreetController {
    @GetMapping("/{regionId}")
    @PreAuthorize("@regionSecurityExpressions.hasRegionAccess(authentication, #regionId)")
    public List<Street> getStreets(@PathVariable Long regionId) {
        // 业务逻辑实现
    }
}

3. 性能优化建议

  • 缓存授权地域子树:将用户权限对应的地域子树ID列表存入Redis,有效期与JWT一致,避免每次请求都执行递归SQL。
  • JWT黑名单机制:当用户权限变更时,将旧JWT加入黑名单,在过滤器中校验,及时失效旧权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:42:48