芋道框架支持基本的权限控制机制,其技术层面,是通过SpringSecurity框架来实现的。
下面就从源代码代码中,分析一下芋道是如何做权限控制的。
拿岗位管理的代码示例来说。在文件PostController.java中,看如下代码:
@GetMapping(value = "/get")
@Operation(summary = "获得岗位信息")
@Parameter(name = "id", description = "岗位编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:post:query')")
public CommonResult<PostRespVO> getPost(@RequestParam("id") Long id) {
PostDO post = postService.getPost(id);
return success(BeanUtils.toBean(post, PostRespVO.class));
}
这段代码是用于查询岗位列表的。其中,注解@PreAuthorize("@ss.hasPermission('system:post:query')"),表明了,要想调用本方法,需要当前用户具有'system:post:query'权限。
那么,@PreAuthorize("@ss.hasPermission('system:post:query')")这段代码是如何被执行的呢?
答案位于文件YudaoSecurityAutoConfiguration.java中:
@Bean("ss") // 使用 Spring Security 的缩写,方便使用
public SecurityFrameworkService securityFrameworkService(PermissionApi permissionApi) {
return new SecurityFrameworkServiceImpl(permissionApi);
}
可以看到,名称为ss的Bean被注入进来。它是接口SecurityFrameworkService的实现。
而该接口的实现,位于SecurityFrameworkServiceImpl.java。
@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) {
return permissionApi.hasAnyPermissions(getLoginUserId(), permissions);
}
@Override
public boolean hasRole(String role) {
return hasAnyRoles(role);
}
@Override
public boolean hasAnyRoles(String... roles) {
return permissionApi.hasAnyRoles(getLoginUserId(), 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));
}
}
可以看到,其中有一个方法hasPermission,正是上文中提到的注解中的方法。
继续跟踪该方法的实现,最终会落在接口PermissionService的实现类PermissionServiceImpl上:
@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));
}
该方法的操作就很明显了,就是通过查询数据库,来判断当前的用户是否拥有对应的权限。