Spring Security学习总结2_2

说了这么些,那我们到底应该如何来实现这个ObjectDefinitionSource接口呢?

首先还是说说Acegi Seucrity 1.x 版本,org.acegisecurity.intercept.weborg.acegisecurity.intercept.method 包下AbstractFilterInvocationDefinitionSourceAbstractMethodDefinitionSource 两个抽象类,这两个类分别实现了FilterInvocationDefinitionSourceMethodDefinitionSource 接口,而这两个接口都继承自ObjectDefinitionSource接口并实现了其中的方法。两个抽象类都使用方法模板模式来实现,将具体的实现方法交给了子类。

提示:
两个抽象类实现了各自接口的 getAttributes(Object object) 方法并在此方法中调用lookupAttributes
(Method method)
方法,而实际该方法在抽象类中并没有具体的实现,而是留给了子类去实现。
 

在Acegi Seucrity 1.x版本中,系统为我们提供了默认的实现,MethodDefinitionMap 类用于返回方法的权限信息,而PathBasedFilterInvocationDefinitionMap 类和RegExpBasedFilterInvocationDefinitionMap 类用于返回URL资源对应的权限信息,也就是ConfigAttributeDefinition对象,现在也许明白一点儿了吧,我们只要按照这三个类的实现方式(也就是”模仿”,从后面的代码中你可以看到)从数据库中获取用户信息和权限信息然后封装成一个ConfigAttributeDefinition 对象返回即可(其实就是一个List列表,前面已经介绍过了),相信通过Hibernate从数据库中获取一个列表应该是再容易不过的了。

回到Spring Security ,系统为我们提供的默认实现有些变化,DefaultFilterInvocationDefinitionSourceDelegatingMethodDefinitionSource 两个类,从名字也可以看出来它们分别是干什么的了。这两个类分别实现了FilterInvocationDefinitionSourceMethodDefinitionSource 接口,而这两个接口都继承自ObjectDefinitionSource接口并实现了其中的方法,这和1.x版本中一样。它们都是从配置文件中得到资源和相应权限的信息。

通过上面的介绍,你或许更名白了一些,那我们下面要做的就是实现系统的FilterInvocationDefinitionSourceMethodDefinitionSource 接口,只是数据源不是从配置文件中读取配置信息是数据库而已。

我们这里对比着 Acegi Seucrity 1.x 版本中的实现,我个人认为它更好理解,还是请你好好看看源代码。

1 自定义FilterInvocationDefinitionSource

在2.0中,系统没有在系统抽象类,所以我们还是使用1.x中的实现方式,首先通过一个抽象类来实现 ObjectDefinitionSource 接口。代码如下:

 1   public  ConfigAttributeDefinition getAttributes(Object object)
 2  
 3               throws  IllegalArgumentException {
 4  
 5           if  (object  ==   null   ||   ! ( this .supports(object.getClass()))) {
 6  
 7              thrownew IllegalArgumentException( " Object must be a FilterInvocation " );
 8  
 9          }
10  
11          String url  =  ((FilterInvocation)object).getRequestUrl();
12  
13          returnthis.lookupAttributes(url);
14  
15       }
16  
17   publicabstract ConfigAttributeDefinition lookupAttributes(String url);
18  
19   @SuppressWarnings( " unchecked " )
20  
21   publicabstract Collection getConfigAttributeDefinitions();
22  
23   @SuppressWarnings( " unchecked " )
24  
25   publicboolean supports(Class clazz) {
26  
27        return  FilterInvocation. class .isAssignableFrom(clazz);
28  
29   }

