2007-01-06

浅谈Acegi配置

Acegi配置文档
作者:javafish(likunkun)
Email:javafish@sunxin.org
Acegi是基于Spring的一个开源的安全认证框架,现在的最新版本是1.04。Acegi的特点就是有很多的过滤器:不过我们也用不到这么多的过滤器,只是可以把它们看作为一个个的模块,在用的时候加上自己用的着的即可,由于认证的流程的方面比较复杂导致它的配置很复杂,如果能摸清它的工作原理还是不太难.下面用比较顺着人思维的流程过一遍
这里只列出常用的过滤器和拦载器
1. 过滤器:HttpSessionContextIntegrationFilter,authenticationProcessingFilter,BasicProcessingFilter,RememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter
2. 拦截器:filterSecurityInterceptor(其实它是过滤器,不过把它放在这里更能说明它的功能),methodSecurityInterceptor
看着上面的用红色标出的过滤器是用来认证(表单和HTTP基本认证,当然还有别的不过这两个比较长用)它们是资源访问的入口.其它的过滤器是用来辅助的:HttpSessionContextIntegrationFilter是用来把认证信息记录到Session中的RememberMeProcessingFilter是以cookie的形式来保存认证信息的. anonymousProcessingFilter是在匿名的时候(这时候是没有认证信息的)给这个用户分配一个匿名的认证信息,exceptionTranslationFilter总结一下异常并处理.在实际中选择适合程序的即可.
上面只是资源访问的入口,真正保护资源的是这两个拦截器:filterSecurityInterceptor,拦截URL的类(它是个过滤器)
metohdSecurityInterceptor,拦截类中方法的调用,它们为什么要拦截呢?就是想在访问或调用这些方法之前来判断一下用户是否有访问或调用的权限,有就通过,没有就踢出.
除此之外,Acegi专门做了两个管理器(实际上就是两个类,为什么会用做这两个管理器,因为认证和授权都有一些的操作,这就需要专门做两个管理器了):authenticationManager(class= org.acegisecurity.providers.ProviderManager),授权管理器accessDecisionManager(class=org.acegisecurity.vote.AffirmativeBased)
说白了一个用于认证用户,一个是用于权限的授于的
先来说认证用户,认证管理器有什么东西呢?只内置了一些提供者:这些提供者呢又是什么呢,他们是提供用户的验证身份信息的,比如从数据库或配置文件里读出用户名和密码,在用户的cookie里读出身份信息(rememberMeProcessingFilter用到的[前面讲了的,有印象吧]),或在Session里读出身份验证信息(HttpSessionContextIntegrationFilter起作用的),这里我们只说一下从数据库或配置文件里读出用户名密码来装配验证信息的,其它的配置类似可以找一下对应api在Spring里配置即可,daoAuthenticationProvider是数据库的提供者class=org.acegisecurity.providers.dao.DaoAuthenticationProvider,而它提供的服务呢又有几种,数据库和配置文件(这是Acegi的两个默认的实现)当然也可以自己实现(实现userDetailsService接口就行)
<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 local="InMemoryDaoImpl"/></property> --><!-- 这里有两种选择 -->
		<property name="userDetailsService"><ref local="jdbcDaoImpl"/></property>
	</bean>

