学习Acegi-认证(authentication)

最近两星期在学习acegi,过程中感谢JavaEye,SpringSide和在网上提供acegi学习心得的网友们。
为了加深自己的认识,准备写下一些DEMO,希望可以给准备学习acegi的同学一些帮助。
作为安全服务离不开认证和授权这两个主要组成部分。而这篇文章就是针对acegi的认证服务。
《学习Acegi-认证(authentication)》
代码环境基于:
JDK1.5
acegi1.0.3
spring2.0

IDE基于:
Eclipse3.2+MyEclipse5.0.1

面向人员:
熟悉Eclipse+MyEclipse开发但刚开始了解acegi的人员。如果你是高手请指出文章不足之处。

1.建立一个MyEclipse的WebProject,把下列jar文件拷贝到项目的WEB-INF/lib目录:
acegi-security-1.0.3.jar
spring2.0.jar
commons-codec-1.3.jar
费话说一句(占些字数):这是因为代码运行需要这些包的支持。

2.修改WEB-INF下的web.xml文件,内容如下:


代码
<?xml version="1.0" encoding="UTF-8"?> 
<web-app> 
 
    <display-name>acegi Example of liuyxit</display-name> 
      
    <!-- 
        定义应用的上下文参数,用于ContextLoaderListener     
    --> 
    <context-param> 
        <param-name>contextConfigLocation</param-name> 
        <param-value> 
            classpath:spring/applicationContext.xml  
        </param-value> 
    </context-param> 
 
    <!--acegi 的filter链代理--> 
    <filter> 
        <filter-name>Acegi Filter Chain Proxy</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> 
    <filter-mapping> 
        <filter-name>Acegi Filter Chain Proxy</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping> 
      
    <!--  
        装载应用软件的Spring上下文  
        要由WebapplicationContextUtils.getWebApplicationnContext(servletContext)得到.  
    --> 
    <listener> 
        <listener-class> 
            org.springframework.web.context.ContextLoaderListener  
        </listener-class> 
    </listener> 
</web-app> 


其中FilterChainProxy实现了filter接口,它主要是实例化FilterChainProxy,并把所有动作交由FilterChainProxy处理。这样简化了web.xml的配置,并且充分利用了Spring IOC管理Bean的优势。

3.在src目录右键新建一个resource folder,在下面再建立acegi和spring目录
在spring目录中创建applicationContext.xml文件,内容:


代码
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:aop="http://www.springframework.org/schema/aop" 
       xmlns:tx="http://www.springframework.org/schema/tx" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"  
           default-autowire="byName" default-lazy-init="true"> 
      
     <!--   ========================  FILTER CHAIN  =======================   -->   
     <bean id="filterChainProxy"   class="org.acegisecurity.util.FilterChainProxy">   
       <property name="filterInvocationDefinitionSource">   
          <value>   
            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON  
            PATTERN_TYPE_APACHE_ANT  
            /**=authenticationProcessingFilter,exceptionTranslationFilter  
         </value> 
      </property> 
    </bean>     
    <!-- ======================== 认证filter ======================= --> 
     
    <!-- 表单认证处理filter --> 
    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/> 
        <property name="defaultTargetUrl" value="/userinfo.jsp"/> 
        <property name="filterProcessesUrl" value="/j_acegi_security_check"/> 
    </bean>      
         
   <!-- 认证管理器 --> 
   <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> 
      <property name="providers"><!-- 可有多个认证提供器,其中一个证通过就可以了 --> 
         <list> 
            <ref local="daoAuthenticationProvider"/> 
         </list> 
      </property> 
   </bean>     
    <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> 
        <property name="userDetailsService" ref="inMemoryDaoImpl"/>          
    </bean>      
    <!-- 用户资料--> 
     <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">    
        <property name="userMap">    
            <value>    
                liuyxit=123,ROLE_SUPERVISOR  
                user1user1=user1,ROLE_USER  
                user2user2=user2,disabled,ROLE_USER     
            </value>    
        </property>    
    </bean> 
    <!-- 异常处理filter --> 
    <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> 
        <property name="authenticationEntryPoint"> 
            <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> 
                <property name="loginFormUrl" value="/acegilogin.jsp"/> 
                <property name="forceHttps" value="false"/> 
            </bean> 
        </property> 
        <property name="accessDeniedHandler"> 
            <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl"> 
                <property name="errorPage" value="/accessDenied.jsp"/> 
            </bean> 
        </property> 
    </bean>      
</beans>   



其中filterChainProxy就是由web.xml声明的filter(FilterToBeanProxy)的targetClass。它主要是装载filterInvocationDefinitionSource指定的filter类(例子中为authenticationProcessingFilter,exceptionTranslationFilter),并顺序调用它们的doFilter方法,进行安全服务处理。


而authenticationProcessingFilter是处理一个认证表单,登陆用的表单必须提交用户名和密码这两个参数给这个filter.由用户名和密码构造一个UsernamePasswordAuthenticationToken,将传给AuthenticationManager的authenticate方法进行认证处理。该filter默认处理filterProcessesUrl属性指定的URL,认证失败会转到authenticationFailureUrl,认证成功会转到defaultTargetUrl页面。


AuthenticationManager顾名思义认证管理器,它只有一个接口方法authenticate用于返回认证结果,他的实现类由多个AuthenticationProvider进行投票,决定认证是否通过。


daoAuthenticationProvider是检验用户录入的认证数据是否正确(说白了就是用户名和密码是否正确)


inMemoryDaoImpl是给daoAuthenticationProvider提供系统的用户资料。而资料的来源是从配置中装载到内存的。


当认证不通过时,AuthenticationManager的实现类AbstractAuthenticationManager会抛出AuthenticationException类型的异常。这时排在最后的exceptionTranslationFilter会捕获该异常,并转向authenticationEntryPoint。

4.在WebRoot下创建index.jsp(其实不要也没关系,主要是为了方便),直接转向用户资料显示页。内容如下:


代码
<%@ page language="java" pageEncoding="UTF-8"%> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<html> 
<head> 
    <META HTTP-EQUIV="Refresh" CONTENT="0;URL=userinfo.jsp"> 
</head> 
 
<body> 
<p>Loading ...</p> 
</body> 
</html> 


5.在WebRoot下创建userinfo.jsp,用于显示当前登陆的用户信息。内容如下


代码
<%@ page language="java" pageEncoding="UTF-8"%> 
<%@ page import="org.acegisecurity.context.SecurityContextHolder"%> 
<%@ page import="org.acegisecurity.userdetails.*"%> 
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
    <head> 
        <title>My JSP 'pass.jsp' starting page</title> 
 
        <meta http-equiv="pragma" content="no-cache"> 
        <meta http-equiv="cache-control" content="no-cache"> 
        <meta http-equiv="expires" content="0"> 
        <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> 
        <meta http-equiv="description" content="This is my page"> 
    </head> 
 
    <body> 
        当前用户:  
        <%  
            Object obj = SecurityContextHolder.getContext().getAuthentication();          
            if (null != obj){  
                Object userDetail = SecurityContextHolder.getContext().getAuthentication().getPrincipal();  
                String username = "";  
                if (userDetail instanceof UserDetails) {  
                    username = ((UserDetails) userDetail).getUsername();  
                } else {  
                    username = userDetail.toString();  
                }  
                out.print(username);  
                out.print("<br><a href=\"j_acegi_logout\">注销</a>");  
            }else{  
                out.print("当前没有有效的用户");  
                out.print("<br><a href=\"acegilogin.jsp\">登陆</a>");  
            }  
        %>        
    </body> 
</html> 


6.在WebRoot下创建acegilogin.jsp


代码
<%@ page language="java" pageEncoding="UTF-8"%> 
<%@ page import="org.acegisecurity.ui.AbstractProcessingFilter" %> 
<%@ page import="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter" %> 
<%@ page import="org.acegisecurity.AuthenticationException" %> 
<html> 
  <head> 
    <title>Login</title> 
  </head> 
  <body> 
    <h1>Login</h1> 
 
    <P>Valid users:  
    <P> 
    <P>username <b>liuyxit</b>, password <b>123</b> (supervisor)  
    <P>username <b>user1</b>, password <b>user1</b> (normal user)  
    <p>username <b>user2</b>, password <b>user2</b> (user disabled)  
    <p> 
    <%  
        String strError = request.getParameter("login_error");        
        if (null != strError){   
     %> 
      <font color="red"> 
        你的登陆失败,请重试。<BR><BR> 
         原因: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %> 
      </font> 
      <%  
        }//end if  
      %> 
 
    <form action="j_acegi_security_check" method="POST"> 
      <table> 
        <tr><td>User:</td><td><input type='text' name='j_username' value='<%= session.getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_LAST_USERNAME_KEY) %>'></td></tr> 
        <tr><td>Password:</td><td><input type='password' name='j_password'></td></tr> 
        <tr><td><input type="checkbox" name="_acegi_security_remember_me"></td><td>2周内自动登录</td></tr> 
 
        <tr><td colspan='2'><input name="submit" type="submit"></td></tr> 
        <tr><td colspan='2'><input name="reset" type="reset"></td></tr> 
      </table> 
    </form> 
 
  </body> 
</html> 


7.OK,发布项目,访问http://localhost:8080/acegiexample
这时index.jsp会自动转向userinfo.jsp,由于还没有用户登录,所以没有资料显示。按登陆链接进入登录页,登录成功后会看到显示用户名的页面(当然可以有更多的用户资料,但这仅仅是example),不成功时会在登录页提示信息。我们可以用user1和user2登陆,可以分别测试登录成功和失败的流程。

8.可以看到登录页上有自动登陆功能,而userinfo.jsp页有注销功能。但还是不起作用,好,现在我们马上首手加入这两个功能。看acegi可以方便到什么程度。修改applicationContext.xml如下:


代码
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:aop="http://www.springframework.org/schema/aop" 
       xmlns:tx="http://www.springframework.org/schema/tx" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"  
           default-autowire="byName" default-lazy-init="true">   
      
     <!--   ========================  FILTER CHAIN  =======================   -->   
     <bean id="filterChainProxy"   class="org.acegisecurity.util.FilterChainProxy" >   
       <property name="filterInvocationDefinitionSource" >   
          <value >   
            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON  
            PATTERN_TYPE_APACHE_ANT  
             /** =authenticationProcessingFilter,logoutFilter,rememberMeProcessingFilter,exceptionTranslationFilter  
         </value> 
      </property> 
    </bean>     
    <!-- ======================== 认证filter ======================= --> 
     
    <!-- 表单认证处理filter --> 
    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/> 
        <property name="defaultTargetUrl" value="/userinfo.jsp"/> 
        <property name="filterProcessesUrl" value="/j_acegi_security_check"/> 
    </bean> 
      
    <!-- 利用cookie自动登陆filter --> 
    <bean id="rememberMeProcessingFilter" 
          class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"> 
        <property name="authenticationManager" 
                  ref="authenticationManager"/> 
        <property name="rememberMeServices" ref="rememberMeServices"/> 
    </bean>      
    <bean id="rememberMeServices" 
          class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"> 
        <property name="userDetailsService" ref="inMemoryDaoImpl"/> 
        <property name="key" value="javargb"/> 
    </bean>    
    <bean id="rememberMeAuthenticationProvider" 
          class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> 
        <property name="key" value="javargb"/> 
    </bean>     
      
    <!-- 注销处理filter --> 
    <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> 
      <constructor-arg value="/acegilogin.jsp"/> <!-- URL redirected to after logout --> 
      <constructor-arg> 
         <list> 
              <ref bean="rememberMeServices"/> 
              <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> 
         </list> 
      </constructor-arg> 
   </bean> 
      
   <!-- 认证管理器 --> 
   <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> 
      <property name="providers"><!-- 可有多个认证提供器,其中一个证通过就可以了 --> 
         <list> 
            <ref local="daoAuthenticationProvider"/> 
            <ref local="rememberMeAuthenticationProvider"/> 
         </list> 
      </property> 
   </bean>     
    <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> 
        <property name="userDetailsService" ref="inMemoryDaoImpl"/>          
    </bean>      
    <!--   
    <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> 
        <property name="userProperties"> 
            <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean"> 
                <property name="location" value="classpath:acegi/users.properties"/> 
            </bean> 
        </property> 
    </bean> 
     --> 
     <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">    
        <property name="userMap">    
            <value>    
                liuyxit=123,ROLE_SUPERVISOR  
                user1user1=user1,ROLE_USER  
                user2user2=user2,disabled,ROLE_USER     
            </value>    
        </property>    
    </bean> 
    <!-- 异常处理filter --> 
    <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> 
        <property name="authenticationEntryPoint"> 
            <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> 
                <property name="loginFormUrl" value="/acegilogin.jsp"/> 
                <property name="forceHttps" value="false"/> 
            </bean> 
        </property> 
        <property name="accessDeniedHandler"> 
            <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl"> 
                <property name="errorPage" value="/accessDenied.jsp"/> 
            </bean> 
        </property> 
    </bean>      
</beans>   


另要注意:Acegi默认的自动登陆设定参数名为_acegi_security_remember_me,注销链接为/j_acegi_logout。
马上重启Tomcat测试看看^_^。

9.通常用户资料会放在数据库中,而不会放在配置文件,接着下面我们来再行修改一下。
首先创建要用到的用户表和权限表,并插入初始化数据:


代码
CREATE TABLE USERS(  
USERNAME VARCHAR( 50 ) NOT NULL PRIMARY KEY,  
PASSWORD VARCHAR( 50 ) NOT NULL,  
ENABLED BIT NOT NULL)  
 
INSERT INTO USERS(username,password,enabled) values( ' liuyxit ' , ' 123 ' , ' 1 ' )  
INSERT INTO USERS(username,password,enabled) values( ' user1 ' , ' user1 ' , ' 1 ' )  
INSERT INTO USERS(username,password,enabled) values( ' user2 ' , ' user2 ' , ' 0 ' )  
 
CREATE TABLE AUTHORITIES(  
USERNAME VARCHAR( 50 ) NOT NULL,  
AUTHORITY VARCHAR( 50 ) NOT NULL,  
CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME)  
);  
 
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( ' liuyxit ' , ' ROLE_SUPERVISOR ' )  
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( ' user1 ' , ' ROLE_USER ' )  
INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( ' user2 ' , ' ROLE_USER ' )   


这里我用的是acegi默认的数据结构,可以改只要你指定JdbcDaoImpl的authoritiesByUsernameQuery和usersByUsernameQuery属性就可以了。另AUTHORITIES表也要一同加入,原因acegi获得userDetail时,也会读取这个表的内容,否则会抛“nested exception is java.sql.SQLException: 对象名 'authorities' 无效”这个异常。

修改applicationContext.xml,变成如下:


代码
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:aop="http://www.springframework.org/schema/aop" 
       xmlns:tx="http://www.springframework.org/schema/tx" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd  
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"  
           default-autowire="byName" default-lazy-init="true"> 
      
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 
        <property name="driverClassName"> 
            <!-- 请自行修改为对应你的数据库的驱动类 --> 
            <value>net.sourceforge.jtds.jdbc.Driver</value>              
        </property> 
        <property name="url"> 
            <!-- 请自行修改为对应你的数据库URL --> 
            <value>jdbc:jtds:sqlserver://localhost:1433/javauser</value> 
        </property> 
        <property name="username"> 
            <value>sa</value> 
        </property> 
        <property name="password"> 
            <value>javauser</value> 
        </property> 
    </bean> 
    
    <!-- ======================== FILTER CHAIN ======================= --> 
    <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> 
      <property name="filterInvocationDefinitionSource"> 
         <value> 
            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON  
            PATTERN_TYPE_APACHE_ANT  
            /**=authenticationProcessingFilter,logoutFilter,rememberMeProcessingFilter,exceptionTranslationFilter  
         </value> 
      </property> 
    </bean>     
    <!-- ======================== 认证filter ======================= --> 
     
    <!-- 表单认证处理filter --> 
    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/> 
        <property name="defaultTargetUrl" value="/userinfo.jsp"/> 
        <property name="filterProcessesUrl" value="/j_acegi_security_check"/> 
    </bean> 
      
    <!-- 利用cookie自动登陆filter --> 
    <bean id="rememberMeProcessingFilter" 
          class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"> 
        <property name="authenticationManager" 
                  ref="authenticationManager"/> 
        <property name="rememberMeServices" ref="rememberMeServices"/> 
    </bean>      
    <bean id="rememberMeServices" 
          class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"> 
        <property name="userDetailsService" ref="jdbcDaoImpl"/> 
        <property name="key" value="javargb"/> 
    </bean>    
    <bean id="rememberMeAuthenticationProvider" 
          class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> 
        <property name="key" value="javargb"/> 
    </bean>     
      
    <!-- 注销处理filter --> 
    <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> 
      <constructor-arg value="/acegilogin.jsp"/> <!-- URL redirected to after logout --> 
      <constructor-arg> 
         <list> 
              <ref bean="rememberMeServices"/> 
              <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> 
         </list> 
      </constructor-arg> 
   </bean> 
      
   <!-- 认证管理器 --> 
   <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> 
      <property name="providers"><!-- 可有多个认证提供器,其中一个证通过就可以了 --> 
         <list> 
            <ref local="daoAuthenticationProvider"/> 
            <ref local="rememberMeAuthenticationProvider"/> 
         </list> 
      </property> 
   </bean>     
    <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> 
        <property name="userDetailsService" ref="jdbcDaoImpl"/>       
    </bean>      
      
    <bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl"> 
        <property name="dataSource"><ref bean="dataSource"/></property> 
    </bean> 
      
    <!-- 异常处理filter --> 
    <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> 
        <property name="authenticationEntryPoint"> 
            <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> 
                <property name="loginFormUrl" value="/acegilogin.jsp"/> 
                <property name="forceHttps" value="false"/> 
            </bean> 
        </property> 
        <property name="accessDeniedHandler"> 
            <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl"> 
                <property name="errorPage" value="/accessDenied.jsp"/> 
            </bean> 
        </property> 
    </bean>   
</beans> 


OK,大功告成。

后记:很少写技术文章,除了要坚持之外,文笔和思路都很重要。感觉自己的写作水平太差了,希望大家指出不合理的地方。有时间我会再写后篇《学习Acegi-授权(authorization)》,感谢大家把拙文看完,TKS!


声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。若作者同意转载,必须以超链接形式标明文章原始出处和作者。

你可能感兴趣的:(spring,jsp,xml,MyEclipse,Acegi)