Spring Security学习总结2_2

本文介绍如何自定义实现Spring Security中的FilterInvocationDefinitionSource和MethodDefinitionSource接口,以支持从数据库中加载资源及其所需的权限。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

说了这么些,那我们到底应该如何来实现这个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、付费专栏及课程。

余额充值