- spring security 安全框架都是通过xml配置文件在容器启动时把资源和角色之间的许可信息加载到内存中,可往往我们需要通过在数据库中配置资源和角色的许可管理来实现动态授权,下面介绍一些通过修改springsecurity源代码的方法来实现动态授权。
- 首先下载spring security的源代码找到org.springframework.security.intercept.web.FilterSecurityInterceptor类,找到obtainObjectDefinitionSource方法,我们就是要通过修改该方法实现动态授权
- private static FilterInvocationDefinitionSource s = null;
- private ScRoleResDAO scRoleResDAO;
- private ScResourceDAO scResourceDAO;
- public ObjectDefinitionSource obtainObjectDefinitionSource() {
- if (s == null){
- UrlMatcher urlMatcher = new AntUrlPathMatcher(true);
- LinkedHashMap requestMap = new LinkedHashMap();
- // ApplicationContext context = new ClassPathXmlApplicationContext(
- // "spring/dataAccess.xml"
- // );
- // ScRoleResDAO scRoleResDAO = (ScRoleResDAO)context.getBean("ScRoleResDAO");
- // ScResourceDAO scResourceDAO = (ScResourceDAO)context.getBean("ScResourceDAO");
- /* 一下这段代码可以忽视,因为每个人的实现方式都不同,
- * 这段代码主要是从数据库中取得资源和角色信息的,
- * 大家取出来的信息格式可能多种多样,关键是要把这些信息整理成统一的格式
- * 参照下面第二段标注为关键代码的部分*/
- Map<ScResource, List<ScRoleRes>> map = new TreeMap<ScResource, List<ScRoleRes>>();
- ScResourceExample e = new ScResourceExample();
- e.setOrderByClause("ORDER_NUM");
- List<ScResource> resList = scResourceDAO.selectByExample(e);
- ScRoleResExample example = null;
- String url = "";
- List<ScRoleRes> list = null;
- for (ScResource resource : resList) {
- url = resource.getUrl();
- example = new ScRoleResExample();
- example.createCriteria().andUrlEqualTo(url);
- list = scRoleResDAO.selectByExample(example);
- if (list != null){
- map.put(resource, list);
- }
- }
- /**************** 以下是关键代码 *********************/
- /*
- * 这段代码的重点是生成 requestMap
- * requestMap 的 key 的类型为 RequestKey ,需要包含资源的url
- * requestMap 的 value 的类型为 ConfigAttributeDefinition ,它需要包含该资源可访问的角色列表信息
- * 最后生成 DefaultFilterInvocationDefinitionSource 返回就可以了,
- * 当然需要把这个结果保存在内存中,要不然画面上每次刷新都要从数据库中取得信息,资源消耗太大了
- * 提供一个刷内存的方法,当数据库中的权限信息改变时要刷新内存中的旧的信息*/
- Iterator iterator = map.entrySet().iterator();
- ScResource key = null;
- List<ScRoleRes> list1 = null;
- List<ConfigAttribute> roles = null;
- while (iterator.hasNext()) {
- Map.Entry entry = (Map.Entry) iterator.next();
- key = (ScResource)entry.getKey();
- RequestKey reqKey = new RequestKey(key.getUrl());
- list1 = (List<ScRoleRes>)entry.getValue();
- roles = new ArrayList<ConfigAttribute>();
- for (ScRoleRes res : list1) {
- roles.add(new SecurityConfig(res.getRole()));
- }
- requestMap.put(reqKey, new ConfigAttributeDefinition(roles));
- }
- s = new DefaultFilterInvocationDefinitionSource(urlMatcher,requestMap);
- /**************** 以上是关键代码 *********************/
- }
- return s;
- }
- /**
- * 提供一个刷新内存的静态方法
- */
- public static void refresh() {
- s = null;
- 修改启动加载的xml文件,如果此文件是通过命名空间配置的,需要加入
- <beans:bean id="_filterSecurityInterceptor"
- class="org.springframework.security.intercept.web.FilterSecurityInterceptor"
- p:authenticationManager-ref="_authenticationManager"
- p:accessDecisionManager-ref="accessDecisionManager">
- <beans:property name="objectDefinitionSource">
- <beans:value><![CDATA[
- CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
- PATTERN_TYPE_APACHE_ANT
- /**=ROLE_SUPERVISOR
- ]]></beans:value>
- </beans:property>
- <beans:property name="scRoleResDAO" ref="ScRoleResDAO"/>
- <beans:property name="scResourceDAO" ref="ScResourceDAO"/>
- </beans:bean>
- 其中objectDefinitionSource并没有实际意义,因为我们已经修改了源代码,如果不想注入这个,把FilterSecurityInterceptor类中的objectDefinitionSource变量去掉应该就可以了,不过没有试过
- 另外两个DAO是我根据我的代码注入的,大家换成自己的DAO就可以了。