如果用户名和密码在配置文件里可以用InMemoryDaoImpl,class=org.acegisecurity.userdetails.memory.InMemoryDaoImpl,在这个类的userMap里配置即可:javafish=java,ROLE_USER,配置了一个用户名为javafish,密码为java,用户组为ROLE_USER的用户,不过最常用的还是数据库的JDBC实现(两个二选一)org.acegisecurity.userdetails.jdbc.JdbcDaoImpl里面需要usersByUsernameQuery和authoritiesByUsernameQuery还有数据源dataSource(有人问为什么呢,userByUsernameQuery是用来通过用户名来查密码的,authoritiesByUsernameQuery是用来通过用户名来查权限的,查询数据库肯定的用数据源吧这个里是用的SpringFrameWork的DataSource)它们查询的sql语句是有讲究的,就是查密码的时候查三个第一个是username,第二个是password,第三个是是否可用,查权限的时候查两个:username和authorities(具体看例子)
<bean id="InMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
		<property name="userMap">
			<value>
				javafish=java,ROLE_USER
			</value>
		</property>
	</bean>
	<bean id="jdbcDaoImpl" class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
		<property name="usersByUsernameQuery">
			<value>select username,password,enabled from users where username=?</value>
		</property>
		<property name="authoritiesByUsernameQuery">
			<value>select username,authority from authorities where username=?</value>
		</property>
		<property name="dataSource">
			<ref local="dataSource"/>
		</property>
	</bean>
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://localhost:3306/test</value>
        </property>
        <property name="username">
            <value>root</value>
        </property>
        <property name="password">
            <value>javafish</value>
        </property>
    </bean>

下面说一下授权,授权管理器又有什么东西呢?accessDecisionManager,Acegi把授权方面弄的比较的形象化,把某个URL或方法是否可以被访问按投票的形式来决定,

Acegi提出来了几种方案:
1. 如果有一个赞成就同意(具体的说就是只要你在那个URL对应的几个用户组中的一个就让你访问)
2. 如果都赞成就同意(具本的说就是那个URL对应的几个用户组里都有你,你才能访问)
3. 如果都不反对就同意(这个在下面讲投票者的时候再说)
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
		<property name="allowIfAllAbstainDecisions"><!-- 是否让全部弃权的通过 -->
			<value>false</value>
		</property>
		<property name="decisionVoters"><!-- 投票者们 -->
			<ref bean="roleVoter"/>
		</property>
	</bean>

而投票者呢:Acegi自己实现了一个投票者的类RoleVoter:
现在我用第一种方案,RoleVoter只是在URL对应的用户组里有ROLE_为前缀的才进行投票,否则的话弃权.(我们也可以在配置RoleVoter的时候把ROLE_配置成为别的前缀如JAVA_),分别对URL对应的每个用户组投票,如果用户在这个用户组里就投赞成,不在投反对(在用户组的前缀是ROLE_的前提下)这样就不难体会第三种方案的用途了吧
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
		<property name="rolePrefix">
			<value>ROLE_</value><!-- 可以改成别的 -->
		</property>
	</bean>

这样认证管理器和授权管理器就ok了,别的无论是过滤器还是拦截器都会用到它们两个,因为它们都要验证而这两个就是凭证.
那么那两个访问过滤器呢,先说authenticationProcessingFilter是用于表单登陆的
<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
		<property name="authenticationManager"><ref bean="authenticationManager"/></property>
		<property name="authenticationFailureUrl"><value>/failure.html</value></property><!--登陆失败转向的页面  -->
		<property name="defaultTargetUrl"><value>/ok.html</value></property><!-- 登陆成功转向的页面 -->
      	<property name="filterProcessesUrl"><value>/check</value></property><!-- 要验证的地址 -->
	</bean>

这样的话加上上面配置的认证管理器就已经可以处理登陆了(注意的是它没有用到授权管理器,因为它只是个访问入口还没有权限的授予)
再说一下HTTP基本认证:它比上面的略复杂一点
需要配置一个
<bean id="BasicProcessingFilterEntryPoint" class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
		<property name="realmName"><value>javafish</value></property><!-- 基本认证对话框上显示的字 -->
	</bean>
然后
<bean id="BasicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
		<property name="authenticationManager">
			<ref bean="authenticationManager"/>
		</property>
		<property name="authenticationEntryPoint">
			<ref bean="BasicProcessingFilterEntryPoint"/>
		</property>
	</bean>