这段代码你也可以在1.0中找到,getAttributes 方法的入口参数是一个Object对象,这是由系统传给我们的,因为是URL资源的请求,所有可以将这个Object对象强制转换为 FilterInvocation 对象,并通过调用它的getRequestUrl() 方法来获取用户当前请求的URL地址,然后调用子类需要实现的lookupAttributes 方法并将该URL地址作为参数传给该方法,下面是具体的实现类DataBaseFilterInvocationDefinitionSource 类的代码,也就是我们需要实现抽象父类的lookupAttributes方法:

 

  1   @Override
  2  
  3   public  ConfigAttributeDefinition lookupAttributes(String url) {
  4  
  5           //  TODO Auto-generated method stub
  6  
  7           // 初始化数据,从数据库读取
  8  
  9       cacheManager.initResourceInCache();
 10  
 11        if  (isUseAntPath()) {
 12  
 13           int  firstQuestionMarkIndex  =  url.lastIndexOf( " ? " );
 14  
 15           if  (firstQuestionMarkIndex  !=   - 1 ) {
 16  
 17              url  =  url.substring( 0 , firstQuestionMarkIndex);
 18  
 19          }
 20  
 21       }
 22  
 23        // 将URL在比较前都转换为小写
 24  
 25        if  (isConvertUrlToLowercaseBeforeComprison()) {
 26  
 27          url  =  url.toLowerCase();
 28  
 29       }
 30  
 31        // 获取所有的URL
 32  
 33       List < String >  urls  =  cacheManager.getUrlResources();
 34  
 35        // 倒叙排序--如果不进行排序,如果用户使用浏览器的导航工具访问页面可能出现问题
 36  
 37        // 例如:访问被拒绝后用户刷新页面
 38  
 39       Collections.sort(urls);
 40  
 41       Collections.reverse(urls);
 42  
 43       GrantedAuthority[] authorities  =   new  GrantedAuthority[ 0 ];
 44  
 45        // 将请求的URL与配置的URL资源进行匹配,并将正确匹配的URL资源对应的权限
 46  
 47        // 取出
 48  
 49        for  (String resourceName_url : urls) {
 50  
 51           boolean  matched  =   false ;
 52  
 53           // 使用ant匹配URL
 54  
 55           if  (isUseAntPath()) {
 56  
 57              matched  =  pathMatcher.match(resourceName_url, url);
 58  
 59          }  else  { // perl5编译URL
 60  
 61              Pattern compliedPattern  =   null ;
 62  
 63              Perl5Compiler compiler  =   new  Perl5Compiler();
 64  
 65               try  {
 66  
 67                 compliedPattern  =  compiler.compile(resourceName_url, Perl5Compiler.READ_ONLY_MASK);
 68  
 69              }  catch  (MalformedPatternException e) {
 70  
 71                 e.printStackTrace();
 72  
 73              }
 74  
 75              matched  =  matcher.matches(url, compliedPattern);
 76  
 77          }
 78  
 79           // 匹配正确,获取响应权限
 80  
 81           if  (matched) {
 82  
 83               // 获取正确匹配URL资源对应的权限
 84  
 85              ResourcDetail detail  =  cacheManager.getResourcDetailFromCache(resourceName_url);
 86  
 87              authorities  =  detail.getAuthorities();
 88  
 89               break ;
 90  
 91          }
 92  
 93   }
 94  
 95           // 将权限封装成ConfigAttributeDefinition对象返回(使用ConfigAttributeEditor)
 96  
 97           if  (authorities.length  >   0 ) {
 98  
 99              String authTemp  =   "" ;
100  
101               for  (GrantedAuthority grantedAuthority : authorities) {
102  
103                 authTemp  +=  grantedAuthority.getAuthority()  +   " , " ;
104  
105              }
106  
107              String authority  =  authTemp.substring( 0 , (authTemp.length()  -   1 ));
108  
109              System.out.println(authority);
110  
111              ConfigAttributeEditor attributeEditor  =   new  ConfigAttributeEditor();
112  
113              attributeEditor.setAsText(authority.trim());
114  
115               return  (ConfigAttributeDefinition)attributeEditor.getValue();
116  
117          }
118  
119          returnnull;
120  
121    }

