acegi+ssh动态实现基于角色的权限管理(一)

    第一次写blog,由于文笔差,请大家见谅.
    最近通过springside和springframework社区学习acegi,并将其加入到我的设计之中.,现在做到web-request,method interceptor,至于domain object级别和acl也没有去研究,但感觉做到这两步也就差不多了吧..
   jar包:
    spring1.2.jar   
    acegi-security-1.0.3.jar    
    commons-codec-1.3.jar
    ehcache-1.2.3.jar
 
   下边说一下我的实现步骤和方法.
    first:数据库结构的设计,我没有用acegi默认的数据库结构,分别用了6张表来实现自己的数据库表.
       权限资源表:存储受保护的url,method

CREATE TABLE `authorities` (
  `auth_ID` int(11) NOT NULL auto_increment,
  `authProtected` varchar(100) NOT NULL,
  `authNote` varchar(200) default NULL,
  `authType` int(2) NOT NULL,
  `authState` int(1) NOT NULL,
  PRIMARY KEY  (`auth_ID`),
  KEY `FK_authorities` (`authType`),
  CONSTRAINT `authorities_ibfk_1` FOREIGN KEY (`authType`) REFERENCES `authtype` (`type_ID`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

     权限资源类型表:
CREATE TABLE `authtype` (
  `type_ID` int(11) NOT NULL auto_increment,
  `typeName` varchar(20) NOT NULL,
  `typeState` int(11) NOT NULL default '0',
  PRIMARY KEY  (`type_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
     角色表:
CREATE TABLE `role` (
  `role_ID` int(11) NOT NULL auto_increment,
  `roleName` varchar(20) NOT NULL,
  `roleNote` varchar(50) default NULL,
  `roleState` int(11) NOT NULL default '0',
  PRIMARY KEY  (`role_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    角色_权限资源表:
CREATE TABLE `role_auth` (
  `role_auth_ID` int(11) NOT NULL auto_increment,
  `role_ID` int(11) NOT NULL,
  `auth_ID` int(11) NOT NULL,
  PRIMARY KEY  (`role_auth_ID`),
  KEY `FK_role_auth` (`role_ID`),
  KEY `FK_auth_role` (`auth_ID`),
  CONSTRAINT `role_auth_ibfk_1` FOREIGN KEY (`role_ID`) REFERENCES `role` (`role_ID`) ON UPDATE CASCADE,
  CONSTRAINT `role_auth_ibfk_2` FOREIGN KEY (`auth_ID`) REFERENCES `authorities` (`auth_ID`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
   用户表:
CREATE TABLE `users` (
  `user_ID` int(11) NOT NULL auto_increment,
  `userName` varchar(40) NOT NULL,
  `userPass` varchar(40) NOT NULL,
  `enabled` int(1) NOT NULL default '0',
  PRIMARY KEY  (`user_ID`),
  UNIQUE KEY `userName` (`userName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  用户_角色表:
CREATE TABLE `user_role` (
  `user_role_ID` int(11) NOT NULL auto_increment,
  `user_ID` int(11) NOT NULL,
  `role_ID` int(11) NOT NULL,
  PRIMARY KEY  (`user_role_ID`),
  KEY `FK_user_role` (`user_ID`),
  KEY `FK_role_users` (`role_ID`),
  CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`user_ID`) REFERENCES `users` (`user_ID`) ON UPDATE CASCADE,
  CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`role_ID`) REFERENCES `role` (`role_ID`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 主要用到Users,Roles,Authorities三张表,由于Users,Roles多对对,Roles,Authorities也是多对多关系,加了2张中间表,另外authType为区分权限资源的类型,受保护的权限资源有url,method以及domain object的三种保护类型.
当然也可以不要这张表.
 
second:在web.xml配置如下:

<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>/WEB-INF/spring.xml,/WEB-INF/spring_security_acegi.xml,
      /WEB-INF/spring_dao.xml,/WEB-INF/spring_service.xml,
      /WEB-INF/spring_action.xml</param-value>
  </context-param>

  <!-- set acegi filter -->
  <filter>
   <filter-name>acegiFilterChainProxy</filter-name>
   <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
   <init-param>
    <param-name>targetClass</param-name>
    <!-- you can change there to set channelProcessingFilter -->
    <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
   </init-param>
  </filter>
  <filter-mapping>
   <filter-name>acegiFilterChainProxy</filter-name>
   <url-pattern>/*</url-pattern>
  </filter-mapping>

  先通过ContextLoaderListener加载acegi的配置文件,存放在webApplicationContext中.然后将所有的过滤请求交给FilterChainProxy处理.
如果需要使用channelProcessingFilter 安全通道处理的话,在这里也可以加上.
以前使用openSessionInView,我只把spring.xml这个主配置文件通过ContextLoader
Listener加载,现在为了需要,全都用这个加载了.
 
  third:配置spring_security_acegi.xml
  通过ant加载所有的过滤器
  <!-- load all filter or inteceptor with ant -->
 <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
  <property name="filterInvocationDefinitionSource">
   <value>
    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
    PATTERN_TYPE_APACHE_ANT
    /**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
   </value>
  </property>
 </bean>
  八个过滤器, 一个拦截器,注意的两个问题:
  1:注意过滤器之间的先后顺序,否则会出一些错误的.
  2:所有过滤器都写一行,不要换行,否则换行的那些过滤器在加载的时候会报什么bean name ""错误.
  下边就是这些过滤器了.
 
 <!-- 1. set httpSessionContextIntegrationFilter -->
 <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
 集成所有过滤器的,没什么好说
 
 <!-- 2. set logoutFilter -->
 注销过滤器
 <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
  <constructor-arg value="/admin_login.jsp"/>
  <constructor-arg>
   <list>
    <ref bean="rememberMeServices"/>
    <ref bean="securityContextLogoutHanlder"/>
   </list>
  </constructor-arg>
  <property name="filterProcessesUrl" value="/logout"/>
 </bean>
 <!-- set rememberMeServices -->
 <bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
  <property name="userDetailsService" ref="hibernateDaoImpl"/>
  <property name="key" value="rememberMeUser"/>
  <property name="parameter" value="rememberMe"/>
  <property name="authenticationDetailsSource" ref="authenticationDetailsSourceHandler"/>
 </bean>
 <!-- set SecurityContextLogoutHandler -->
 <bean id="securityContextLogoutHanlder" class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
这个过滤器用来注销用户信息,如果用了cookie保存的话,需要进行设置.
 
<bean id="authenticationDetailsSourceHandler" class="com.runsa.components.acegi.support.AuthenticationDetailsSourceHandler"/>
这个是自己实现的,为了解决ConCurrentSessionImpl与rememberMe冲突的问题

public class AuthenticationDetailsSourceHandler extends
  AuthenticationDetailsSourceImpl {

 /**
  * 解决rememberMe与ConCurrentSessionController冲突
  */
 @Override
 public Object buildDetails(HttpServletRequest request) {
  // TODO 自动生成方法存根
  HttpSession session = request.getSession(false);
  if(session == null ){
   session = request.getSession(true);
  }
  return super.buildDetails(request);
 }
}

 

 基本处理过滤器,设置了登录入口页面,这个可以不要

 <!-- 3.set basicProcessingFilter -->
 <bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="authenticationEntryPoint">
   <bean class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
    <property name="realmName" value="www.runsa.cn"/>
   </bean>
  </property>
 </bean>

 

 认证处理过滤器,应该是最重要的一个了吧

<!-- 4.set authenticationProcessingFilter -->
 <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="authenticationFailureUrl" value="/admin_login.jsp?login_error=loginError"/>
  <property name="defaultTargetUrl" value="/admin/adminIndex.do"/>
  <property name="filterProcessesUrl" value="/check"/>
  <property name="rememberMeServices" ref="rememberMeServices"/>
  <property name="alwaysUseDefaultTargetUrl" value="true"/>
 </bean>
  authenticationManager认证管理器,这个等会在说
  authenticationFailureUrl认证失败页面
  defaultTargetUrl认证成功后转向的页面,我设置alwaysUseDefaultTargetUrl
为true,在认证成功后,自己做一些其他的处理.
 filterProcessesUrl认证时提交的form对应的ation
 rememberMeServices,认证时先通过cookie获取保存信息进行认证.
 
securityContextHolder处理过滤器,他用来存放securityContext信息,而securityContext存放Authorities[]权限信息,p rincipal(存放用户信息)
<!-- 5.set securityContextHolderAwareRequestFilter -->
 <bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>
 
cookie处理过滤器
<!-- 6.set rememberMeProcessingFilter -->
 <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="rememberMeServices" ref="rememberMeServices"/>
 </bean>
记录cookie信息
 
匿名处理过滤器
 <!-- 7.set anonymousProcessingFilter -->
 <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
  <property name="key" value="anonymousUser"/>
  <property name="userAttribute" value="isAnonymous,ROLE_ANONYMOUS"/>
 </bean>
 
异常处理过滤器
 <!-- 8. set exceptionTranslationFilter -->
 <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
  <property name="authenticationEntryPoint">
   <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
    <property name="loginFormUrl" value="/admin_login.jsp?login_error=noLogin"/>
    <property name="forceHttps" value="false"/>
   </bean>
  </property>
  <property name="accessDeniedHandler">
   <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
    <property name="errorPage" value="/admin/deny.jsp"/>
   </bean>
  </property>
 </bean>
在认证失败后,一般会抛出AccessDeniedException,过滤器捕获此异常并转向errorPage页面.
 
资源调用拦截器
 <!-- 9. set filterInvocationInterceptor -->
 <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
  <property name="authenticationManager" ref="authenticationManager"/>
  <property name="accessDecisionManager" ref="httpAccessDecisionManager"/>
  <property name="objectDefinitionSource" ref="filterDefinitionSource"/>
 </bean>
如果需要从配置文件加载保护资源的话,要相对简单
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/admin/* = ROLE_ADMIN
/* = ROLE_USER,ROLE_ANONYMOUS
这样就可以了
但为了实现配置动态更新,从数据库获取保护资源.这个等会再说.
 
在过滤拦截保护资源时,需要访问决策管理器,三种投票方式
  org.acegisecurity.vote.ConsensusBased多数投票者同意可通过
org.acegisecurity.vote.UnanimousBased全部投票者同意可通过
org.acegisecurity.vote.AffirmativeBased至少一个投票者同意可通过
一般选最后一个 就可以了
 <!-- set httpAccessDecisionManager -->
 <bean id="httpAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
  <property name="allowIfAllAbstainDecisions" value="false"/>
  <property name="decisionVoters">
   <list>
    <bean class="org.acegisecurity.vote.RoleVoter">
     <property name="rolePrefix" value="ROLE_"/>
    </bean>
    <bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
   </list>
  </property>
 </bean>

 

 晕 超过2W了  换一页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值