# 授权管理
# 授权 (能做什么?)
Authorization,也就是授权操作,指的是对来访用户是否具备某操作权限进行判断进而采取对应措施。授权解决“能做什么?”的问题。
# 逻辑
用户具有一组权限 userPerms,系统设置了权限拦截规则 :sysPerms;
系统根据权限拦截规则sysPerms中的 url,对访问系统资源的行为进行判断和拦截
拦截到之后,获取访问用户信息,然后根据用户权限 userPerms 进行比对,如果用户具备权限,则通过,否则拒绝。
更多授权模型细节,请参考:授权
# 实现权限数据服务
你的系统需要实现如下接口,为 Heimdall 框架提供权限相关数据。 通俗点说就是:你需要告诉 Heimdall,哪些系统资源需要被拦截以及如何授权,当前用户有哪些权限。
public interface AuthorizationMetaDataService {
//加载系统权限资源
Map<String, Collection<String>> loadSysAuthorities();
//加载用户权限资源
List<? extends GrantedAuthority> loadUserAuthorities(SimpleSession user);
}
参考
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class AuthorizationMetaDataServiceImpl implements AuthorizationMetaDataService {
private final SysResourceService sysResourceService;
@Override
public Map<String, Collection<String>> loadSysAuthorities() {
//////// restful 系统权限
String hql = "from SysResourceEntity where resType = 3 and uri <> ''";
final List<SysResourceEntity> objects = sysResourceService.listByHql(hql);
if (null != objects && !objects.isEmpty()) {
Map<String, Collection<String>> perms = new LinkedHashMap<>();
//实际使用的时候,需要对 url 和 perm 进行校验
for (SysResourceEntity resource : objects) {
//restful 形式下的权限、权限标志无意义,依据 method+url 进行授权
perms.put(resource.getUri(), null);
}
log.warn("加载到的系统权限: \n{}", JacksonUtils.toPrettyJson(perms));
return perms;
}
//////// restful 系统权限
return new LinkedHashMap<>();
}
@Override
public List<? extends GrantedAuthority> loadUserAuthorities(SimpleSession session) {
final SysUserDTO user1 = sysResourceService.getCurrentUser(true, true);
SysUserDTOTransfer user = new SysUserDTOTransfer();
BeanUtils.copyProperties(user1, user);
//////// restful 类型 用户权限
final List<SysResourceDTO> resources = user.getPermissions();
if (null != resources && !resources.isEmpty()) {
//构造用户 Restful 权限
List<MethodAndUrlGrantedAuthority> perms =
resources.stream().map(d -> new MethodAndUrlGrantedAuthority(d.getMethod(), d.getUri())).collect(Collectors.toList());
log.warn("加载到的用户权限: \n{}", JacksonUtils.toPrettyJson(perms));
return perms;
}
//////// restful 类型 用户权限
return new ArrayList<>();
}
}
# 权限数据服务何时被调用?
loadSysAuthorities:
权限拦截器进行isAuthorized授权判定之前
如果配置参数:sysCachedEnabled=true,会从缓存中获取系统权限,如果没有获取到,则会调用此方法从数据库获取;
如果配置参数:sysCachedEnabled=false,则每次都会调用此接口直接从数据库获取;
loadUserAuthorities:
当用户访问授权资源之前,
如果配置参数:userCachedEnabled=true,会从缓存中获取用户权限,如果没有获取到,则会调用此方法从数据库获取;
如果配置参数:userCachedEnabled=false,则每次都会调用此接口直接从数据库获取;
# 授权使用
系统支持如下形式授权:
- 基于 url 的拦截授权
- 注解授权
# 开启 url 拦截授权
以 Spring Boot 为例, 实现WebMvcConfigurer,注册PermBasedAuthorizeInterceptor拦截器
public class AbstractWebMvcConfigurer implements WebMvcConfigurer {
/**
* 认证管理器
*/
@Autowired
private AuthorizationManager authorizationManager;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截处理操作的匹配路径
//放开静态拦截
log.warn("初始化 Url 权限拦截器");
registry.addInterceptor(new PermBasedAuthorizeInterceptor(authorizationManager))
//拦截所有路径
.addPathPatterns("/**")
//排除路径
//排除静态资源拦截
.excludePathPatterns(
"/login/**",
"/logout/**",
"/current/**",
"/static/**",
"/resources/**",
"/webjars/**",
"/error/**");
}
}
同时,框架也提供了 Mvc 配置基础类:com.luter.heimdall.boot.starter.config.AbstractWebMvcConfigurer, 如果没有特殊需求,可直接扩展使用即可,如下:
@Configuration
@Slf4j
public class MvcConfig extends AbstractWebMvcConfigurer {
}
# 开启注解授权
系统实现了如下几类注解权限:
// 是否登录
@RequiresUser
//是否具备单个角色 如: @RequiresRole("admin")
@RequiresRole
//是否具备多个角色其一或者全部,如:@RequiresRoles(value={"admin","user"},mode=Mod.Any)
@RequiresRoles
//是否具备某个权限标识符,如: @RequiresPermission("catList")
@RequiresPermission
//是否具备多个权限标识符其一或者全部,如:@RequiresPermissions(value={"catList","catSave"},mode=Mod.ALL)
@RequiresPermissions
要开启注解授权,注册 权限注解 Bean 即可。如下所示:
/**
* 授权过滤器
*
* @param authenticationManager the authentication manager
* @param authorizationManager the authorization manager
* @return the authorization filter handler
*/
@Bean
public AuthorizationFilterHandler authorizationFilterHandler(AuthenticationManager authenticationManager,
AuthorizationManager authorizationManager) {
log.warn("初始化 授权过滤器");
return new DefaultAuthorizationFilterHandler(authenticationManager, authorizationManager);
}
/**
* 支持注解权限
*
* @param authorizationFilterHandler the authorization filter handler
* @return the security annotation aspect handler
*/
@Bean
public AuthorizationAnnotationAspect securityAspect(AuthorizationFilterHandler authorizationFilterHandler) {
return new AuthorizationAnnotationAspect(authorizationFilterHandler);
}
# 使用注解授权
@GetMapping("/{id}")
@RequiresPermissions(value = {"ADMIN","USER"},mode = Mod.ALL)
public ResponseEntity<ResponseVO<SysLogDTO>> getById(@PathVariable Long id) {
SysLogDTO data = sysLogService.getById(id);
return ResponseUtils.ok(data);
}
# 授权优先级
在注解授权和 url 拦截授权同时开启的情况下,如果同一个资源,如果既存在 url 拦截规则,又存在注解,则注解生效,url 拦截自动忽略。
比如: 系统资源: GET:/sys/role
对应控制器方法:
@GetMapping("/sys/role")
//存在授权注解
@RequiresRole("admin")
public String requireRole() {
return "RequiresRole(admin)";
}
这种情况下,只有@RequiresRole("admin")注解生效。PermBasedAuthorizeInterceptor不在对此资源进行授权判断。
具体逻辑,可参考源码:PermBasedAuthorizeInterceptor.java