论坛首页 Java版 Spring

用 acegi 控制url权限

浏览 7764 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
最后更新时间:2005-08-22
为公司的权限控制模块做的一个spike,尚未完毕。使用的是acegi v0.82版。
1。web.xml加入acegi的filter:
         [code:1]<filter>
         <filter-name>Acegi  Security  System  for  Spring  HttpSession  Integration  Filter</filter-name>
         <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
         <init-param>
             <param-name>targetClass</param-name>
             <param-value>net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter</param-value>
         </init-param>
     </filter>
    
     <filter>
        <filter-name>Acegi  Authentication  Processing  Filter</filter-name>
        <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
            <param-value>net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter</param-value>
        </init-param>
    </filter>

    <filter>
        <filter-name>Acegi  HTTP  Request  Security  Filter</filter-name>
        <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
        <init-param>
            <param-name>targetClass</param-name>
            <param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value>
        </init-param>
    </filter>


     <filter-mapping>
       <filter-name>Acegi  Security  System  for  Spring  HttpSession  Integration  Filter</filter-name>
       <url-pattern>/*</url-pattern>
     </filter-mapping>
        
    <filter-mapping>
        <filter-name>Acegi  Authentication  Processing  Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
        
    <filter-mapping>
        <filter-name>Acegi  HTTP  Request  Security  Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> [/code:1]
2.applicationContext-security.xml配置
[code:1]<?xml  version="1.0"  encoding="UTF-8"?>
<!DOCTYPE  beans  PUBLIC  "-//SPRING//DTD BEAN/EN"  "http://www.springframework.org/dtd/spring-beans.dtd"  >
<beans>

    <bean  id="jdbcDaoImpl"  class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
       <property  name="dataSource"><ref  bean="dataSource"/></property>
    </bean>
   
    <bean  id="daoAuthenticationProvider"  class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
         <property  name="authenticationDao">
             <ref  local="jdbcDaoImpl"/>
         </property>
    </bean>
   
    <bean  id="authenticationManager"  class="net.sf.acegisecurity.providers.ProviderManager">
        <property  name="providers">
            <list>
                <ref  bean="daoAuthenticationProvider"/>
            </list>
        </property>
    </bean>
   
    <bean  id="authenticationProcessingFilter"  class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
        <property  name="authenticationManager">
            <ref  bean="authenticationManager"/>
        </property>
        <property  name="authenticationFailureUrl">
            <value>/login.jsp?error=1</value>
        </property>
        <property   name="defaultTargetUrl">
            <value>/</value>
        </property>
        <property  name="filterProcessesUrl">
            <value>/j_acegi_security_check</value>
        </property>
    </bean>
   
    <bean  id="roleVoter"  class="net.sf.acegisecurity.vote.RoleVoter"/>
    <!--  投票策略:
    Acegi安全系统中,使用投票策略的AccessDecisionManager共有三个具体实现类:
    AffirmativeBased、  ConsensusBased和UnanimousBased。它们的投票策略是,
    AffirmativeBased类只需有一个投票赞成即可通过; 
    ConsensusBased类需要大多数投票赞成即可通过;
    而UnanimousBased类需要所有的投票赞成才能通过。
    -->
    <bean  id="accessDecisionManager"  class="net.sf.acegisecurity.vote.AffirmativeBased">
        <property  name="allowIfAllAbstainDecisions">
            <value>false</value>
        </property>
        <property  name="decisionVoters">
            <list>
               <ref  local="roleVoter"/>
            </list>
        </property>
    </bean>

    <bean  id="securityEnforcementFilter"  class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
        <property  name="filterSecurityInterceptor">
            <ref  bean="filterInvocationInterceptor"/>
        </property>
        <property  name="authenticationEntryPoint">
            <ref  bean="authenticationEntryPoint"/>
        </property>
    </bean>


    <!--bean  id="httpSessionIntegrationFilter"  class="net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter"/-->
   
    <bean  id="httpSessionIntegrationFilter"  class="net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter">
        <property  name="context">
            <value>net.sf.acegisecurity.context.SecurityContextImpl</value>
        </property>
    </bean>
       
    <bean  id="authenticationEntryPoint"  class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
        <property  name="loginFormUrl">
            <value>/login.jsp</value>
        </property>
    </bean>
   
    <bean  id="filterInvocationInterceptor"  class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
        <property  name="authenticationManager">
            <ref  bean="authenticationManager"/></property>
        <property  name="accessDecisionManager">
            <ref  bean="accessDecisionManager"/></property>
        <property  name="objectDefinitionSource">
         <!--value>
             CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
            PATTERN_TYPE_APACHE_ANT
             /roles/**=ROLE_SUPERVISOR
             /users/**=ROLE_12
         </value-->
            <!--  将角色的url权限配置移到数据库  -->
         <ref  local="objectDefinitionSource"/>
        </property>
    </bean>
    <bean  id="objectDefinitionSource"  class="com.sunbor.demo.service.acegi.FilterInvocationDefinitionSourceImp">
        <property  name="urlsService"><ref  bean="urlsService"/></property>
    </bean>
</beans>[/code:1]


3。数据库schema(sql server 2000):
[code:1]alter table acl_permission drop constraint FK32AAC8A4225BBDD9
alter table authorities drop constraint FK2B0F1321F70AE816
alter table url_permissions drop constraint FK63D169B4EF611297
drop table acl_object_identity
drop table acl_permission
drop table authorities
drop table url_permissions
drop table urls
drop table users
create table acl_object_identity (
id numeric(19,0) identity not null,
object_identity numeric(19,0) null,
parent_object varchar(32) null,
acl_class varchar(32) null,
primary key (id)
)
create table acl_permission (
id numeric(19,0) identity not null,
recipient varchar(32) null,
mask int null,
objectid numeric(19,0) null,
primary key (id)
)
create table authorities (
username varchar(32) not null,
authority varchar(32) not null,
primary key (username, authority)
)
create table url_permissions (
permission_id numeric(19,0) identity not null,
roles varchar(32) not null,
url_id numeric(19,0) null,
primary key (permission_id)
)
create table urls (
url_id numeric(19,0) identity not null,
name varchar(32) null,
url varchar(250) null,
primary key (url_id)
)
create table users (
username varchar(32) not null,
password varchar(32) null,
name varchar(32) null,
enabled tinyint null,
primary key (username)
)
alter table acl_permission
add constraint FK32AAC8A4225BBDD9
foreign key (objectid)
references acl_object_identity
alter table authorities
add constraint FK2B0F1321F70AE816
foreign key (username)
references users
alter table url_permissions
add constraint FK63D169B4EF611297
foreign key (url_id)
references urls[/code:1]
4。为了实现角色的url权限动态配置,将权限信息保存到数据库表url_permissions 中,然后在安全检查的时候,从数据库读取url的角色权限。下面是自定义FilterInvocationDefinitionSource的实现类:

[code:1]package com.sunbor.demo.service.acegi;

import java.util.List;

import net.sf.acegisecurity.ConfigAttributeDefinition;
import net.sf.acegisecurity.SecurityConfig;
import net.sf.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sunbor.demo.model.Urlpermissions;
import com.sunbor.demo.model.Urls;
import com.sunbor.demo.service.IUrlsService;

public class FilterInvocationDefinitionSourceImp extends PathBasedFilterInvocationDefinitionMap
        implements
        IFilterInvocationDefinitionSource
        {
    private static final Log logger = LogFactory.getLog(FilterInvocationDefinitionSourceImp.class);
   
    private IUrlsService urlsService;

    public FilterInvocationDefinitionSourceImp() {
        super();
    }
    public ConfigAttributeDefinition lookupAttributes(String url) {
        logger.info("request url:"+url);

        return super.lookupAttributes(url);
    }
    private void init(){
        ConfigAttributeDefinition config = new ConfigAttributeDefinition();
   
        List urls = urlsService.listUrls();
        logger.info("-------------------- urls size1:"+urls.size());
        for(int i=0;i<urls.size();i++){
            Urls url = (Urls)urls.get(i);
            logger.info("================ url:"+url.getUrl()+" and permission size:"+url.getUrlpermissions().size());
            if(url.getUrlpermissions().isEmpty()) continue;
            config = new ConfigAttributeDefinition();
            for(int j=0;j<url.getUrlpermissions().size();j++){
                Urlpermissions urlpermission = (Urlpermissions)url.getUrlpermissions().get(j);
                logger.info("================ url:"+url.getUrl()+" permission:"+urlpermission.getRoles());
                SecurityConfig     sc = new SecurityConfig(urlpermission.getRoles());
               
                config.addConfigAttribute(sc);
            }
            addSecureUrl(url.getUrl(),config);
        }

    }

    public void setUrlsService(IUrlsService urlsService) {
        this.urlsService = urlsService;
        init();
    }
}[/code:1]

接口:


[code:1]public interface  IFilterInvocationDefinitionSource extends FilterInvocationDefinitionSource{

    public abstract void setUrlsService(IUrlsService urlsService);
}[/code:1]


这里继承了PathBasedFilterInvocationDefinitionMap类。如果要使用url正则表达式,则应继承RegExpBasedFilterInvocationDefinitionMap类。

到此,acegi的url权限控制已经有效了,由于acegi根据角色定义的前面是否为“ROLE_”开头来判断是否角色,所以在表 authorities 输入测试数据的时候,字段authority 要输入以“ROLE_”开头的字符串,url_permissions表中的roles字段也是如此。
5。汉字支持
由于使用 Modelstry 生成代码,生成的 jsp 中有包含汉字的url参数,而acegi并不支持参数包含汉字的url。虽然可以修改 Modelstry的模板,但是还是决定修改acegi的代码,使其支持url中包含汉字的参数值。
1)修改 net.sf.acegisecurity.intercept.web.FilterInvocation.java,
getRequestUrl方法,修改后如下:
[code:1]    public String getRequestUrl() {
        String pathInfo = getHttpRequest().getPathInfo();
        String queryString = getHttpRequest().getQueryString();
        //String queryString = getQueryString(request);
        if(queryString !=null && queryString.length()>0){
        try {
            queryString = decode(queryString);
           
            //System.out.println(FilterInvocation queryString:+queryString);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        }
        return getHttpRequest().getServletPath()
        + ((pathInfo == null) ? "" : pathInfo)
        + ((queryString == null) ? "" : ("?" + queryString));
    }[/code:1]

这里使用了decode方法,将url中的unicode代码转换为中文。
[code:1]    public static String decode(String s) throws     Exception {
           
            StringBuffer sb = new StringBuffer();
            for(int i=0; i<s.length(); i++) {
                char c = s.charAt(i);
                switch (c) {
                case '+':
                    sb.append(' ');
                    break;
                case '%':
                    try {
                        sb.append((char)Integer.parseInt(
                        s.substring(i+1,i+3),16));
                    }
                    catch (NumberFormatException e) {
                        throw new IllegalArgumentException();
                    }
                    i += 2;
                    break;
                default:
                    sb.append(c);
                break;
                }
            }
            String result = sb.toString();
            byte[] inputBytes = result.getBytes("8859_1");
            return new String(inputBytes,"UTF8");
    }[/code:1]

&这样将直接支持中文。

2) 不过如果访问一个未授权匿名用户访问的资源时,将会转到登陆界面登陆,在登陆后才根据授权转移到用户所访问的页面。

然而如果用户所访问的页面url中包含中文,还将会出现乱码。
&于是,如下法修改:

net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter类,
&将方法
  [code:1]   protected void sendStartAuthentication(FilterInvocation fi,
        AuthenticationException reason) throws ServletException, IOException {
        ...
        }[/code:1]

中调用上面所改的方法FilterInvocation.getRequestUrl部分
[code:1]        String targetUrl = request.getScheme()+ "://"
            + request.getServerName() +((includePort) ? (":" + port) : "")
            + request.getContextPath() +fi.getRequestUrl();[/code:1]

修改为:
[code:1]String targetUrl = request.getScheme() +"://"
        + request.getServerName() + ((includePort) ?(":" + port) : "")
        + request.getContextPath() +request.getServletPath()
        + ((request.getPathInfo() == null) ? "" :request.getPathInfo())
        + ((request.getQueryString() == null) ? "" :("?" + request.getQueryString());[/code:1]

即在保存到session中的queryString编码不改变。
   
最后更新时间:2005-08-22
引用
由于acegi根据角色定义的前面是否为“ROLE_”开头来判断是否角色,所以在表 authorities 输入测试数据的时候,字段authority 要输入以“ROLE_”开头的字符串,url_permissions表中的roles字段也是如此。

可以去掉ROLE的
[code:1]    <bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter">
        <property name="rolePrefix"><value/></property>
    </bean>
[/code:1]
   
0 请登录后投票
最后更新时间:2005-08-22
你 FilterInvocationDefinitionSourceImp 中从数据库读资源只能实现一次读取, 如果权限数据改变的话就不能动态读取了, 看看我的实现:

[code:1]
/*
* Copyright 2005-2010 the original author or autors

*    http://www.skyon.com.cn
*
* Project { SkyonFramwork }
*/
package com.skyon.framework.security.acegi.intercept.web;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