我们这里同样使用了缓存,它参考自系统的UseCache接口的实现,这里不在介绍,你可以查看本例的源代码和系统的实现和本例的配置文件。这里将用户请求的URL地址与从数据库中获取的受保护的URL资源使用 ant 和perl5匹配(这取决与你的配置),如果匹配成功则从缓存中获取访问该资源需要的权限信息,并将其封装成ConfigAttributeDefinition 对象返回,这里使用org.springframework.security.ConfigAttributeEditor 类,该类提供了一个setAsText(String s) ,该方法收取一个字符串作为参数,在该方法中创建ConfigAttributeDefinition 对象并将字符串参数传递给ConfigAttributeDefinition类的构造函数 来初始化该对象。详细的实现还是请你看源代码。现在我们在配置文件添加自己的实现,如下:

1   < bean  id ="objectDefinitionSource"
2       class ="org.security.intercept.web.DataBaseFilterInvocationDefinitionSource"
3        p:convertUrlToLowercaseBeforeComprison ="true"
4        p:useAntPath ="true"
5        p:cacheManager-ref ="securityCacheManager" />

    convertUrlToLowercaseBeforeComprison 属性 定义了在匹配之前将URL都转换为小写,useAntPath属性 定义使用Ant方式匹配URL,cacheManager 属性定义了指向另一个Bean的引用,我们使用它从缓存中获取相应的信息。

 

2 自定义 MethodDefinitionSource

将方法资源存放在数据库中的实现与 URL资源类似,这里不在累述,下面是 DataBaseMethodInvocationDefinitionSource 的 源代码,读者可以参考注释进行阅读(该类也是继承自一个自定义的抽象类 AbstractMethodDefinitionSource ):

 1   public  ConfigAttributeDefinition lookupAttributes(Method method, Class targetClass) {
 2  
 3           //  TODO Auto-generated method stub
 4  
 5           // 初始化资源并缓存
 6  
 7          securityCacheManager.initResourceInCache();
 8  
 9           // 获取所有方法资源
10  
11          List < String >  methods  =  securityCacheManager.getMethodResources();
12  
13           // 权限集合
14  
15          Set < GrantedAuthority >  authSet  =   new  HashSet < GrantedAuthority > ();
16  
17           // 遍历方法资源,并获取匹配的资源名称,然后从缓存中获取匹配正确
18  
19           // 的资源对应的权限(ResourcDetail对象的GrantedAuthority[]对象数据)
20  
21           for  (String resourceName_method : methods) {
22  
23               if  (isMatch(targetClass, method, resourceName_method)) {
24  
25                 ResourcDetail detail  =  securityCacheManager.getResourcDetailFromCache(resourceName_method);
26  
27                  if  (detail  ==   null ) {
28  
29                      break ;
30  
31                 }
32  
33                 GrantedAuthority[] authorities  =  detail.getAuthorities();
34  
35                  if  (authorities  ==   null   ||  authorities.length  ==   0 ) {
36  
37                      break ;
38  
39                 }
40  
41                 authSet.addAll(Arrays.asList(authorities));
42  
43              }
44  
45          }
46  
47           if  (authSet.size()  >   0 ) {
48  
49              String authString  =   "" ;
50  
51               for  (GrantedAuthority grantedAuthority : authSet) {
52  
53                 authString  +=  grantedAuthority.getAuthority()  +   " , " ;
54  
55              }
56  
57              String authority  =  authString.substring( 0 , (authString.length()  -   1 ));
58  
59              System.out.println( " >>>>>>>>>>>>>>> "   +  authority);
60  
61              ConfigAttributeEditor attributeEditor  =   new  ConfigAttributeEditor();
62  
63              attributeEditor.setAsText(authority.trim());
64  
65               return  (ConfigAttributeDefinition)attributeEditor.getValue();
66  
67          }
68  
69          returnnull;
70  
71   }

    isMatch 方法用于 对用户当前调用的方法与受保护的方法进行匹配,与URL资源类似,请参考代码。下面是applicationContext-security.xml 文件中的配置,请查看该配置文件。

1   < bean  id ="methodDefinitionSource"
2      class ="org.security.intercept.method.DataBaseMethodInvocationDefinitionSource"
3       p:securityCacheManager-ref ="securityCacheManager" />

securityCacheManager 属性定义了指向另一个Bean的引用,我们使用它从缓存中获取相应的信息。这个Bean和前一节中介绍的一样。只是这里我们获取的是方法保护定义资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值