即可.
不过在HTTP基本认证里需要注意的地方是:好多人配置好了怎么看不到效果啊,一开始我也是很郁闷,看了BasicProcessingFilter的源代码:
String header = httpRequest.getHeader("Authorization");//我们一般进入网页测试的时候这里的header始终是null的
if (logger.isDebugEnabled()) {
            logger.debug("Authorization header: " + header);
        }
        if ((header != null) && header.startsWith("Basic ")) {//从这里可以看到一般的登陆基本认证是不起作用的
.................

只有在服务器上配置哪个目录在访问的时候用HTTP基本认证,它才会起作用(一开始还以为是Acegi的BUG呢)
下面说一下真正对URL资源的保护了filterSecurityInterceptor它的本质是个过滤器,有了前面*管理器的基础了这就很容易了:
<bean id="filterSecurityInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
		<property name="authenticationManager">
			<ref local="authenticationManager"/>
		</property>
		<property name="accessDecisionManager">
			<ref local="accessDecisionManager"/>
		</property>
		<property name="objectDefinitionSource"><!-- 把URL和可访问的用户组对应起来 -->
			<value>
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<!-- 把URL全部转化为小写 -->
				PATTERN_TYPE_APACHE_ANT<!-- 以ANT的形式来配置路径 -->
				/ok.html=ROLE_USER
			</value>
		</property>
	</bean>

光这样配置还是不够的,因为当授权失败的时候会抛出异常的,我们应该配置一个异常过滤器来捕获它,exceptionTranslationFilter它是用来捕获异常的,看一下配置吧:
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
      <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property>
      <property name="accessDeniedHandler">
      	<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
      		<property name="errorPage" value="/failure.html"/><!-- 发生异常转向的网页 -->
      	</bean>
      </property>
   </bean>
   <bean id="authenticationProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
   		<property name="loginFormUrl"><value>/Login.html</value></property><!-- 得到表单的信息 -->
      	<property name="forceHttps"><value>false</value></property><!-- 不用https -->
   </bean>

这样就OK了
最后说一下对类中方法的保护:
首先写一个类并在spring中配置好:
package org.li.acegi;

public class TestAcegi
{
	public void Role()
	{
		System.out.println("javafish");
	}
}
<bean id="testAcegi" class="org.li.acegi.TestAcegi"/>

然看写个servlet访问一下它
package org.li.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.li.acegi.TestAcegi;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class TestServlet extends HttpServlet
{
	private static final long serialVersionUID = -5610016980827214773L;

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException
	{
		response.setContentType("text/html;charset=GBK");
		PrintWriter out = response.getWriter();
		ApplicationContext ctx = 
            WebApplicationContextUtils.getRequiredWebApplicationContext(request.getSession().getServletContext());
		TestAcegi test = (TestAcegi)ctx.getBean("testAcegi");
		test.Role();//访问TestAcegi类的Role方法
		out.println("调用成功");
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException
	{
		doGet(request,response);
	}

}

准备工作做好了,开始配置Acegi
先在Spring里给Acegi做个代理:
<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
		<property name="beanNames">
			<list>
				<value>testAcegi</value><!-- 要代理的Bean的id -->
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<value>methodSecurityInterceptor</value><!-- 代理为... -->
			</list>
		</property>
	</bean>

里面的methodSecurityInterceptor呢配置为:
<bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
		<property name="authenticationManager">
			<ref bean="authenticationManager"/>
		</property>
		<property name="accessDecisionManager">
			<ref bean="accessDecisionManager"/>
		</property>
		<property name="objectDefinitionSource"><!-- 对代理的类的方法开始配置权限 -->
			<value>org.li.acegi.TestAcegi.Role=ROLE_USER</value>
		</property>
	</bean>

这样当直接访问http://localhost:8080/AcegiWeb/servlet/TestServlet的时候会发现不可访问,控件台也不输出”javafish”,当输入正确的用户名和密码之后便可以访问.
这样它就对类的方法调用起了保护的作用,这一点可以把Acegi应用到DWR上效果是很理想的.
对于Acegi有很多的过滤器不用全写在web.xml里,acegi提供了一个特殊的过滤器我们可以写成这样,在Web.xml里:
<filter>
		<filter-name>Acegi</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-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/applicationContext.xml
		</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
	<listener>
        <listener-class>org.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
    </listener>
  <servlet>
    <servlet-name>TestServlet</servlet-name>
    <servlet-class>org.li.servlet.TestServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>TestServlet</servlet-name>
    <url-pattern>/servlet/TestServlet</url-pattern>
  </servlet-mapping>

在Spring的配置文件里:
<bean id="chainProxy" class="org.acegisecurity.util.FilterChainProxy">
		<property name="filterInvocationDefinitionSource">
			<value>
				CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
		    PATTERN_TYPE_APACHE_ANT
            /**=HttpSessionContextIntegrationFilter,authenticationProcessingFilter,BasicProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
			</value>
		</property>
	</bean>
评论
kkbear 2007-09-11
Thx for ur post
and I have a problem to retrieve the login info from the cookie "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE "
I used the method cookie.getValue(),but the return is some hex just like "YWRtaW46MTE3NTY3NDk3ODc5NjphYjEzNzk3YzMxYmRkMDg3YjQ2MTY0YjIzOWRlOTlmZQ"
and now i want to retrieve the loginName from the cookie,how to do that,thx.
wl1985 2007-08-16
我看我公司就是配com.crea.service.UserManager.getUsers=admin
然后资源都是固定好了的,不能新增模块
wl1985 2007-08-16
org.li.acegi.TestAcegi.Role=ROLE_USER
这句不太好懂,资源就是这样管理么?
Michael.zhl 2007-07-13
要是把例子打包出来就好了.
wensky222 2007-07-11
method判断
对这个方法的检查只看到针对某个方法的权限,这样在类和方法很多时设置很麻烦
不知道怎么能扩展到象spring2一样可以对方法模糊匹配
sg552 2007-05-27
请教几个问题:
1、密码加密如何处理;
有专门的加密类。请看Acegi代码,官方文档用的是.properties文件保存密码。

2、select username,authority from authorities where username=?取出的authority是什么,应该是role吧;
这个要看源代码了。貌似Acegi官方文档没有楼主说的这么麻烦,不用自己写SQL

3、如果像“/ok.html=ROLE_USER”这样配置,当系统运行期间,role的增删改如何处理;
这个是动态的增删改了。请参考Acegi自行斟酌。
liuganquan 2007-05-26
jamesby 写道
to daquan198163:

我也想知道你的第3个问题,我想应该从objectDifinitonSource入手吧!

acegi有两种权限判断,一种是url判断,一种是method判断,就在objectDifinitonSource里面设置就可以了
snowman6002 2007-05-25
有对证书认证整合、证书交叉认证特别了解的么?
能谈一下看法么?
klyuan 2007-05-25
是得好好学习一下!!!支持,投良好
morfil 2007-05-25
正在学习Acegi中,看了之后对其原理有了初步的认识
mazzystar 2007-03-21
我在acegi中用到了remembeme,但是在页面上选择rememberme之后没有任何反映,还得手动登陆。
cookies里的相关信息:ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE
YWRtaW46MTE3NTY3NDk3ODc5NjphYjEzNzk3YzMxYmRkMDg3YjQ2MTY0YjIzOWRlOTlmZQ==
172.10.0.40/acegiPlatform
1536
2108775680
30213545
575214272
29846418
*

不知道还有哪里没配到的,以下是相关的配置:
1.login.jsp中加入了一个单选钮:保存我的信息<input type="checkbox" name="rememberme"/>
2.认证filter
bean id="authenticationProcessingFilter"
		class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
		<property name="authenticationManager"
			ref="authenticationManager" />
		<!-- 登陆失败转向页 -->
		<property name="authenticationFailureUrl"
			value="/jsp/login.jsp?login_error=1" />
		<!-- 登陆成功转向页 -->
		<property name="defaultTargetUrl" value="/jsp/s.jsp" />
		<!-- 对应表单的action请求 -->
		<property name="filterProcessesUrl"
			value="/jsp/j_acegi_security_check" />
		<property name="rememberMeServices" ref="rememberMeServices" />
		<property name="exceptionMappings">
			<value>
				org.acegisecurity.AuthenticationException=/jsp/login.jsp?login_error=user_psw_error
				org.acegisecurity.concurrent.ConcurrentLoginException=/jsp/login.jsp?login_error=too_many_user_error
			</value>
		</property>
	</bean>

3.认证管理器
<bean id="authenticationManager"
		class="org.acegisecurity.providers.ProviderManager">
		<property name="providers"><!-- 可有多个认证提供器,其中一个证通过就可以了 -->
			<list>
				<ref local="rememberMeAuthenticationProvider" />
				<ref local="daoAuthenticationProvider" />
				<ref local="anonymousAuthenticationProvider" />
			</list>
		</property>
		<property name="sessionController">
			<ref bean="concurrentSessionController" />
		</property>

	</bean>

4.
<!--
		当SecurityContextHolder中不存在Authentication.用户授权信息,
		rememberMeProcessingFilter就会调用autoLogin()方法从cookie中获取用户信息,在验证filter之前使用
	-->
	<bean id="rememberMeProcessingFilter"
		class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
		<property name="authenticationManager"
			ref="authenticationManager" />
		<property name="rememberMeServices" ref="rememberMeServices" />
	</bean>

	<!--
		提供rememberMe功能
		
		登陆验证时:登陆成功后会调用loginSuccess方法,在cookie里面保留用户信息,
		格式为:username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
		
		访问页面时:rememberMeProcessingFilter会调用autoLogin方法,在cookie中获取用户信息
	-->
	<bean id="rememberMeServices"
		class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
		<property name="userDetailsService" ref="jdbcDaoImpl" />
		<property name="authenticationDetailsSource"
			ref="authenticationDetailsSourceHelper" />
		<property name="key" value="springRocks" />
		<property name="parameter" value="rememberme" />
	</bean>

	<bean id="authenticationDetailsSourceHelper"
		class="edu.ccut.acegi.intercept.AuthenticationDetailsSourceHelper" />

	<!--
		验证rememberMe证明信息的合法性,利用两个的key来进行比较验证
	-->
	<bean id="rememberMeAuthenticationProvider"
		class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
		<property name="key" value="springRocks" />
	</bean>

	<bean id="sessionRegistry"
		class="org.acegisecurity.concurrent.SessionRegistryImpl" />

	<!--
		退出登陆并且remember-me cookie
	-->
	<bean id="logoutFilter"
		class="org.acegisecurity.ui.logout.LogoutFilter">
		<property name="filterProcessesUrl" value="/jsp/j_acegi_logout" />
		<!-- 退出后指向的 URL -->
		<constructor-arg value="/jsp/index.jsp" />
		<constructor-arg>
			<list>
				<ref bean="rememberMeServices" />
				<bean
					class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" />
				<ref bean="sessionLogoutHandler" />
			</list>
		</constructor-arg>
	</bean>
	<bean id="sessionLogoutHandler"
		class="edu.ccut.acegi.intercept.SessionLogoutHandler">
		<property name="sessionRegistry">
			<ref bean="sessionRegistry" />
		</property>
	</bean>


在我登陆一次并关闭浏览器时,cookies文件还在,当重新打开浏览器并访问登陆页面时cookies文件被删除。不知道cookies是被什么删除的。
请大家给点建议。
ahau205109 2007-03-17

晨星★~雨泪 写道:

ahau205109 写道:

daquan198163 写道:
请教几个问题:
1、密码加密如何处理;
2、select username,authority from authorities where username=?取出的authority是什么,应该是role吧;
3、如果像“/ok.html=ROLE_USER”这样配置,当系统运行期间,role的增删改如何处理;


1.密码加密

  原理解析:

(1)authenticationProcessingFilter 中会从你的登陆页面获得用户名和密码

 (2)userDetatilService.loadUserByUsername(String username)需要你根据用户名把用户的信息(

 用户名,密码,用户权限)放到你自己的UserDetail;

(3) 调用daoAuthenticationProvider中的passwordEncoder对页面密码进行加密,判断与数据库得到的密钥

     是否相等;


   
     
   

   
     
     
   

 

2.是权限

3.只能自己扩展objectDefinitionSource扩展方式


本人还是最总关心第三个问题

哪有写好了objectDefinitionSource的扩展??

期待中。。。。。。

  且看配置:

   objectDefinitionSource :  FilterInvocationDefinitionSource

  FilterInvocationDefinitionSourceEditor类

加载权限方法:

权限定义:

s参数值格式如下:

CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
        PATTERN_TYPE_APACHE_ANT
        /secure/extreme/**=ROLE_SUPERVISOR
        /secure/**=IS_AUTHENTICATED_REMEMBERED
        /**=IS_AUTHENTICATED_ANONYMOUSLY

public void setAsText(String s) throws IllegalArgumentException {
        FilterInvocationDefinitionDecorator source = new FilterInvocationDefinitionDecorator();

        if ((s == null) || "".equals(s)) {
            // Leave target object empty
            source.setDecorated(new RegExpBasedFilterInvocationDefinitionMap());
        } else {
            // Check if we need to override the default definition map
            if (s.lastIndexOf(DIRECTIVE_PATTERN_TYPE_APACHE_ANT) != -1) {
                source.setDecorated(new PathBasedFilterInvocationDefinitionMap());

                if (logger.isDebugEnabled()) {
                    logger.debug(("Detected " + DIRECTIVE_PATTERN_TYPE_APACHE_ANT
                        + " directive; using Apache Ant style path expressions"));
                }
            } else {
                source.setDecorated(new RegExpBasedFilterInvocationDefinitionMap());
            }

            if (s.lastIndexOf(DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON) != -1) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Detected " + DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                        + " directive; Instructing mapper to convert URLs to lowercase before comparison");
                }

                source.setConvertUrlToLowercaseBeforeComparison(true);
            }

            BufferedReader br = new BufferedReader(new StringReader(s));
            int counter = 0;
            String line;

            List mappings = new ArrayList();

            while (true) {
                counter++;

                try {
                    line = br.readLine();
                } catch (IOException ioe) {
                    throw new IllegalArgumentException(ioe.getMessage());
                }

                if (line == null) {
                    break;
                }

                line = line.trim();

                if (logger.isDebugEnabled()) {
                    logger.debug("Line " + counter + ": " + line);
                }

                if (line.startsWith("//")) {
                    continue;
                }

                // Attempt to detect malformed lines (as per SEC-204)
                if (line.lastIndexOf(DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON) != -1) {
                    // Directive found; check for second directive or name=value
                    if ((line.lastIndexOf(DIRECTIVE_PATTERN_TYPE_APACHE_ANT) != -1) || (line.lastIndexOf("=") != -1)) {
                        throw new IllegalArgumentException("Line appears to be malformed: " + line);
                    }
                }

                // Attempt to detect malformed lines (as per SEC-204)
                if (line.lastIndexOf(DIRECTIVE_PATTERN_TYPE_APACHE_ANT) != -1) {
                    // Directive found; check for second directive or name=value
                    if ((line.lastIndexOf(DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON) != -1)
                        || (line.lastIndexOf("=") != -1)) {
                        throw new IllegalArgumentException("Line appears to be malformed: " + line);
                    }
                }

                // Skip lines that are not directives
                if (line.lastIndexOf('=') == -1) {
                    continue;
                }

                if (line.lastIndexOf("==") != -1) {
                    throw new IllegalArgumentException("Only single equals should be used in line " + line);
                }

                // Tokenize the line into its name/value tokens
                // As per SEC-219, use the LAST equals as the delimiter between LHS and RHS
                String name = StringSplitUtils.substringBeforeLast(line, "=");
                String value = StringSplitUtils.substringAfterLast(line, "=");

                if (!StringUtils.hasText(name) || !StringUtils.hasText(value)) {
                    throw new IllegalArgumentException("Failed to parse a valid name/value pair from " + line);
                }

                // Attempt to detect malformed lines (as per SEC-204)
                if (source.isConvertUrlToLowercaseBeforeComparison()
                    && source.getDecorated() instanceof PathBasedFilterInvocationDefinitionMap) {
                    // Should all be lowercase; check each character
                    // We only do this for Ant (regexp have control chars)
                    for (int i = 0; i < name.length(); i++) {
                        String character = name.substring(i, i + 1);

                        if (!character.toLowerCase().equals(character)) {
                            throw new IllegalArgumentException("You are using the "
                                + DIRECTIVE_CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                                + " with Ant Paths, yet you have specified an uppercase character in line: " + line
                                + " (character '" + character + "')");
                        }
                    }
                }

                FilterInvocationDefinitionSourceMapping mapping = new FilterInvocationDefinitionSourceMapping();
                mapping.setUrl(name);

                String[] tokens = org.springframework.util.StringUtils
                        .commaDelimitedListToStringArray(value);

                for (int i = 0; i < tokens.length; i++) {
                    mapping.addConfigAttribute(tokens[i].trim());
                }

                mappings.add(mapping);
            }
            source.setMappings(mappings);
        }

        setValue(source.getDecorated());
    }

  
=========================================

定义自己的类:

package common.spring.acegi.business;

import java.util.*;

import org.acegisecurity.*;
import org.acegisecurity.intercept.*;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
import org.acegisecurity.intercept.web.FilterInvocationDefinitionDecorator;
import org.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;
import org.acegisecurity.intercept.web.FilterInvocationDefinitionSourceMapping;

import common.spring.acegi.vo.PreviligeVo;

public class MObjectDefinitionSource implements ObjectDefinitionSource {
    private MObjectDefinitionSource() {

    }

    private static MObjectDefinitionSource mobjectDefinitionSource;
    public MObjectDefinitionSource getInstance() {
        if (mobjectDefinitionSource == null) {
            mobjectDefinitionSource = new MObjectDefinitionSource();
        }
        return mobjectDefinitionSource;
    }

    private static final Log logger = LogFactory.getLog(
            MObjectDefinitionSource.class);
    private boolean lowercase = true; //采取的匹配模式
    //Object 是FillterInvocation
    public ConfigAttributeDefinition getAttributes(Object object) throws
            IllegalArgumentException {
        if (!init) {
            this.initConfigAttributeDefinitions();
        }
        return source.getAttributes(object);
    }

    public Iterator getConfigAttributeDefinitions() {
        if (!init) {
            this.initConfigAttributeDefinitions();
        }
        return source.getConfigAttributeDefinitions();
    }

    public boolean supports(Class clazz) {
        return true;
    }

    public void setLowercase(boolean lowercase) {
        this.lowercase = lowercase;
    }

    public void setPreviligeType(Integer[] previligeType) {
        this.previligeType = previligeType;
    }

    public boolean isLowercase() {
        return lowercase;
    }

    public Integer[] getPreviligeType() {
        return previligeType;
    }

    //需要加载的权限类型
    private Integer[] previligeType;
    private static FilterInvocationDefinitionDecorator source;
    private static boolean init = false;
    /*
     * 功能:重新更新权限列表
     **/
    public static void refreshSource() {
        mobjectDefinitionSource.initConfigAttributeDefinitions();
    }

    /*
     * 加载权限
     **/
    private void initConfigAttributeDefinitions() {
        source = null;
        //查找系统的权限
        source = new FilterInvocationDefinitionDecorator();
        source.setDecorated(new PathBasedFilterInvocationDefinitionMap());
        source.setConvertUrlToLowercaseBeforeComparison(this.lowercase);
        List mappings = new ArrayList();
        PreviligeVo vo[] = PreviligeFactory.loadPrevilige(this.previligeType);
        if (vo == null) {
            vo = new PreviligeVo[0];
        }
        for (int i = 0; i < vo.length; i++) {
            FilterInvocationDefinitionSourceMapping mapping = new
                    FilterInvocationDefinitionSourceMapping();
            mapping.setUrl(vo[i].getCode()); //其实就是url 如 /login.html
            mapping.setConfigAttributes(Arrays.asList(vo[i].getRole())); //login.html里的角色
            mappings.add(mapping);
        }
        source.setMappings(mappings);
        init = true;
    }

}





晨星★~雨泪 2007-03-17

ahau205109 写道:

daquan198163 写道:
请教几个问题:
1、密码加密如何处理;
2、select username,authority from authorities where username=?取出的authority是什么,应该是role吧;
3、如果像“/ok.html=ROLE_USER”这样配置,当系统运行期间,role的增删改如何处理;


1.密码加密

  原理解析:

(1)authenticationProcessingFilter 中会从你的登陆页面获得用户名和密码

 (2)userDetatilService.loadUserByUsername(String username)需要你根据用户名把用户的信息(

 用户名,密码,用户权限)放到你自己的UserDetail;

(3) 调用daoAuthenticationProvider中的passwordEncoder对页面密码进行加密,判断与数据库得到的密钥

     是否相等;


   
     
   

   
     
     
   

 

2.是权限

3.只能自己扩展objectDefinitionSource扩展方式


本人还是最总关心第三个问题

哪有写好了objectDefinitionSource的扩展??

期待中。。。。。。


ricsson 2007-03-17
正在学习这东西,感谢楼主!
ahau205109 2007-03-15

daquan198163 写道:
请教几个问题:
1、密码加密如何处理;
2、select username,authority from authorities where username=?取出的authority是什么,应该是role吧;
3、如果像“/ok.html=ROLE_USER”这样配置,当系统运行期间,role的增删改如何处理;


1.密码加密

  原理解析:

(1)authenticationProcessingFilter 中会从你的登陆页面获得用户名和密码

 (2)userDetatilService.loadUserByUsername(String username)需要你根据用户名把用户的信息(

 用户名,密码,用户权限)放到你自己的UserDetail;

(3) 调用daoAuthenticationProvider中的passwordEncoder对页面密码进行加密,判断与数据库得到的密钥

     是否相等;

<bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
    <property name="userDetailsService">
      <ref local="userDetailsService"/>
    </property>
    <property name="passwordEncoder">
      <ref local="passwordEncoder"/>
      <!--密码加密算法,客户端输入的密码是明文,但是数据库可能是密钥-->
    </property>

</bean>

2.是权限

3.只能自己扩展objectDefinitionSource扩展方式

huzai 2007-03-14
您写到:只有在服务器上配置哪个目录在访问的时候用HTTP基本认证,它才会起作用(一开始还以为是Acegi的BUG呢)

怎么配:配置哪个目录在访问的时候用HTTP基本认证呀?

谢谢!
zhou_song 2007-03-13
管理员大哥,这里边的内容能不能不要两边拉动啊。看起来真烦哦。
landyou 2007-03-11
不错,写的很清楚,看了之后总体上更加清楚了
zt_smile 2007-01-29
这是我看到ACEGI中写的最通俗易懂的文章,谢谢楼主。
jamesby 2007-01-10
to daquan198163:

我也想知道你的第3个问题,我想应该从objectDifinitonSource入手吧!
likunkun
搜索本博客
最近加入圈子
最新评论
评论排行榜