/**
* @see com.skyon.framework.security.acegi.intercept.web.FilterInvocationDefinitionSourceHolder
* @see com.skyon.framework.security.acegi.intercept.event.FilterInvocationDefinitionSourceChangedEvent
* @see com.skyon.framework.security.acegi.intercept.event.FilterInvocationDefinitionSourceListener
* @since 2005-8-7
* @author 王政
* @version $Id: SecurityEnforcementDynamicExtensionFilter.java,v 1.2 2005/08/17 03:00:45 Administrator Exp $
*/
public class SecurityEnforcementDynamicExtensionFilter extends
        SecurityEnforcementFilter implements InitializingBean {
   
    private FilterInvocationDefinitionSourceHolder definitionSourceHolder;

    /**
     * @return Returns the definitionSourceHolder.
     */
    public FilterInvocationDefinitionSourceHolder getDefinitionSourceHolder() {
        return definitionSourceHolder;
    }

    /**
     * @param definitionSourceHolder The definitionSourceHolder to set.
     */
    public void setDefinitionSourceHolder(
            FilterInvocationDefinitionSourceHolder definitionSourceHolder) {
        this.definitionSourceHolder = definitionSourceHolder;
    }

    /**
     * @see net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        super.afterPropertiesSet();
        Assert.notNull(getDefinitionSourceHolder(), " definitionSourceHolder must be specified ");
    }

    /**
     * 从 {@link FilterInvocationDefinitionSourceHolder} 中读取 {@link net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource}
     * @see net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {   
    // get the defination source form soure holder
    getFilterSecurityInterceptor().setObjectDefinitionSource(definitionSourceHolder.getFilterInvocationDefinitionSource());
        super.doFilter(request, response, chain);
    }
    
   
   
}
[/code:1]

FilterInvocationDefinitionSourceHolder:

[code:1]
/*
* Copyright 2005-2010 the original author or autors

*    http://www.skyon.com.cn
*
* Project { SkyonFramwork }
*/
package com.skyon.framework.security.acegi.intercept.web;

import org.springframework.beans.factory.FactoryBean;

import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;

/**
* <class>FilterInvocationDefinitionSourceHolder</class> use to hold the global FilterInvocationDefinitionSource,
* it keeps a static variable , if the source been changed(generally the database data),  the reload method should be called
*
* @see com.skyon.framework.security.acegi.intercept.event.FilterInvocationDefinitionSourceChangedEvent
* @see com.skyon.framework.security.acegi.intercept.event.FilterInvocationDefinitionSourceListener
* @see com.skyon.framework.security.acegi.intercept.web.SecurityEnforcementDynamicExtensionFilter
* @since 2005-8-7
* @author 王政
* @version $Id: FilterInvocationDefinitionSourceHolder.java,v 1.1 2005/08/09 01:47:28 Administrator Exp $
*/
public interface FilterInvocationDefinitionSourceHolder extends FactoryBean {

/** The Perl5 expression  */
    int REOURCE_EXPRESSION_PERL5_REG_EXP = 1;
   
    /** The ant path expression */
    int RESOURCE_EXPRESSION_ANT_PATH_KEY = 2;
   
    /**
     * Set resource expression, the value must be {@link #REOURCE_EXPRESSION_PERL5_REG_EXP} or {@link #RESOURCE_EXPRESSION_ANT_PATH_KEY}
     * @see #REOURCE_EXPRESSION_PERL5_REG_EXP
     * @see #RESOURCE_EXPRESSION_ANT_PATH_KEY
     * @param resourceExpression the resource expression
     */
    void setResourceExpression(int resourceExpression);
   
    /**
     * Set whether convert url to lowercase before comparison
     * @param convertUrlToLowercaseBeforeComparison whether convertUrlToLowercaseBeforeComparison
     */
    void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison);
   
/**
* Get the defination source, generally from a database schema
* @return the defination source
*/
    FilterInvocationDefinitionSource getFilterInvocationDefinitionSource();
   
    /**
     * reoald the defination source, if the database's data has been changed, id should be called
     * @see com.skyon.framework.security.acegi.intercept.event.FilterInvocationDefinitionSourceChangedEvent
     * @see com.skyon.framework.security.acegi.intercept.event.FilterInvocationDefinitionSourceListener#onApplicationEvent(ApplicationEvent)
     */
    void reload();
         

}

