基于SpringSecurity实现URL权限校验

397 阅读3分钟

思路

  1. 使用SpringSecurity提供的@PreAuthorize注解来实现对URL权限的校验
@PostMapping("/create")
@Operation(summary = "创建菜单")
@PreAuthorize("@ss.hasPermission('system:menu:create')")
public CommonResult<Long> createMenu(@Valid @RequestBody MenuSaveVO createReqVO) {
    Long menuId = menuService.createMenu(createReqVO);
    return success(menuId);
}

@PreAuthoruze作用是允许你再方法执行之前基于权限的访问扩展,它通过表达式语言(SpEL)来评估方法是否可以执行,当返回值为true时方法才会被执行

  1. 在骤1中使用@ss.hasPermission('system:menu:create')"@ss是自定义一个Bean真正校验权限功能由它来实现

    • 自定义ssBean
    @Bean("ss") // 使用 Spring Security 的缩写,方便使用
    public SecurityFrameworkService securityFrameworkService(PermissionApi permissionApi) {
        return new SecurityFrameworkServiceImpl(permissionApi);
    }
    
    • PermissionApi: 权限 API 接口
    public interface PermissionApi {
    
        /**
         * 获得拥有多个角色的用户编号集合
         *
         * @param roleIds 角色编号集合
         * @return 用户编号集合
         */
        Set<Long> getUserRoleIdListByRoleIds(Collection<Long> roleIds);
    
        /**
         * 判断是否有权限,任一一个即可
         *
         * @param userId 用户编号
         * @param permissions 权限
         * @return 是否
         */
        boolean hasAnyPermissions(Long userId, String... permissions);
    
        /**
         * 判断是否有角色,任一一个即可
         *
         * @param userId 用户编号
         * @param roles 角色数组
         * @return 是否
         */
        boolean hasAnyRoles(Long userId, String... roles);
    
    }
    
    • SecurityFrameworkService 接口定义权限校验相关接口
    public interface SecurityFrameworkService {
    
        /**
         * 判断是否有权限
         *
         * @param permission 权限
         * @return 是否
         */
        boolean hasPermission(String permission);
    
        /**
         * 判断是否有权限,任一一个即可
         *
         * @param permissions 权限
         * @return 是否
         */
        boolean hasAnyPermissions(String... permissions);
    
        /**
         * 判断是否有角色
         *
         * 注意,角色使用的是 SysRoleDO 的 code 标识
         *
         * @param role 角色
         * @return 是否
         */
        boolean hasRole(String role);
    
        /**
         * 判断是否有角色,任一一个即可
         *
         * @param roles 角色数组
         * @return 是否
         */
        boolean hasAnyRoles(String... roles);
    
        /**
         * 判断是否有授权
         *
         * @param scope 授权
         * @return 是否
         */
        boolean hasScope(String scope);
    
        /**
         * 判断是否有授权范围,任一一个即可
         *
         * @param scope 授权范围数组
         * @return 是否
         */
        boolean hasAnyScopes(String... scope);
    
    • SecurityFrameworkServiceImpl 实现类
    @AllArgsConstructor
    public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
    
        private final PermissionApi permissionApi;
    
        @Override
        public boolean hasPermission(String permission) {
            return hasAnyPermissions(permission);
        }
    
        @Override
        public boolean hasAnyPermissions(String... permissions) {
            Long userId = getLoginUserId();
            if (userId == null) {
                return false;
            }
            return permissionApi.hasAnyPermissions(userId, permissions);
        }
    
        @Override
        public boolean hasRole(String role) {
            return hasAnyRoles(role);
        }
    
        @Override
        public boolean hasAnyRoles(String... roles) {
            Long userId = getLoginUserId();
            if (userId == null) {
                return false;
            }
            return permissionApi.hasAnyRoles(userId, roles);
        }
    
        @Override
        public boolean hasScope(String scope) {
            return hasAnyScopes(scope);
        }
    
        @Override
        public boolean hasAnyScopes(String... scope) {
            LoginUser user = SecurityFrameworkUtils.getLoginUser();
            if (user == null) {
                return false;
            }
            return CollUtil.containsAny(user.getScopes(), Arrays.asList(scope));
        }
    
    }
    

这里只是定义了相关规范 这种关于权限的实现交给PermissionApi该接口来实验

实现

综上所述,我们只要在Controller类的方法上添加@PreAuthorize注解并实现PermissionApi接口 SpringSecurity默认会前置执行PermissionApi的实现,并返回true和false来判断该方法是否执行

实例

@PostMapping("/create")
@Operation(summary = "创建菜单")
@PreAuthorize("@ss.hasPermission('system:menu:create')")
public CommonResult<Long> createMenu(@Valid @RequestBody MenuSaveVO createReqVO) {
    Long menuId = menuService.createMenu(createReqVO);
    return success(menuId);
}
  1. 在执行到该方法后先执行@ss.hasPermission('system:menu:create')来鉴权
  2. 由上述思路可知该方法实现逻辑会交给PermissionApihasPermission接口 实现如下:
@Override
public boolean hasAnyPermissions(Long userId, String... permissions) {
    // 如果为空,说明已经有权限
    if (ArrayUtil.isEmpty(permissions)) {
        return true;
    }

    // 获得当前登录的角色。如果为空,说明没有权限
    List<RoleDO> roles = getEnableUserRoleListByUserIdFromCache(userId);
    if (CollUtil.isEmpty(roles)) {
        return false;
    }

    // 情况一:遍历判断每个权限,如果有一满足,说明有权限
    for (String permission : permissions) {
        if (hasAnyPermission(roles, permission)) {
            return true;
        }
    }

    // 情况二:如果是超管,也说明有权限
    return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId));
}