【SSI开发总结.4】Spring中使用Acegi安全框架

 

Acegi认证授权主要基于两大技术,一是Filter机制,二是AOP的拦截 机制。通过FilterSecurityInterceptor很好地实现了对URI的保护,通过MethodSecurityInterceptor实 现了对Service的方法的拦截保护,通过ACL 实现了对prototype类型的Object进行过滤和保护。

HttpSessionContextIntegrationFilter 存储SecurityContext in HttpSession
ChannelProcessingFilter 重定向到另一种协议,如http到https

ConcurrentSessionFilter 因为不使用任何SecurityContextHolder的功能,但是需要更新SessionRegistry来表示当前的发送请求的 principal,通过在web.xml中注册Listener监听Session事件,并发布相关消息,然后由SessionRegistry获得消 息以判断当前用户的Session数量。

AuthenticationProcessingFilter 普通认证机制(大多数用这个)

CasProcessingFilter CAS认证机制

BasicProcessingFilter Http协议的Basic认证机制

HttpRequestIntegrationFilter Authentication 从容器的HttpServletRequest.getUserPrincipal()获得

JbossIntegrationFilter 与Jboss相关。

SecurityContextHolderAwareRequestFilter 与servlet容器结合使用。

RememberMeProcessingFilter 基于Cookies方式进行认证。

AnonymousProcessingFilter 匿名认证。

ExceptionTranslationFilter 捕获所有的Acegi Security 异常,这样要么返回一个HTTP错误响应或者加载一个对应的AuthenticationEntryPoint

AuthenticationEntryPoint 认证入口

Acegi认证授权流程
1、FilterToBeanProxy 负责代理请求给FilterChainProxy

2、FilterChainProxy 方便的将多个Filter串联起来,如上面基本概念中提到的各种Filter,当然如果对URI进行授权保护,也可以包含FilterSecurityInterceptor。注意各Filter的顺序。

3、AbstractSecurityInterceptor 调度中心。负责调用各模块完成相应功能。
FilterSecurityInterceptor 对URI进行拦截保护
AspectJSecurityInterceptor 对方法进行拦截保护
MethodSecurityInterceptor 对方法进行拦截保护

4、AuthenticationManager 用户认证
-> AuthenticationProvider 实际进行用户认证的地方(多个)。
-> UserDetailsService 返回带有GrantedAuthority的UserDetail或者抛出异常。

5、AccessDecisionManager(UnanimousBased/AffirmativeBased/ConsensusBased) 授权
-> AccessDecisionVoter(RoleVoter/BaseAclEntryVoter) 实际投票的Voter(多个).

6、RunAsManager 变更GrantedAuthority

7、AfterInvocationManager 变更返回的对象
-> BaseInvocationProvider 实际完成返回对象变更的地方(多个)。

在我的项目中,用ACEGI实现了会员系统的权限控制操作

1.配置web.xml

/****** /web-inf/web.xml ******/

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
   <display-name>game_proj</display-name>
    <!-- 中文语言支持过滤器 -->
    <filter>
        <filter-name>Set Character Encoding</filter-name>
        <filter-class>com.popoann.SetCharacterEncodingFilter</filter-class>
        <init-param>
           <param-name>encoding</param-name>
           <param-value>GBK</param-value>
        </init-param>
    </filter>
    <filter-mapping>
       <filter-name>Set Character Encoding</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>
<filter>
<!-- Acegi过滤器 -->
       <filter-name>acegiFilterChain</filter-name>
       <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
       <init-param>
          <param-name>targetClass</param-name>
          <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
       </init-param>
    </filter>
    <!-- Acegi过滤器URL映射 -->
    <filter-mapping>
       <filter-name>acegiFilterChain</filter-name>
       <url-pattern>*.htm</url-pattern>
    </filter-mapping>

    <filter-mapping>
       <filter-name>acegiFilterChain</filter-name>
       <url-pattern>*.jsp</url-pattern>
    </filter-mapping>

    <filter-mapping>
       <filter-name>acegiFilterChain</filter-name>
       <url-pattern>*.do</url-pattern>
    </filter-mapping>

    <!-- ActionContextCleanUp过滤器 -->
    <filter>
       <filter-name>struts-cleanup</filter-name>
       <filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
    </filter>
   <filter-mapping>
       <filter-name>struts-cleanup</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- SiteMesh的核心过滤器 -->
    <filter>
       <filter-name>sitemesh</filter-name>
       <filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
    </filter>
   <filter-mapping>
      <filter-name>sitemesh</filter-name>
      <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- Struts核心过滤器 -->
    <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
    <init-param>
           <param-name>struts.action.extension</param-name>
           <param-value>htm</param-value>
       </init-param>