[/code:1]

如果权限数据发生变化, 使用 listener 重新 load 数据:

[code:1]
/*
* Copyright 2005-2010 the original author or autors

*    http://www.skyon.com.cn
*
* Project { SkyonFramwork }
*/
package com.skyon.framework.security.acegi.intercept.event;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.Assert;

import com.skyon.framework.security.acegi.intercept.web.FilterInvocationDefinitionSourceHolder;

/**
*
* @since 2005-8-7
* @author 王政
* @version $Id: FilterInvocationDefinitionSourceListener.java,v 1.1 2005/08/09 01:47:18 Administrator Exp $
*/
public class FilterInvocationDefinitionSourceListener implements ApplicationListener,InitializingBean {
   
    private static final Log logger = LogFactory.getLog(FilterInvocationDefinitionSourceListener.class);
   
    private FilterInvocationDefinitionSourceHolder definitionSourceHolder;
   
   
    /**
     * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
     */
    public void onApplicationEvent(ApplicationEvent event) {
        if (! FilterInvocationDefinitionSourceChangedEvent.class.isInstance(event)) {
            return;
        }
              
        if (logger.isDebugEnabled()) {
            logger.debug("Enter FilterInvocationDefinitionSourceListener, begin reload FilterInvocationDefinitionSource");
        }
       
        getDefinitionSourceHolder().reload();      
    }


