之前看的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对应的记录
完成后清空对应的缓存