</filter>
<filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

    <!-- 应用程序启动时,加载SpringIoC容器 -->
    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
      /WEB-INF/beans-*.xml
      </param-value>
    </context-param>
    <listener>
     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 欢迎页面 -->
<welcome-file-list>
   <welcome-file>index.htm</welcome-file>
</welcome-file-list>

</web-app>

2.配置spring bean

/****** /web-inf/beans-myspace_security.xml ******/

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
          "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
   <!-- 1.配制认证管理器(AuthenticationManager) -->
   <bean id="myspace_authenticationManager"
      class="org.acegisecurity.providers.ProviderManager">
      <property name="providers">
        <list>
            <ref bean="myspace_daoAuthenticationProvider"/>
            <ref bean="myspace_rememberMeAuthenticationProvider"/>
        </list>
      </property>
   </bean>

<!-- 表示通过DAO接口进行数据库验证 -->

<!-- 指定userDetailsService,表示用户列表由这个类得到,后面编程实现这个自定义用户列表类 -->

<!-- 指定passwordEncoder,表示在验证之前,对表单中的密码先进行指定编码方式的加密,这里为MD5加密策略 -->
   <bean id="myspace_daoAuthenticationProvider"
      class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
      <property name="userDetailsService" ref="myspace_userDetailsBo"/>
      <property name="passwordEncoder" ref="myspace_passwordEncoder"/>
   </bean>
   <bean id="myspace_passwordEncoder"
      class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>

<!-- 表示通过COOKIES进行验证 -->
   <bean id="myspace_rememberMeAuthenticationProvider"
      class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
      <property name="key" value="myspace_remember_me"/>
   </bean>


   <!-- 2.配制决策管理器(AccessDecisionManager) -->
   <bean id="myspace_accessDecisionManager"
      class="org.acegisecurity.vote.AffirmativeBased">
      <property name="decisionVoters">
         <list>
            <bean class="org.acegisecurity.vote.RoleVoter"/>
         </list>
      </property>
      <property name="allowIfAllAbstainDecisions" value="false"/>
   </bean>
   <!-- 3.配制拦截器链(FilterChain) -->
   <bean id="myspace_filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
      <property name="filterInvocationDefinitionSource">
        <value>

   
           PATTERN_TYPE_APACHE_ANT
             /myspace/*.htm*=myspace_httpSessionContextIntegrationFilter,myspace_logoutFilter,myspace_authenticationProcessingFilter,myspace_rememberMeFilter,myspace_exceptionFilter,myspace_securityInterceptor
            /**=myspace_httpSessionContextIntegrationFilter,myspace_logoutFilter,myspace_authenticationProcessingFilter,myspace_rememberMeFilter,myspace_exceptionFilter,myspace_securityInterceptor

        </value>
      </property>
   </bean>
   <!-- (1) Session会话验证拦截器-->
   <bean id="myspace_httpSessionContextIntegrationFilter"
      class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
   <!-- (2) 注销功能拦截器-->
   <bean id="myspace_logoutFilter"
      class="org.acegisecurity.ui.logout.LogoutFilter">
      <!-- 注销后默认的跳转页面 -->
      <constructor-arg value="/login.htm"/>
      <constructor-arg>
         <list>
            <ref bean="myspace_rememberMeServices"/>
            <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
         </list>
      </constructor-arg>
      <!-- 用户注销URL -->
      <property name="filterProcessesUrl" value="/j_logout.do"/>
   </bean>
   <!-- (3) -->
   <bean id="myspace_authenticationProcessingFilter"
      class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
      <property name="authenticationManager" ref="myspace_authenticationManager"/>
      <!-- 登录失败的页面,通常包含出错信息的登录页面 -->
      <property name="authenticationFailureUrl" value="/login!error.htm"/>
      <!-- 登录后默认跳转页面 -->
      <property name="defaultTargetUrl" value="/myspace/hellouser.htm"/>
      <!-- 用户登录URL -->
      <property name="filterProcessesUrl" value="/j_login.do"/>
      <property name="rememberMeServices" ref="myspace_rememberMeServices"/>
   </bean>
   <!-- (4) cookies验证拦截器-->
   <bean id="myspace_rememberMeFilter"
      class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
      <property name="authenticationManager" ref="myspace_authenticationManager"/>
      <property name="rememberMeServices" ref="myspace_rememberMeServices"/>
   </bean>
   <bean id="myspace_rememberMeServices"
      class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
      <property name="userDetailsService" ref="myspace_userDetailsBo"/>
      <property name="parameter" value="myspace_remember_me"/>
      <property name="key" value="myspace_remember_me"/>
      <property name="tokenValiditySeconds" value="31536000"/>
   </bean>
   <!-- (5) -->
   <bean id="myspace_exceptionFilter"
      class="org.acegisecurity.ui.ExceptionTranslationFilter">
      <!-- 出现AuthenticationException时的登陆入口 -->
      <property name="authenticationEntryPoint">
         <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
            <property name="loginFormUrl" value="/login.htm"/>
            <property name="forceHttps" value="false"/>
         </bean>
      </property>

       <!-- 指定错误页,当前角色不能访问本资源时,定位到错误页 -->
      <!-- 出现AccessDeniedException时的handler -->
      <property name="accessDeniedHandler">
         <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
           <property name="errorPage" value="/error.jsp" />
         </bean>
      </property>
   </bean>
   <!-- (6) 安全拦截器,整合认证和决策管理器,并定义各种目录结构对应的角色,可用ANT表达式-->
   <bean id="myspace_securityInterceptor"
      class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
      <property name="authenticationManager" ref="myspace_authenticationManager"/>
      <property name="accessDecisionManager" ref="myspace_accessDecisionManager"/>
      <property name="objectDefinitionSource">
         <value>
           PATTERN_TYPE_APACHE_ANT
           /myspace/sales_*.htm*=ROLE_SALES_USER
           /myspace/provide_*.htm*=ROLE_PROVIDE_USER

         </value>
      </property>
   </bean>
</beans>

一定要注意Filter的配置顺序,还有更多Filter,不过以上足够了

3.编写UserDetails类

/****** myspace_userDetailsBo.java ******/