    /**
     * @return Returns the definitionSourceHolder.
     */
    public FilterInvocationDefinitionSourceHolder getDefinitionSourceHolder() {
        return definitionSourceHolder;
    }


    /**
     * @param definitionSourceHolder The definitionSourceHolder to set.
     */
    public void setDefinitionSourceHolder(FilterInvocationDefinitionSourceHolder sourceHolder) {
        this.definitionSourceHolder = sourceHolder;
    }


    public void afterPropertiesSet() throws Exception {
        Assert.notNull(getDefinitionSourceHolder(), " definitionSourceHolder must be specfied ");
    }
   
}

[/code:1]
   
0 请登录后投票
最后更新时间:2005-08-23
好帖,不过acegi0.82版本之前的扩展性不好,加一些特殊的authentication流就要写很多code,还改了源码。
   
0 请登录后投票
最后更新时间:2005-08-23
我总感觉acegi动不动就一大把的filter,这样搞不会影响效率啊?速度能不受影响吗??我还是喜欢自己实现个。
   
0 请登录后投票
最后更新时间:2005-08-23
引用
这样搞不会影响效率啊?

使用正则表达式比较的话,看url及表达式复杂度咯.
一般的1秒比较100万次都可以.
   
0 请登录后投票
最后更新时间:2005-08-25
我觉得acegi的objectDefinitionSource应该能够提供一个接收集合的方法,看楼主的实现中,for循环中new对象,实在是。。。

