第 39 章 动态资源管理

之前在第 5 章 使用数据库管理资源中,我们简要讨论过使用数据库管理资源,为了使手册开始的部分保持简洁,我们没有再深入讨论这个话题,包括实例中存在的一些问题也都没有解决,这一章中,我们会尝试进行更深层次的讨论。

39.1. 基本知识

对应的数据库结构与ER图,可以参考第 5 章 使用数据库管理资源

拦截器与所需的权限配置数据格式,可以参考第 36 章 拦截器

所有,我们需要做的就是把数据库中的数据读取出来,组装成拦截器所需的格式,然后把权限配置数据放到拦截器里。

39.2. 读取资源

为了区分URL和METHOD,我们在resc表中使用res_type字段来区分这两种不同的被保护资源。

res_type="URL"对应将在FilterSecurityInterceptor中使用的被保护网址。

INSERT INTO RESC VALUES(1,'','URL','/admin.jsp',1,'')
        

这里将/admin.jsp作为一个网址进行保护,随后它将被设置到FilterSecurityInterceptor中。

res_type="METHOD"对应将在MethodSecurityInterceptor中使用的被保护方法。

INSERT INFO RESC VALUES(3,'','METHOD','com.family168.springsecuritybook.ch207.MessageService.adminMessage',3,'');
        

这里将com.family168.springsecuriytbook.ch207.Message的adminMessage()方法设置为被保护资源,随后它将被设置到MethodSecurityInterceptor中。

我们使用如下sql语句从数据库中分别读取被保护的url和method信息。

读取被保护url信息。

  select re.res_string,r.name
    from role r
    join resc_role rr
      on r.id=rr.role_id
    join resc re
      on re.id=rr.resc_id
   where re.res_type='URL'
order by re.priority
        

读取被保护method信息。

  select re.res_string,r.name
    from role r
    join resc_role rr
      on r.id=rr.role_id
    join resc re
      on re.id=rr.resc_id
   where re.res_type='METHOD'
order by re.priority

        

为了实现资源的统一配置,我们创建了名为ResourceDetailsMonitor的类用来管理数据库中的被保护资源信息,它负责从数据库中读取原始信息,并转换成FilterSecurityInterceptor和MethodInterceptor所需的数据格式。

39.3. URL资源扩展点

为了动态设置FilterSecurityInterceptor中的资源配置,ResouceDetailsMonitor中直接将组装后的FilterInvocationDefinitionSource使用setObjectDefinitionSource()方法设置到FilterSecurityInterceptor中。

FilterInvocationSecurityMetadataSource source = resourceDetailsBuilder
	.createUrlSource(queryUrl);
filterSecurityInterceptor.setSecurityMetadataSource(source);
        

之后,FilterSecurityInterceptor就会根据我们设置的资源信息控制用户可以访问哪些资源。

39.4. METHOD资源扩展点

MethodSecurityInterceptor的情况有些复杂,因为涉及到spring中aop的pointcut部分特性,所以直接为MethodSecurityInterceptor设置objectDefinitionSource是不会起作用的。

我们需要获取delegatingMethodDefinitionSource,将数据库中读取的资源信息设置到它里面才能使MethodSecurityInterceptor和动态生成的pointcut都是用我们最新的资源信息。

MethodSecurityMetadataSource source = resourceDetailsBuilder
	.createMethodSource(queryMethod);
List<MethodSecurityMetadataSource> sources = new ArrayList<MethodSecurityMetadataSource>();
sources.add(source);

List<MethodSecurityMetadataSource> methodSecurityMetadataSources =
	delegatingMethodSecurityMetadataSource
	.getMethodSecurityMetadataSources();
methodSecurityMetadataSources.clear();
methodSecurityMetadataSources.addAll(sources);

try {
	Map attributeCache = (Map) ReflectUtils.getFieldValue(delegatingMethodSecurityMetadataSource,
			"attributeCache");
	attributeCache.clear();
} catch (Exception ex) {
	logger.error(ex.getMessage(), ex);
}
        

因为ACL实际上也是借助于MethodSecurityInterceptor来实现的,所以可以将ACL_READ和AFTER_ACL_READ配置在res_type="METHOD"的资源中。

实例在ch207中。