package bo;

import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.User;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.springframework.dao.DataAccessException;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.GrantedAuthority;
import bo.UserBo;
import java.util.*;

public class UserDetailsBo implements UserDetailsService {

     //定义两个角色
    private final GrantedAuthority ROLE_SALES_USER=new GrantedAuthorityImpl("ROLE_SALES_USER");
    private final GrantedAuthority ROLE_PROVIDE_USER=new GrantedAuthorityImpl("ROLE_PROVIDE_USER");


    private UserBo bo;
    private model.User user;
    private List< model.User> myusers;
    private Map<String,String> users=new HashMap<String,String>();;
    private GrantedAuthority[] authorities;
   //注入业务对象的同时,把得到的用户列表依次拷贝的一个HashMap对象中
public void setBo(UserBo bo) {
     this.bo = bo;
    myusers=bo.getUserList();
    users.put("test", "test");
    for(model.User user:myusers){
       users.put(user.getUsername().trim(), user.getPassword().trim());
       System.out.println(user.getUsername().trim()+"=="+user.getPassword().trim());
    }
}
    public UserDetailsBo(){

    }
/* 必须实现这个接口方法,改方法表示在验证之前,该用户是否存在,如果不存在,当然就没有验证的必要了,如果存在,之前通过表单得到的用户名,密码就要和本类中定义的Hashmap中存在的用户密码名值对做对比,然后根据spring bean配置实现验证 */
public UserDetails loadUserByUsername(String username)
    throws UsernameNotFoundException, DataAccessException {
   // TODO Auto-generated method stub
   System.out.println(username);
   String password=users.get(username);
   if(password==null)
    throw new UsernameNotFoundException("No such user");
   System.out.println(password);
   if(bo.getUser(username).getType()==1)
    authorities=new GrantedAuthority[]{ROLE_SALES_USER};
   else
    authorities=new GrantedAuthority[]{ROLE_PROVIDE_USER};   
   return new User(username,password,true,true,true,true,authorities);
}

}

4. 根据配置,制作登录表单

<table width="167" height="80" border="0" cellpadding="0" cellspacing="0">
    <form onSubmit="return doValidate('_login')" id="_login" name="_login" method="post" action="j_login.do">
      <tr class="style3">
        <td width="60" align="right" class="style3"><span class="STYLE9 style3">用户名:</span></td>
        <td width="107"><input name="j_username " type="text" class="STYLE9 " id="j_username" size="15" /></td>
      </tr>
      <tr class="style3">
        <td align="right" class="style3"><span class="STYLE9 style3">密&nbsp;&nbsp;码:</span></td>
        <td><input name="j_password " type="password" class="STYLE9 " id="j_password" size="15" /></td>
      </tr>
      <tr class="style3">
        <td colspan="2" align="center"><table width="100%" height="27" border="0" cellpadding="0" cellspacing="0">
            <tr>
              <td align="center" class="style2"><input style=" background-image:url(images/btnbg.gif); width:67px; height:22px" type="submit" name="Submit" value="登录" /><input type="checkbox" name="myspace_remember_me "/>               
                记住用户</td>
            </tr>
        </table></td>
      </tr>
    </form>
</table>

注意高亮的三个地方,用户名和密码的字段必须是j_username,j_password,myspace_remember_me这个名称显然 要和spring bean配置中,myspace_rememberMeAuthenticationProvider这个bean的key属性对应

到此,安全验证完成。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值