之前看的hs-web中demo里的hsweb-starter.js
脚本有初始化系统菜单表(s_menu)、权限设置表(s_autz_setting)、权限设置菜单表(s_autz_menu)、用户表(s_user)中的数据,但是对权限表(s_permission)却没有初始化的脚本,但启动后在权限表中却有数据,具体是在哪里初始化的呢?
其实之前在分析AopAuthorizingController
类时就已经有了,只不过没有注意。
AopAuthorizingController
类的matches
方法
public boolean matches(Method method, Class<?> aClass) {
boolean support = AopUtils.findAnnotation(aClass, Controller.class) != null
|| AopUtils.findAnnotation(aClass, RestController.class) != null
|| AopUtils.findAnnotation(aClass, method, Authorize.class) != null;
if (support && autoParse) {
defaultParser.parse(aClass, method);
}
return support;
}
其中的autoParse
变量是在声明的时候注入的
AopAuthorizeAutoConfiguration
中对AopAuthorizingController
的声明部分
@Bean
@ConfigurationProperties(prefix = "hsweb.authorize")
public AopAuthorizingController aopAuthorizingController(AuthorizingHandler authorizingHandler,
AopMethodAuthorizeDefinitionParser aopMethodAuthorizeDefinitionParser) {
return new AopAuthorizingController(authorizingHandler, aopMethodAuthorizeDefinitionParser);
}
ConfigurationProperties
注解的注释如下:
Annotation for externalized configuration. Add this to a class definition or a @Bean method in a @Configuration class if you want to bind and validate some external Properties (e.g. from a .properties file).
Note that contrary to @Value, SpEL expressions are not evaluated since property values are externalized.
大概意思是如果想要绑定和验证一些外部属性文件,则将该注解加到注解了 @Configuration
的类定义或@Bean
方法上。
再说matches
的方法中调用的parse
方法
public AuthorizeDefinition parse(Class target, Method method, MethodInterceptorContext context) {
if (excludeMethodName.contains(method.getName())) {
return null;
}
CacheKey key = buildCacheKey(target, method);
AuthorizeDefinition definition = cache.get(key);
if (definition instanceof EmptyAuthorizeDefinition) {
return null;
}
if (null != definition) {
return definition;
}
//使用自定义
if (!CollectionUtils.isEmpty(parserCustomizers)) {
definition = parserCustomizers.stream()
.map(customizer -> customizer.parse(target, method, context))
.filter(Objects::nonNull)
.findAny().orElse(null);
if (definition instanceof EmptyAuthorizeDefinition) {
return null;
}
if (definition != null) {
return definition;
}
}
Authorize classAuth = AopUtils.findAnnotation(target, Authorize.class);
Authorize methodAuth = AopUtils.findMethodAnnotation(target, method, Authorize.class);
RequiresDataAccess classDataAccess = AopUtils.findAnnotation(target, RequiresDataAccess.class);
RequiresDataAccess methodDataAccess = AopUtils.findMethodAnnotation(target, method, RequiresDataAccess.class);
RequiresExpression expression = AopUtils.findAnnotation(target, RequiresExpression.class);
if (classAuth == null && methodAuth == null && classDataAccess == null && methodDataAccess == null && expression == null) {
cache.put(key, EmptyAuthorizeDefinition.instance);
return null;
}
if ((methodAuth != null && methodAuth.ignore()) || (classAuth != null && classAuth.ignore())) {
cache.put(key, EmptyAuthorizeDefinition.instance);
return null;
}
synchronized (cache) {
DefaultBasicAuthorizeDefinition authorizeDefinition = new DefaultBasicAuthorizeDefinition();
authorizeDefinition.setTargetClass(target);
authorizeDefinition.setTargetMethod(method);
if (methodAuth == null || methodAuth.merge()) {
authorizeDefinition.put(classAuth);
}
authorizeDefinition.put(methodAuth);
authorizeDefinition.put(expression);
authorizeDefinition.put(classDataAccess);
authorizeDefinition.put(methodDataAccess);
if (authorizeDefinition.getPermissionDescription().length == 0) {
if (classAuth != null) {
authorizeDefinition.put(classAuth.dataAccess());
String[] desc = classAuth.description();
if (desc.length > 0) {
authorizeDefinition.setPermissionDescription(desc);
}
}
}
if (authorizeDefinition.getActionDescription().length == 0) {
if (methodAuth != null) {
if (methodAuth.description().length != 0) {
authorizeDefinition.setActionDescription(methodAuth.description());
}
}
}
log.info("parsed authorizeDefinition {}.{} => {}.{} permission:{} actions:{}",
target.getSimpleName(),
method.getName(),
authorizeDefinition.getPermissionDescription(),
authorizeDefinition.getActionDescription(),
authorizeDefinition.getPermissions(),
authorizeDefinition.getActions());
cache.put(key, authorizeDefinition);
return authorizeDefinition;
}
}
如果缓存中存在,则直接返回,避免重复解析
判断是否有自定义的AopMethodAuthorizeDefinitionCustomizerParser
类,如果有,则使用自定义的类解析并返回AuthorizeDefinition
获取类的Authorize
注解、方法的Authorize
注解、类的RequiresDataAccess
注解、方法的RequiresDataAccess
注解、类的RequiresExpression
注解。如果都没有,则默认在缓存中保存EmptyAuthorizeDefinition
。如果方法有注解,但ignore
为true或者类有注解,但ignore
为true,也在缓存中保存EmptyAuthorizeDefinition
同步cache
,设置DefaultBasicAuthorizeDefinition
实例authorizeDefinition
的属性。多次调用DefaultBasicAuthorizeDefinition
的put
方法,只是在类鉴权注解时只有在方法鉴权注解为空或者merge
为true(需要合并类的注解信息)时才会调用。
如果DefaultBasicAuthorizeDefinition
的permissionDescription
为空,如果类权限注解不为空,则调用DefaultBasicAuthorizeDefinition
的put
方法,传入类注解的dataAccess
值。如果类注解的description
为空,则设置DefaultBasicAuthorizeDefinition
的permissionDescription
如果DefaultBasicAuthorizeDefinition
的actionDescription
为空,如果方法权限注解不为空,则设置DefaultBasicAuthorizeDefinition
的actionDescription
加入缓存后返回authorizeDefinition
DefaultBasicAuthorizeDefinition
类的put
方法代码如下,其实就是解析不同的注解,将对应的属性保存下来
public void put(Authorize authorize) {
if (null == authorize || authorize.ignore()) {
return;
}
permissions.addAll(Arrays.asList(authorize.permission()));
actions.addAll(Arrays.asList(authorize.action()));
roles.addAll(Arrays.asList(authorize.role()));
user.addAll(Arrays.asList(authorize.user()));
if (authorize.logical() != Logical.DEFAULT) {
logical = authorize.logical();
}
message = authorize.message();
phased = authorize.phased();
put(authorize.dataAccess());
}
public void put(RequiresExpression expression) {
if (null == expression) {
return;
}
script = new DefaultScript(expression.language(), expression.value());
}
public void put(RequiresDataAccess dataAccess) {
if (null == dataAccess || dataAccess.ignore()) {
return;
}
if (!"".equals(dataAccess.permission())) {
permissions.add(dataAccess.permission());
}
actions.addAll(Arrays.asList(dataAccess.action()));
DefaultDataAccessDefinition definition = new DefaultDataAccessDefinition();
definition.setEntityType(dataAccess.entityType());
definition.setPhased(dataAccess.phased());
if (!"".equals(dataAccess.controllerBeanName())) {
definition.setController(dataAccess.controllerBeanName());
} else if (DataAccessController.class != dataAccess.controllerClass()) {
definition.setController(dataAccess.getClass().getName());
}
dataAccessDefinition = definition;
dataAccessControl = true;
}
完成对Authorize
注解的解析后,在AopAuthorizingController
的run
方法中发布AuthorizeDefinitionInitializedEvent
事件,将解析得到的AuthorizeDefinition
作为参数传入。
AutoSyncPermission
类监听了该事件,如下是代码
public void onApplicationEvent(AuthorizeDefinitionInitializedEvent event) {
List<AuthorizeDefinition> definitions = event.getAllDefinition();
Map<String, List<AuthorizeDefinition>> grouping = new HashMap<>();
//以permissionId分组
for (AuthorizeDefinition definition : definitions) {
for (String permissionId : definition.getPermissions()) {
grouping.computeIfAbsent(permissionId, id -> new ArrayList<>())
.add(definition);
}
}
//创建权限实体
Map<String, PermissionEntity> waitToSyncPermissions = new HashMap<>();
for (Map.Entry<String, List<AuthorizeDefinition>> permissionDefinition : grouping.entrySet()) {
String permissionId = permissionDefinition.getKey();
//一个权限的全部定义(一个permission多个action)
List<AuthorizeDefinition> allDefinition = permissionDefinition.getValue();
if (allDefinition.isEmpty()) {
return;
}
AuthorizeDefinition tmp = allDefinition.get(0);
//action描述
List<String> actionDescription = allDefinition.stream()
.map(AuthorizeDefinition::getActionDescription)
.flatMap(Stream::of)
.collect(Collectors.toList());
//action
List<String> actions = allDefinition
.stream()
.map(AuthorizeDefinition::getActions)
.flatMap(Collection::stream)
.collect(Collectors.toList());
//创建action实体
Set<ActionEntity> actionEntities = new HashSet<>(actions.size());
if (!actions.isEmpty()) {
for (int i = 0; i < actions.size(); i++) {
String action = actions.get(i);
String desc = actionDescription.size() > i ? actionDescription.get(i) : actionDescMapping.getOrDefault(actions.get(i), action);
ActionEntity actionEntity = new ActionEntity();
actionEntity.setAction(action);
actionEntity.setDescribe(desc);
actionEntities.add(actionEntity);
}
}
//创建permission
PermissionEntity entity = entityFactory.newInstance(PermissionEntity.class);
if (tmp instanceof AopAuthorizeDefinition) {
AopAuthorizeDefinition aopAuthorizeDefinition = ((AopAuthorizeDefinition) tmp);
Class type = aopAuthorizeDefinition.getTargetClass();
Class genType = entityFactory.getInstanceType(ClassUtils.getGenericType(type));
List<OptionalField> optionalFields = new ArrayList<>();
entity.setOptionalFields(optionalFields);
if (genType != Object.class) {
List<Field> fields = new ArrayList<>();
ReflectionUtils.doWithFields(genType, fields::add, field -> (field.getModifiers() & Modifier.STATIC) == 0);
for (Field field : fields) {
if ("id".equals(field.getName())) {
continue;
}
ApiModelProperty property = field.getAnnotation(ApiModelProperty.class);
OptionalField optionalField = new OptionalField();
optionalField.setName(field.getName());
if (null != property) {
if (property.hidden()) {
continue;
}
optionalField.setDescribe(property.value());
}
optionalFields.add(optionalField);
}
}
}
entity.setId(permissionId);
entity.setName(tmp.getPermissionDescription().length > 0 ? tmp.getPermissionDescription()[0] : permissionId);
entity.setActions(new ArrayList<>(actionEntities));
entity.setType("default");
entity.setStatus(DataStatus.STATUS_ENABLED);
waitToSyncPermissions.putIfAbsent(entity.getId(), entity);
}
//查询出全部旧的权限数据并载入缓存
Map<String, PermissionEntity> oldCache = permissionService
.select()
.stream()
.collect(Collectors.toMap(PermissionEntity::getId, Function.identity()));
waitToSyncPermissions.forEach((permissionId, permission) -> {
log.info("try sync permission[{}].{}", permissionId, permission.getActions());
PermissionEntity oldPermission = oldCache.get(permissionId);
if (oldPermission == null) {
permissionService.insert(permission);
} else {
Set<ActionEntity> oldAction = new HashSet<>();
if (oldPermission.getActions() != null) {
oldAction.addAll(oldPermission.getActions());
}
Map<String, ActionEntity> actionCache = oldAction
.stream()
.collect(Collectors.toMap(ActionEntity::getAction, Function.identity()));
boolean permissionChanged = false;
for (ActionEntity actionEntity : permission.getActions()) {
//添加新的action到旧的action
if (actionCache.get(actionEntity.getAction()) == null) {
oldAction.add(actionEntity);
permissionChanged = true;
}
}
if (permissionChanged) {
oldPermission.setActions(new ArrayList<>(oldAction));
permissionService.updateByPk(oldPermission.getId(), oldPermission);
}
actionCache.clear();
}
});
oldCache.clear();
waitToSyncPermissions.clear();
definitions.clear();
grouping.clear();
}
将所有之前解析出的AuthorizeDefinition
以permissionId
分组
循环分组
获取每个permissionId
对应的所有action(因为一个permissionId
对应多个action
),获取所有action描述,获取所有action。循环action创建ActionEntity
并保存
使用entityFactory
的 newInstance
方法创建PermissionEntity
的实例。因为PermissionEntity
是接口,所以默认会创建SimplePermissionEntity
类的实例。
判断第一个AuthorizeDefinition
,如果为AopAuthorizeDefinition
(默认都是),则获取targetClass
(也就是各个controller)的第一个泛型类(即实体类型,参见hs-web中的controller结构)
如果不是Object
类,则通过反射获取该类所有的非static字段。循环这些字段,如果非id
,则获取字段的ApiModelProperty
注解并构造OptionalField
,并将OptionalField
设置到PermissionEntity
的实例中
设置PermissionEntity
实例的其它属性
将对象保存到以permissionId
为key,PermissionEntity
为value的HashMap
中,实例名为waitToSyncPermissions
将全部之前数据库中的权限数据查出,并以permissionId
为key进行分组,保存在Map
的实例oldCache
中
循环之前设置的waitToSyncPermissions
如果oldCache
中不存在以permissionId
为key的值,则说明是新增的权限,直接插入数据库
否则,判断旧的action中是否存在新的permission
中对应的action。如果不存在,则将不存在的action增加到旧的action列表中,并设置permissionChanged
为true。
如果permissionChanged
为true,则更新permission对应的记录
完成后清空对应的缓存