zoukankan      html  css  js  c++  java
  • SpringSecurity3.2.5自己定义角色及权限的教程

    近期阴差阳错的搞上了SpringSecurity3。由于是自己做的小系统。中间遇到了非常多坑,基本每一个坑都踩过了,网上也查了不少资料,发现有不少错误的。更是让我绕了一圈又一圈,如今把一些主要的东西总结一下。

    先从总体上总结一下为什么使用SS,一般的,在不使用ss的情况下,我们基本会在每一个业务方法运行前。插入一段用于验证权限的代码。从而推断当前用户是否有对应权限进行操作。这样做就会让业务方法和验证权限有了一个紧密的耦合。假设使用ss,我们就能够通过注解或者XML配置方式取代权限验证,使得业务和权限代码彻底分离,通过下图能够更形象的理解:


    眼下。权限管理採用最多的技术都是基于角色訪问控制技术RBAC(Role Based Access Control)。一般来说,提供例如以下功能:1,角色管理界面,由用户定义角色,给角色赋权限。2。用户角色管理界面,由用户给系统用户赋予角色。什么是RBAC。说究竟事实上就是五张表,权限表-权限角色相应表-角色表-角色用户相应表-用户表,比較常见。可是ss3默认支持的并非这样的模式,而是通过XML配置角色及用户的方式实现的权限验证等操作,所以须要我们去实现SS中一些接口。让其支持RBAC。以下開始搭建一套支持RBAC技术的SS框架:

    (1)数据库相关表格:

    1.用户表Users

        CREATE TABLE `users` (

           `password` varchar(255) default NULL,
           `username` varchar(255) default NULL,
           `uid` int(11) NOT NULL auto_increment,
           PRIMARY KEY  (`uid`)
        )

     

       2.角色表Roles

       CREATE TABLE `roles` (
         `rolename` varchar(255) default NULL,
         `rid` int(11) NOT NULL auto_increment,
         PRIMARY KEY  (`rid`)
       )

     

       3 用户_角色表users_roles

       CREATE TABLE `users_roles` (

         --用户表的外键
         `uid` int(11) default NULL,

         --角色表的外键
         `rid` int(11) default NULL,
         `urid` int(11) ,
         PRIMARY KEY  (`urid`),
       )

     

       4.资源表resources

       CREATE TABLE `resources` (

         -- 权限所相应的url地址
         `url` varchar(255) default NULL,

         --权限所相应的编码,例201代表发表文章
         `resourcename` varchar(255) default NULL,
         `rsid` int(11) ,
         PRIMARY KEY  (`rsid`)
       )

     

       5.角色_资源表roles_resources

        CREATE TABLE `roles_resources` (
          `rsid` int(11) default NULL,
          `rid` int(11) default NULL,
          `rrid` int(11) NOT NULL ,
          PRIMARY KEY  (`rrid`),
          )


    (2)在继续配置前,须要知道ss是怎样通过权限验证的。实际上ss通过拦截器,拦截发来的请求,对其进行验证的。而详细验证的方式则是通过我们实现相关接口的方法来进行的。

    既然是拦截器,web.xml势必是优先配置的。

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <!DOCTYPE web-app PUBLIC  
    2.  "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"  
    3.  "http://java.sun.com/dtd/web-app_2_3.dtd" >  
    4.   
    5.   
    6. <web-app>  
    7.   <display-name>Archetype Created Web Application</display-name>  
    8.       <!-- Spring Security配置 -->  
    9.       <filter>    
    10.         <filter-name>springSecurityFilterChain</filter-name>    
    11.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>    
    12.       </filter>    
    13.           
    14.       <filter-mapping>    
    15.         <filter-name>springSecurityFilterChain</filter-name>    
    16.         <url-pattern>/*</url-pattern>    
    17.       </filter-mapping>    
    18.      <!-- Spring MVC配置 -->  
    19.     <servlet>  
    20.         <servlet-name>spring</servlet-name>  
    21.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    22.          <init-param>  
    23.             <param-name>contextConfigLocation</param-name>  
    24.             <param-value>classpath:spring-mvc.xml</param-value>  
    25.         </init-param>  
    26.         <load-on-startup>1</load-on-startup>  
    27.     </servlet>  
    28.        
    29.     <servlet-mapping>  
    30.         <servlet-name>spring</servlet-name>  
    31.         <url-pattern>*.do</url-pattern>  
    32.     </servlet-mapping>  
    33.        
    34.        
    35.     <!-- Spring配置 -->  
    36.     <listener>  
    37.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    38.     </listener>  
    39.     <listener>  
    40.         <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
    41.     </listener>  
    42.        
    43.     <!-- 指定Spring Bean的配置文件所在文件夹。默认配置在WEB-INF文件夹下 -->  
    44.     <context-param>  
    45.         <param-name>contextConfigLocation</param-name>  
    46.         <param-value>classpath:applicationContext*.xml,classpath:spring-mybatis.xml</param-value>  
    47.     </context-param>  
    48.     <filter>  
    49.         <filter-name>encodingFilter</filter-name>  
    50.         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
    51.         <init-param>  
    52.             <param-name>encoding</param-name>  
    53.             <param-value>UTF-8</param-value>  
    54.         </init-param>  
    55.     </filter>  
    56.       
    57.      <!-- Spring 刷新Introspector防止内存泄露 -->    
    58.     <listener>    
    59.         <listener-class>    
    60.             org.springframework.web.util.IntrospectorCleanupListener    
    61.         </listener-class>    
    62.     </listener>    
    63.         
    64.     <!--  获取Spring Security session的生命周期-->    
    65.     <listener>    
    66.         <listener-class>    
    67.             org.springframework.security.web.session.HttpSessionEventPublisher     
    68.         </listener-class>    
    69.     </listener>    
    70.     
    71.     <!-- session超时定义,单位为分钟 -->    
    72.     <session-config>    
    73.         <session-timeout>20</session-timeout>    
    74.     </session-config>    
    75. </web-app>  

    接下来是spring security3的一些配置,详细的每个是什么意思。网上非常多资料。这里不赘述了。

    总之。须要依据自己的需求,进行对应的改动。


    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="UTF-8"?

      >  

    2. <beans:beans xmlns="http://www.springframework.org/schema/security"    
    3.        xmlns:beans="http://www.springframework.org/schema/beans"  
    4.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    5.        xmlns:aop="http://www.springframework.org/schema/aop"   
    6.        xmlns:tx="http://www.springframework.org/schema/tx"   
    7.        xmlns:context="http://www.springframework.org/schema/context"   
    8.        xmlns:mvc="http://www.springframework.org/schema/mvc"   
    9.        xsi:schemaLocation="  
    10.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd   
    11.        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd   
    12.        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd   
    13.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd  
    14.        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd  
    15.        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">  
    16.    <http pattern="/css/**" security="none"></http>     
    17.    <http pattern="/images/**" security="none"></http>     
    18.    <http pattern="/img/**" security="none"></http>     
    19.    <http pattern="/scripts/**" security="none"></http>  
    20.    <http pattern="/font-awesome/**" security="none"></http>  
    21.    <http pattern="/system/resources/**" security="none"></http>  
    22.    <http pattern="/system/login.do" security="none"/>  
    23.    <http auto-config="true" use-expressions="true">  
    24.      <form-login login-page="/system/login.do"  default-target-url="/system/sysManage.do"/>  
    25.       <!--     
    26.          error-if-maximum-exceeded 后登陆的账号会挤掉第一次登陆的账号     
    27.          session-fixation-protection  防止伪造sessionid攻击,用户登录成功后会销毁用户当前的session。

          

    28.     -->    
    29.     <!-- <session-management invalid-session-url="/user/timedout" session-fixation-protection="none">    
    30.         <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>    
    31.     </session-management> -->  
    32.      <custom-filter ref="myFilterSecurityInterceptor" before="FILTER_SECURITY_INTERCEPTOR"/>  
    33.    </http>  
    34.    <!-- 认证管理器。实现用户认证的入口,主要实现UserDetailsService接口就可以 -->  
    35.     <authentication-manager alias="authenticationManager">  
    36.         <authentication-provider  
    37.             user-service-ref="myUserDetailsServiceImpl">  
    38.                 <!-- <password-encoder hash="md5" /> -->  <!--盐值  [加入这个属性后,加密password明文为:"password明文{盐值}"]  -->   
    39.         </authentication-provider>  
    40.     </authentication-manager>  
    41.      <!-- 配置过滤器 -->    
    42.     <beans:bean id="myFilterSecurityInterceptor" class="com.product.sys.security.MyFilterSecurityInterceptor">    
    43.         <!-- 用户是否拥有所请求资源的权限 -->    
    44.         <beans:property name="accessDecisionManager" ref="myAccessDescisionManager" />    
    45.         <!-- 资源与权限相应关系 -->    
    46.         <beans:property name="fisMetadataSource" ref="mySecurityMetadataSource" />    
    47.         <!-- 用户拥有的权限 -->    
    48.         <beans:property name="authenticationManager" ref="authenticationManager" />   
    49.     </beans:bean>    
    50.     <beans:bean id="mySecurityMetadataSource" class="com.product.sys.security.MySecurityMetadataSource"><beans:constructor-arg name="userMapper" ref="userMapper"></beans:constructor-arg></beans:bean>  
    51.     <beans:bean id="myAccessDescisionManager" class="com.product.sys.security.MyAccessDescisionManager"></beans:bean>  
    52.       
    53. </beans:beans>  

    到上面的这个配置文件,则是重中之重了。和ss3打交道。主要都是这个文件。简单说一下,我们须要实现一个自己的filter。在配置中就是myFilterSecurityInterceptor,而这个filter中,还须要我们额外注入三个bean,各自是accessDecisionManager、fisMetadataSource以及authenticationManager,这三个属性中除了fisMetadataSource能够自己定义名称外,其它两个都在ss3的父类中定义好了。所以此处须要特别注意。在这里掉过坑了。另外这里说一下这三个分别的作用。accessDecisionManager中有decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes)方法,该方法用于推断当前用户是否有权限进行操作,參数中authentication包括了当前用户所拥有的权限,configAttributes中包括了进行该步骤须要的权限,对其进行对照就能够推断该用户是否有权限进行操作。

    [html] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /**   
    2.  * @description  訪问决策器,决定某个用户具有的角色。是否有足够的权限去訪问某个资源 ;做终于的訪问控制决定   
    3.   
    4.  */   
    5. public class MyAccessDescisionManager implements AccessDecisionManager{  
    6.   
    7.     @Override  
    8.     public void decide(Authentication authentication, Object object,  
    9.             Collection<ConfigAttribute> configAttributes)  
    10.             throws AccessDeniedException, InsufficientAuthenticationException {  
    11.         // TODO Auto-generated method stub  
    12.          System.out.println("MyAccessDescisionManager.decide()------------------验证用户是否具有一定的权限--------");    
    13.             if(configAttributes==null) return;    
    14.             Iterator<ConfigAttribute> it = configAttributes.iterator();    
    15.             while(it.hasNext()){    
    16.                 String needResource = it.next().getAttribute();    
    17.                 //authentication.getAuthorities()  用户全部的权限    
    18.                 for(GrantedAuthority ga:authentication.getAuthorities()){    
    19.                     if(needResource.equals(ga.getAuthority())){    
    20.                         return;    
    21.                     }    
    22.                 }    
    23.             }    
    24.             throw new AccessDeniedException("--------MyAccessDescisionManager:decide-------权限认证失败!");    
    25.           
    26.     }  
    27.   
    28.     @Override  
    29.     public boolean supports(ConfigAttribute attribute) {  
    30.         // TODO Auto-generated method stub  
    31.         return true;  
    32.     }  
    33.   
    34.     @Override  
    35.     public boolean supports(Class<?> clazz) {  
    36.         // TODO Auto-generated method stub  
    37.         return true;  
    38.     }  
    39.   
    40. }  

    到这里。能够非常自然的想到是权限和用户数据从哪里得到的,filterInvocationSecurityMetadataSource在被载入时候,会首先将权限的信息建立起来。这里我用一个map。key为url,value为该权限的名称。这一步是在构造方法中进行的,也就是server启动时候完毕的。而当用户訪问某一个地址时,ss会到该类中调用getAttributes(Object obj)方法。obj中包括了訪问的url地址,我们须要做的就是将该url相应的权限名称返回给ss,而ss会将返回的这个对象,事实上就是accessDecisionManager的decide方法中的configAttributes对象。

    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /**  
    2.  * @description  资源源数据定义。将全部的资源和权限相应关系建立起来,即定义某一资源能够被哪些角色訪问  
    3.  * @author aokunsang  
    4.  * @date 2012-8-15  
    5.  */    
    6. public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {    
    7.   
    8.     private UserMapper userMapper;    
    9.     public UserMapper getUserMapper() {  
    10.         return userMapper;  
    11.     }  
    12.   
    13.     public void setUserMapper(UserMapper userMapper) {  
    14.         this.userMapper = userMapper;  
    15.     }  
    16.   
    17.     /* 保存资源和权限的相应关系  key-资源url  value-权限 */    
    18.     private static Map<String,Collection<ConfigAttribute>> resourceMap = null;     
    19.     private AntPathMatcher urlMatcher = new AntPathMatcher();    
    20.         
    21.     public MySecurityMetadataSource(UserMapper userMapper) {    
    22.         this.userMapper = userMapper;    
    23.         loadResourcesDefine();    
    24.     }    
    25.         
    26.     @Override    
    27.     public Collection<ConfigAttribute> getAllConfigAttributes() {    
    28.         return null;    
    29.     }    
    30.     
    31.     private void loadResourcesDefine(){    
    32.         resourceMap = new HashMap<String,Collection<ConfigAttribute>>();    
    33.             
    34.         System.out.println("MySecurityMetadataSource.loadResourcesDefine()--------------開始载入资源列表数据--------");    
    35.         List<RolePO> roles = userMapper.findAllRoles();    
    36.         for(RolePO role : roles){    
    37.             List<ResourcePO> resources = role.getResources();   
    38.             for(ResourcePO resource : resources){    
    39.                 Collection<ConfigAttribute> configAttributes = null;    
    40.                 ConfigAttribute configAttribute = new SecurityConfig(resource.getResourceName());    
    41.                 if(resourceMap.containsKey(resource.getUrl())){    
    42.                     configAttributes = resourceMap.get(resource.getUrl());    
    43.                     configAttributes.add(configAttribute);    
    44.                 }else{    
    45.                     configAttributes = new ArrayList<ConfigAttribute>() ;    
    46.                     configAttributes.add(configAttribute);    
    47.                     resourceMap.put(resource.getUrl(), configAttributes);    
    48.                 }    
    49.             }    
    50.         }  
    51.         System.out.println("11");  
    52.         Set<String> set = resourceMap.keySet();  
    53.         Iterator<String> it = set.iterator();  
    54.         while(it.hasNext()){  
    55.             String s = it.next();  
    56.             System.out.println("key:"+s+"|value:"+resourceMap.get(s));  
    57.         }  
    58.     }    
    59.     /*   
    60.      * 依据请求的资源地址,获取它所拥有的权限  
    61.      */    
    62.     @Override    
    63.     public Collection<ConfigAttribute> getAttributes(Object obj)    
    64.             throws IllegalArgumentException {    
    65.         //获取请求的url地址    
    66.         String url = ((FilterInvocation)obj).getRequestUrl();    
    67.         System.out.println("MySecurityMetadataSource:getAttributes()---------------请求地址为:"+url);    
    68.         Iterator<String> it = resourceMap.keySet().iterator();    
    69.         while(it.hasNext()){    
    70.             String _url = it.next();    
    71.             if(_url.indexOf("?

      ")!=-1){    

    72.                 _url = _url.substring(0, _url.indexOf("?"));    
    73.             }    
    74.             if(urlMatcher.match(_url,url)){  
    75.                 System.out.println("MySecurityMetadataSource:getAttributes()---------------须要的权限是:"+resourceMap.get(_url));    
    76.                 return resourceMap.get(_url);    
    77.             }  
    78.                   
    79.         }  
    80.         Collection<ConfigAttribute> nouse = new ArrayList<ConfigAttribute>();  
    81.         nouse.add(new SecurityConfig("无对应权限"));  
    82.         return nouse;  
    83.     }    
    84.     
    85.     @Override    
    86.     public boolean supports(Class<?> arg0) {    
    87.         System.out.println("MySecurityMetadataSource.supports()---------------------");    
    88.         return true;    
    89.     }    
    90.         
    91. }    

    到这里。我们另一个疑问。就是decide方法中的authentication对象(authentication.getAuthorities()包括当前用户拥有的权限),用户的相应角色和权限信息是从哪里获得的?事实上这里是通过调用MyUserDetailsServiceImpl来获取的,该类须要实现UserDetailService接口。更详细一些实际上是通过loadUserByUsername进行获取用户权限信息的,这里注意返回的User不是我们自定义的PO,而是ss3框架中的User。

    (这里说下为什么我自己的UserPO没有继承ss的User,就是由于User没有默认无參构造方法。导致mybatis无法创建对象,详细可能还是有办法的,比方重写mybatis的相关接口,比較麻烦。所以这里是先通过返回我们自己的UserPO后,再组装成ss须要的User对象进行的)这里在回到刚才AccessDescisionManager中的decide方法想一下。authentication.getAuthorities()事实上获得的就是以下的Collection<GrantedAuthority>类型的对象。

    最后以下的这段代码。我没有直接从username中直接获得resource,而是通过先获得role。再通过role获取resource。我感觉这样方便一些,sql也简单,当然有更好的能够替换掉。

    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. @Component("myUserDetailsServiceImpl")  
    2. public class MyUserDetailsServiceImpl implements UserDetailsService{  
    3.   
    4.     @Resource  
    5.     private UserMapper userMapper;  
    6.       
    7.     @Override  
    8.     public UserDetails loadUserByUsername(String username)  
    9.             throws UsernameNotFoundException {  
    10.         System.out.println("username is " + username);    
    11.         UserPO user = userMapper.getUserByUserName(username);  
    12.         if(user == null) {    
    13.             throw new UsernameNotFoundException(username);    
    14.         }    
    15.         Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(user);  
    16.           System.out.println(user.getUsername());  
    17.         return new User(  
    18.                 user.getUsername(),    
    19.                 user.getPassword(),     
    20.                 true,   
    21.                 true,  
    22.                 true,  
    23.                 true,  
    24.                 grantedAuths);    
    25.     }  
    26.   
    27.     //取得用户的权限    
    28.     private Set<GrantedAuthority> obtionGrantedAuthorities(UserPO user) {    
    29.         Set<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();    
    30.         List<RolePO> roles = user.getRoles();    
    31.             
    32.         for(RolePO role : roles) {    
    33.             RolePO innerRole = userMapper.getRoleByRoleName(role.getRoleName());  
    34.             List<ResourcePO> tempRes = innerRole.getResources();  
    35.             for(ResourcePO res : tempRes) {    
    36.                 authSet.add(new GrantedAuthorityImpl(res.getResourceName()));    
    37.            }    
    38.         }    
    39.         return authSet;    
    40.     }    
    41.       
    42.      
    43. }  

    到这里,全部的权限-角色-用户信息已经能够串起来了。

    再来梳理一下流程,启动server时,通过FilterInvocationSecurityMetadataSource获得用户的全部角色及权限信息。当用户登陆时,通过MyUserDetailsServiceImpl中的loadUserByUsername获得该登陆用户全部的权限,发出请求时。通过FilterInvocationSecurityMetadataSource的getAttributes(Object url)获得须要的权限名,最后在AccessDecisionManager中decide方法进行对照。假设用户拥有的权限名称和该url须要的权限名同样。那么放行,否则认证失败。清楚这些后,我们还须要一个filter,把上述流程串起来。就像提葡萄一样~

    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. /**  
    2.  * @description 一个自己定义的filter,  
    3.  *  必须包括authenticationManager,accessDecisionManager,securityMetadataSource三个属性,  
    4.         我们的全部控制将在这三个类中实现  
    5.  */   
    6. public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter{  
    7.   
    8.      private FilterInvocationSecurityMetadataSource fisMetadataSource;    
    9.        
    10.           
    11.   
    12.         /* (non-Javadoc)  
    13.          * @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#getSecureObjectClass()  
    14.          */    
    15.         @Override    
    16.         public Class<?

      > getSecureObjectClass() {    

    17.             return FilterInvocation.class;    
    18.         }    
    19.         
    20.         @Override    
    21.         public SecurityMetadataSource obtainSecurityMetadataSource() {    
    22.             return fisMetadataSource;    
    23.         }    
    24.         
    25.         @Override    
    26.         public void destroy() {}    
    27.             
    28.         @Override    
    29.         public void doFilter(ServletRequest request, ServletResponse response,    
    30.                 FilterChain chain) throws IOException, ServletException {    
    31.             System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------開始拦截器了....");    
    32.             FilterInvocation fi = new FilterInvocation(request, response, chain);    
    33.             InterceptorStatusToken token = super.beforeInvocation(fi);    
    34.               
    35.             try {    
    36.                 fi.getChain().doFilter(fi.getRequest(), fi.getResponse());    
    37.             } catch (Exception e) {    
    38.                 e.printStackTrace();    
    39.             }finally{    
    40.                 super.afterInvocation(token,null);    
    41.             }    
    42.               
    43.             System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....");    
    44.         }    
    45.          
    46.         @Override    
    47.         public void init(FilterConfig config) throws ServletException {    
    48.                 
    49.         }    
    50.             
    51.             
    52.         public void setFisMetadataSource(    
    53.                 FilterInvocationSecurityMetadataSource fisMetadataSource) {    
    54.             this.fisMetadataSource = fisMetadataSource;    
    55.         }    
    56.         public FilterInvocationSecurityMetadataSource getFisMetadataSource() {  
    57.             return fisMetadataSource;  
    58.         }  
    59.   
    60. }  

    假设所有照搬上边的代码,到这里就已经结束了。

    可是昨天晚上遇到一个大坑,就是发现假设我在数据库中配置了该用户的相关权限url后,用户能够訪问。假设用户没有该url的权限。该用户依旧能够訪问url,这是让我无比惊讶,由于大部分都是參考网络的资料写的,后来看了一下ss的源代码,才发现可能是其它人写错了。这里简单说一下,由于单位电脑没有ss的源代码,主要问题出在MyFilterSecurityInterceptor中的doFilter方法:InterceptorStatusToken token = super.beforeInvocation(fi);  当ss在未匹配到url的权限时,即MySecurityMetadataSource中的getAttributes返回的对象为空时。该方法beforeInvocation直接return null。而实际decide方法在下方并未执行。

    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. protected InterceptorStatusToken beforeInvocation(Object object) {  
    2.   
    3.        if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {  
    4.            .....  
    5.        }  
    6.   
    7.        Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);  
    8.   
    9.        if (attributes == null || attributes.isEmpty()) {//此处推断MySecurityMetadataSource中的getAttributes返回的对象  
    10.            if (rejectPublicInvocations) {  
    11.                throw new IllegalArgumentException("Secure object invocation " + object +  
    12.                        " was denied as public invocations are not allowed via this interceptor. "  
    13.                                + "This indicates a configuration error because the "  
    14.                                + "rejectPublicInvocations property is set to 'true'");  
    15.            }  
    16.   
    17.            if (debug) {  
    18.                logger.debug("Public object - authentication not attempted");  
    19.            }  
    20.   
    21.            publishEvent(new PublicInvocationEvent(object));  
    22.   
    23.            return null// no further work post-invocation  
    24.        }  
    25.   
    26.        if (debug) {  
    27.            logger.debug("Secure object: " + object + "; Attributes: " + attributes);  
    28.        }  
    29.   
    30.        if (SecurityContextHolder.getContext().getAuthentication() == null) {  
    31.            credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",  
    32.                    "An Authentication object was not found in the SecurityContext"), object, attributes);  
    33.        }  
    34.   
    35.        Authentication authenticated = authenticateIfRequired();//实际执行decide方法的地方  
    36.   
    37.        // Attempt authorization  
    38.        try {  
    39.            this.accessDecisionManager.decide(authenticated, object, attributes);  
    40.        }  
    41.        catch (AccessDeniedException accessDeniedException) {  
    42.            publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException));  
    43.   
    44.            throw accessDeniedException;  
    45.        }  
    46.   
    47.        if (debug) {  
    48.            logger.debug("Authorization successful");  
    49.        }  
    50.   
    51.        if (publishAuthorizationSuccess) {  
    52.            publishEvent(new AuthorizedEvent(object, attributes, authenticated));  
    53.        }  
    54.   
    55.        // Attempt to run as a different user  
    56.        Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);  
    57.   
    58.        if (runAs == null) {  
    59.            if (debug) {  
    60.                logger.debug("RunAsManager did not change Authentication object");  
    61.            }  
    62.   
    63.            // no further work post-invocation  
    64.            return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);  
    65.        } else {  
    66.            if (debug) {  
    67.                logger.debug("Switching to RunAs Authentication: " + runAs);  
    68.            }  
    69.   
    70.            SecurityContext origCtx = SecurityContextHolder.getContext();  
    71.            SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());  
    72.            SecurityContextHolder.getContext().setAuthentication(runAs);  
    73.   
    74.            // need to revert to token.Authenticated post-invocation  
    75.            return new InterceptorStatusToken(origCtx, true, attributes, object);  
    76.        }  
    77.    }  


    在我看的所有BLOG中,当匹配不到时,所有返回了Null,而当我追到 super.beforeInvocation(fi)源代码中时,发现当getAttributes返回null后。ss就会跳过AccessDecisionManager的decide方法,导致未进行推断!

    从而ss会让用户请求顺利的通过。之后,查了一下ss官方英文文档,例如以下描写叙述:

    Collection<ConfigAttribute> getAttributes(Object object)
                                              throws IllegalArgumentException
    Accesses the ConfigAttributes that apply to a given secure object.
    Parameters:
    object - the object being secured
    Returns:
    the attributes that apply to the passed in secured object. Should return an empty collection if there are no applicable attributes.
    Throws:
    IllegalArgumentException - if the passed object is not of a type supported by the SecurityMetadataSource implementation

    红色标出了,应当返回一个空的对象集合假设没有对应权限的时候。而其它blog文返回的是null,导致兴许跳过了decide方法!

    所以我在MySecurityMetadataSource中的getAttributes中写的是:

    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    1. Collection<ConfigAttribute> nouse = new ArrayList<ConfigAttribute>();  
    2. nouse.add(new SecurityConfig("无对应权限"));  
    3. return nouse;  
    这样当没有权限时。才干够正常拦截。如今博文抄来抄去,正确的还好,但凡有错误。

    真是坑死人。

  • 相关阅读:
    AsyncTask下载图片
    POJ 2418-Hardwood Species(map)
    [Python] Understand List Comprehensions in Python
    [Python] isinstance() for checking object type
    [Python] Find available methods and help in REPL
    [Python] Execute a Python Script
    [Python] Understand Mutable vs. Immutable objects in Python
    [Python] Check for None (Null) in Python
    [Python] Manage Dependencies with Python Virtual Environments
    [React] Controlling Form Values with React
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7359816.html
Copyright © 2011-2022 走看看