思路
- 使用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中使用
@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);
}
- 在执行到该方法后先执行
@ss.hasPermission('system:menu:create')来鉴权 - 由上述思路可知该方法实现逻辑会交给
PermissionApi的hasPermission接口 实现如下:
@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));
}