如果能够传入一个特定格式的hashmap不是更好么?
   
0 请登录后投票
最后更新时间:2005-08-25
to:wuhaixing
多谢指点。
to:Feiing
我原来也是如此想法,以为从数据库读资源只能实现一次读取,可是做了后简单测了一测,发现acegi似乎是动态读取了。我用了一个用户访问没有权限的页面,出来access deny错误后,到数据库添加一行权限记录,然后刷新,竟然过了。
不过还是比较欣赏你的实现方式,改天有空了一定按你的方式做个例子,比较比较。
to:flyromza
我比较喜欢用java类来承载有格式的数据。个人记性不太好,总忘了hashmap里面放的数据是什么格式,所以我一般能不用hashmap的时候都尽量不用了。你能说说"for里面new对象,实在是。。。"中的三个句号吗?
   
0 请登录后投票
最后更新时间:2005-08-25
[quote="bibitoo712"]to:wuhaixing
to:Feiing
我原来也是如此想法,以为从数据库读资源只能实现一次读取,可是做了后简单测了一测,发现acegi似乎是动态读取了。我用了一个用户访问没有权限的页面,出来access deny错误后,到数据库添加一行权限记录,然后刷新,竟然过了。
quote]

[code:1]
不可能的, 除非你把 securityEnforcementFilter 和 filterInvocationInterceptor 都声明为 singleton = "false", 这个我已经做过很多次测试,  如果 singleton = "true" 的话, bean 加载时你的 filterInvocationInterceptor 中的 objectDefinitionSource 已经加载并被容器缓存起来, 所以不可能再变化,  但是把一个 filter 声明为 singleton = "false" 在我看来更不能接受, 所以比较好的方案就是覆盖 FilterInvocationDefinitionEditor, 通过自定义的 filterInvocationDefinitionSourceHolder 来提供 objectDefinitionSource , 然后在所有对 role 表, permission 表, resource 表 写数据时通过 observer  模式通知 filterInvocationDefinitionSourceHolder  重新读取资源权限数据

[/code:1]
   
0 请登录后投票
最后更新时间:2005-08-26
不错不错
支持一下
对了,我在web.xml里面用的是filter chain的配置方法,在context里面顺序列出filter,这样web.xml看起来简单一点
   
0 请登录后投票
论坛首页 Java版 Spring

跳转论坛:
JavaEye推荐