- 浏览: 26089 次
- 性别:
- 来自: 济南
最新评论
-
raykcn:
垃圾死了标题是 ss3, 内容的xml是“spring-sec ...
spring security 3.0 的 用户详细信息的 session 扩展 (基于rapid framework) -
boy in the road:
C:\Documents and Settings\Admin ...
JAVA命令参数学习
在通过各种认证途径获得Authentication认证对象后,事情的发展并没有结束。用户认证只是确定当前用户是否存在,而且她的身份也被辨认出来。至于已经认证的用户是否有权限操控目标资源(web资源,业务方法,领域对象),那么就还需要经过用户授权的考验!
下面就总体上给出acegi是如何实施用户授权工作的,以及围绕web资源的授权操作展开!
当acegi完成用户的认证操作时,认证结果会存储到Authentication对象中。针对不同的场合,Acegi内置了多种Authentication对象类型。比如:PrincipalAcegiUserToken,UsernamePasswordAuthenticationToken,X509AuthenticationToken等,他们必须实现Authentication接口!
那么acegi是如何实施用户授权的呢?当已经认证的用户视图通过手中的校色集合操控目标资源时,acegi内置的AOP拦截器会采用公平投票机制评估这一举动,实际上公平投票贯穿于目标资源的调用前后,调用前,AOP拦截器确保当前用户确实有要求的角色集合;调用后,AOP拦截器会审核操作结果,比如从返回的领域对象集合中删除当前用户不能够操控的那些领域对象!
在实施用户授权的过程中,拦截器起到的重要的作用,比如:FilterSecurityInterceptor是一个过滤器,针对web资源;MethodSecurityInterceptor是一个AOP拦截器,针对业务方法实施授权;AspectJSecurityInterceptor拦截器,真对AspectJ方法调用实施用户授权!
这些拦截器都会分为三个阶段:事前评估,操控资源,事后审查!
事前评估:
下面的配置信息,其中accessDecisionManager属性正是完成事前评估的!
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" />
<property name="objectDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/securedpage.jsp=ROLE_ADMIN
</value>
</property>
</bean>
上述httpRequestAccessDecisionManager的定义如下,开发者需要为AffirmativeBased配置若干投票器(AccessDecisionVoter)
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value=“false”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
其中AffirmativeBased是AccessDecisionManager的子类,还有ConsensusBased,UnanimousBased。他们的含义和使用是有区别的!
AccessDecisionManager采用民主决策机制判断用户是否有权访问目标程序资源,它包含了多个AccessDecisionVoter。在访问决策时每个AccessDecisionVoter都拥有投票权,AccessDecisionManager统计投票结果,并按照某种决策方式根据这些投票结果决定最终是否向用户开放受限资源的访问。Acegi的投票方式有多种,每个Voter对访问权可以投赞成(ACCESS_GRANTED,取值为1),弃权(ACCESS_ABSTAIN,取值为0)或反对(ACCESS_DENIED,取值为-1)!
Acegi提供了三个具体的类去管理投票结果,他们三个的不同存在于对投票结果的统计上。ConsensusBased类根据非弃权票中赞成票多于反对票来决定是否通过。如果赞成票多于反对票就同意通过!在同意和反对票相同时或都投弃权票时可以根据设置来决定授权与否。配置如下:
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.ConsensusBased">
<property name="allowIfAllAbstainDecisions" value=“true”>
<property name="allowIfEqualGrantedDeniedDecisions" value=“true”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
其中属性allowIfAllAbstainDecisions,默认值是false,意味着当配置的投票器只投了弃权票时,不允许继续执行。相比之下,true意味着允许都投弃权票!
allowIfEqualGrantedDeniedDecisions,默认值为true,意味着在赞成票不为0的情况下,且赞成票和反对票相等,则允许通过!false意味着,在赞成票不为0 的情况下,赞成票和反对票相等,不允许通过!
AffirmativeBased类只要有一个或多个投票赞成就会给予通过,同样我们可以设置在都投弃权票的时候是否通过:
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value=“false”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
UnanimousBased类会在所有的非弃权票都投赞成票时才通过,同样的设置出现在都投弃权票的时候:
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.UnanimousBased">
<property name="allowIfAllAbstainDecisions" value=“true”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
Acegi提供了AccessDecisionVoter的两个实现类。对于RoleVoter,我们需要配置它的rolePrefix属性(如ROLE_),当ConfigAttribute以rolePrefix开头时,RoleVoter才投票,否则投弃权票(ACCESS_ABSTAIN)。开始投票时,如果存在一个GrantedAuthority和被访问资源的一个或多个ConfigAttributes完全匹配时,投赞成票,否则投反对票
针对web资源,业务方法,领域对象的用户授权,acegi内置了各种投票器!其中RoleVoter主要针对web资源和业务方法,而AclEntryVoter主要针对领域对象!怎样使用,在后面的章节将!
事后审查:
一旦用户操控到目标资源后,调用结果可能会返回一个领域对象集合,比如,getAll()方法便会返回所有的对象集合!但是并不是任意用户都能够操作到任意领域对象的,我们需要保护他们!
在实施领域对象级的访问控制时(事后审查),acegi的AfterInvocationManager充当了非常主要的角色!
<bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property>
<property name="afterInvocationManager"><ref local="afterInvocationManager"/></property>
<property name="objectDefinitionSource">
<value>
sample.contact.ContactManager.create=ROLE_USER
sample.contact.ContactManager.getAllRecipients=ROLE_USER
sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ
sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ
sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN
sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN
</value>
</property>
</bean>
AfterInvocationManager会对业务对象操作的结果进行审查,比如操作结果中是否含有当前用户无权操作的领域对象,如果有,AfterInvocationManager会从操作结果中将这一领域对象删除!具体怎么回事?后面会讲.
保护web资源:FilterSecurityInterceptor
重新认证:
一旦用户登陆到目标系统后,他便依据其本来的身份进行各种操作,但是在一些场合用户的身份可能会被动态的改变!为了将RDBMS中角色的信息的变化同步到已经登陆的用户中,必须启用alwaysReauthenticate属性,默认值是false:
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="alwaysReauthenticate" value="true"></property>
.......
</bean>
RoleVoter投票器:
对于RoleVoter,默认时候只会对那些前缀为ROLE_的角色进行投票处理,吐过角色信息未使用ROLE_开头,我们需要配置它的rolePrefix属性,当ConfigAttribute以rolePrefix开头时,RoleVoter才投票,否则投弃权票(ACCESS_ABSTAIN)。开始投票时,如果存在一个GrantedAuthority和被访问资源的一个或多个ConfigAttributes完全匹配时,投赞成票,否则投反对票
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" >
<property name="rolePrefix" value="Acegi_" />
</bean>
开发者配置的各个角色,必须存在相应的投票器对他们进行处理,否则目标Acegi应用不能正常启动!除非validateConfigAttributes属性被显式的置为false。不过建议设置为true!
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="validateConfigAttributes" value="true" />
.......
</bean>
把web资源授权信息存储在RDBMS中:
分析FilterInvocationDefinfitionSource的运行:
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/index.jsp=ROLE_ANONYMOUS,ROLE_USER
/hello.htm=ROLE_ANONYMOUS,ROLE_USER
/logoff.jsp=ROLE_ANONYMOUS,ROLE_USER
/acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
/**=ROLE_USER
</value>
</property>
</bean>
其中objectDefinitionSource属性属于FilterInvocationDefinfitionSource类型!
PATTERN_TYPE_APACHE_ANT:启用了apache ant路径风格的url匹配模式(PathBasedFilterInvocationDefinfitionMap),默认的是正则表达式(RegExpBasedFilterInvocationDefinfitionMap),他们都是FilterInvocationDefinfitionSource的子类!为了通过DBMS维护web资源授权信息,必须自己提供FilterInvocationDefiniftionSource子类!
实现基于DBMS的FilterInvocationDefiniftionSource
建表:
create table webresdb(
id bigint not null primary key,
url varchar(60) not null,
roles varchar(100) not null)
insert into webresdb values(1,'/securedpage.jsp','ROLE_ADMIN');
编写RdbmsEntryHolder类:
package websourcedb;
import java.io.Serializable;
import org.acegisecurity.ConfigAttributeDefinition;
/**
*
* @author worldheart
*
*/
public class RdbmsEntryHolder implements Serializable{
//保护的URL模式
private String url;
//要求的角色集合
private ConfigAttributeDefinition cad;
public String getUrl() {
return url;
}
public ConfigAttributeDefinition getCad() {
return cad;
}
public void setUrl(String url) {
this.url = url;
}
public void setCad(ConfigAttributeDefinition cad) {
this.cad = cad;
}
}
从EDBMS装载web资源授权信息,并摆在内存中:
package websourcedb;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.SecurityConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.util.StringUtils;
/**
*
* @author worldheart
*
*/
public class RdbmsSecuredUrlDefinition extends MappingSqlQuery{
protected static final Log logger = LogFactory.getLog(RdbmsSecuredUrlDefinition.class);
protected RdbmsSecuredUrlDefinition(DataSource ds) {
super(ds, "SELECT url, roles FROM webresdb ORDER BY id");
logger.debug("进入到RdbmsInvocationDefinition构建器中.........");
compile();
}
protected Object mapRow(ResultSet rs, int rownum)
throws SQLException {
logger.debug("抽取webresdb中的记录.........");
RdbmsEntryHolder rsh = new RdbmsEntryHolder();
//设置URL
rsh.setUrl(rs.getString("url").trim());
ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
String[] tokens =
StringUtils.commaDelimitedListToStringArray(rs.getString("roles").trim());
for(int i = 0; i < tokens.length;++i)
cad.addConfigAttribute(new SecurityConfig(tokens[i]));
//设置角色集合
rsh.setCad(cad);
return rsh;
}
}
最后开发FilterInvocationDefiniftionSource实现类RdbmsFilterInvocationDefiniftionSource是针对web资源的,因此她实现的supports()方法需要评估FilterInvocation对象,并且采用了Spring EhCache集成服务!
package websourcedb;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.intercept.web.FilterInvocation;
import org.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
*
* @author worldheart
*
*/
public class RdbmsFilterInvocationDefinitionSource extends JdbcDaoSupport
implements FilterInvocationDefinitionSource {
protected static final Log logger = LogFactory.getLog(RdbmsFilterInvocationDefinitionSource.class);
private RdbmsSecuredUrlDefinition rdbmsInvocationDefinition;
private PathMatcher pathMatcher = new AntPathMatcher();
private Ehcache webresdbCache;
public ConfigAttributeDefinition getAttributes(Object object)
throws IllegalArgumentException {
if ((object == null) || !this.supports(object.getClass())) {
throw new IllegalArgumentException("抱歉,目标对象不是FilterInvocation类型");
}
// 抽取出待请求的URL
String url = ((FilterInvocation) object).getRequestUrl();
List list = this.getRdbmsEntryHolderList();
if (list == null || list.size() == 0)
return null;
int firstQuestionMarkIndex = url.indexOf("?");
if (firstQuestionMarkIndex != -1) {
url = url.substring(0, firstQuestionMarkIndex);
}
Iterator iter = list.iterator();
while (iter.hasNext()) {
RdbmsEntryHolder entryHolder = (RdbmsEntryHolder) iter.next();
boolean matched = pathMatcher.match(entryHolder.getUrl(), url);
if (logger.isDebugEnabled()) {
logger.debug("匹配到如下URL: '" + url + ";模式为 "
+ entryHolder.getUrl() + ";是否被匹配:" + matched);
}
if (matched) {
return entryHolder.getCad();
}
}
return null;
}
public Iterator getConfigAttributeDefinitions() {
Set set = new HashSet();
Iterator iter = this.getRdbmsEntryHolderList().iterator();
while (iter.hasNext()) {
RdbmsEntryHolder entryHolder = (RdbmsEntryHolder) iter.next();
set.add(entryHolder.getCad());
}
return set.iterator();
}
public boolean supports(Class clazz) {
if (FilterInvocation.class.isAssignableFrom(clazz)) {
return true;
} else {
return false;
}
}
protected void initDao() throws Exception {
this.rdbmsInvocationDefinition =
new RdbmsSecuredUrlDefinition(this.getDataSource());
if(this.webresdbCache == null)
throw new IllegalArgumentException("必须为RdbmsFilterInvocationDefinitionSource配置一EhCache缓存");
}
private List getRdbmsEntryHolderList(){
List list = null;
Element element = this.webresdbCache.get("webres");
if(element != null){
list = (List)element.getValue();
} else {
list = this.rdbmsInvocationDefinition.execute();
Element elem = new Element("webres", list);
this.webresdbCache.put(elem);
}
return list;
}
public void setWebresdbCache(Ehcache webresdbCache) {
this.webresdbCache = webresdbCache;
}
}
spring中配置一下:
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="validateConfigAttributes" value="true" />
<property name="alwaysReauthenticate" value="true"></property>
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource" ref="rdbmsFilterInvocationDefinitionSource" />
</bean>
<bean id="rdbmsFilterInvocationDefinitionSource"
class="websourcedb.RdbmsFilterInvocationDefinitionSource">
<property name="dataSource" ref="dataSource" />
<property name="webresdbCache" ref="webresCacheBackend" />
</bean>
<bean id="webresCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>webresdbCache</value>
</property>
</bean>
针对用户授权的标签库:
ifAnyGranted含义为:一旦登陆用户至少具备被指定的角色集合中的一个,则<authz:authorize/>标签包围的html标签便会被渲染:
<authz:authorize ifAnyGranted="ROLE_USER">
<authz:authentication operation="username" />
已经具有“ROLE_USER”角色了!<br/>
</authz:authorize>
<authz:authorize ifAnyGranted="ROLE_SUPERVISOR">
<authz:authentication operation="username" />
已经具有“ROLE_SUPERVISOR”角色了!<br/>
</authz:authorize>
<authz:authorize ifAnyGranted="ROLE_SUPERVISOR,ROLE_USER">
<authz:authentication operation="username" />
已经具有<authz:authentication operation="authorities" />角色了!<br/>
</authz:authorize>
ifAllGranted含义为:只有登陆用户具备被指定的角色集合中的所有角色时,则<authz:authorize/>标签包围的html标签便会被渲染:
<authz:authorize ifAllGranted="ROLE_SUPERVISOR,ROLE_USER">
<authz:authentication operation="username" />
已经具有“ROLE_SUPERVISOR,ROLE_USER”角色了!<br/>
</authz:authorize>
ifNotGranted含义为:只有登陆用户不具备被指定的角色集合中的任意角色时,则<authz:authorize/>标签包围的html标签便会被渲染:
<authz:authorize ifNotGranted="ROLE_SUPERVISOR">
<authz:authentication operation="username" />
不具有“ROLE_SUPERVISOR”角色!<br/>
</authz:authorize>
<authz:authorize ifNotGranted="ROLE_SUPERVISOR" ifAllGranted="ROLE_USER"
ifAnyGranted="ROLE_USER">
<authz:authentication operation="username" />
不具有“ROLE_SUPERVISOR”角色,但具有“ROLE_USER”角色!<br/>
</authz:authorize>
<authz:authorize ifNotGranted="ROLE_SUPERVISOR" ifAnyGranted="ROLE_SUPERVISOR">
不应该出现的内容!<br/>
</authz:authorize>
针对用户认证的acegi标签库:
在acegi-securoty-1.x.jar存档的META-INF目录能够找到authz.tld,在使用之前应该
<%@ taglib prefix="authz" uri="http://acegisecurity.org/authz" %>
(如果开发者使用的jsp1.2规范的web容器,还需要将zuthz.tld抽取到某个地方,并在web.xml中注册):
<taglib>
<taglib-uri>http://acegisecurity.org/authz</taglib-uri>
<taglib-location>
/WEB-INF/authz.tld
</taglib-location>
</taglib>
下面展示了anthentication标签的使用:
operation(必须给定)和methodPrefix属性,默认时候methodPrefix取get,为了将UserDetails中的getUserName方法的结果展示出来:
<authz:authentication operation="username" methodPrefix=“get”/>
把getAuthorities()方法的结果输出到页面上:
<authz:authentication operation="authorities" />
下面就总体上给出acegi是如何实施用户授权工作的,以及围绕web资源的授权操作展开!
当acegi完成用户的认证操作时,认证结果会存储到Authentication对象中。针对不同的场合,Acegi内置了多种Authentication对象类型。比如:PrincipalAcegiUserToken,UsernamePasswordAuthenticationToken,X509AuthenticationToken等,他们必须实现Authentication接口!
那么acegi是如何实施用户授权的呢?当已经认证的用户视图通过手中的校色集合操控目标资源时,acegi内置的AOP拦截器会采用公平投票机制评估这一举动,实际上公平投票贯穿于目标资源的调用前后,调用前,AOP拦截器确保当前用户确实有要求的角色集合;调用后,AOP拦截器会审核操作结果,比如从返回的领域对象集合中删除当前用户不能够操控的那些领域对象!
在实施用户授权的过程中,拦截器起到的重要的作用,比如:FilterSecurityInterceptor是一个过滤器,针对web资源;MethodSecurityInterceptor是一个AOP拦截器,针对业务方法实施授权;AspectJSecurityInterceptor拦截器,真对AspectJ方法调用实施用户授权!
这些拦截器都会分为三个阶段:事前评估,操控资源,事后审查!
事前评估:
下面的配置信息,其中accessDecisionManager属性正是完成事前评估的!
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" />
<property name="objectDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/securedpage.jsp=ROLE_ADMIN
</value>
</property>
</bean>
上述httpRequestAccessDecisionManager的定义如下,开发者需要为AffirmativeBased配置若干投票器(AccessDecisionVoter)
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value=“false”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
其中AffirmativeBased是AccessDecisionManager的子类,还有ConsensusBased,UnanimousBased。他们的含义和使用是有区别的!
AccessDecisionManager采用民主决策机制判断用户是否有权访问目标程序资源,它包含了多个AccessDecisionVoter。在访问决策时每个AccessDecisionVoter都拥有投票权,AccessDecisionManager统计投票结果,并按照某种决策方式根据这些投票结果决定最终是否向用户开放受限资源的访问。Acegi的投票方式有多种,每个Voter对访问权可以投赞成(ACCESS_GRANTED,取值为1),弃权(ACCESS_ABSTAIN,取值为0)或反对(ACCESS_DENIED,取值为-1)!
Acegi提供了三个具体的类去管理投票结果,他们三个的不同存在于对投票结果的统计上。ConsensusBased类根据非弃权票中赞成票多于反对票来决定是否通过。如果赞成票多于反对票就同意通过!在同意和反对票相同时或都投弃权票时可以根据设置来决定授权与否。配置如下:
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.ConsensusBased">
<property name="allowIfAllAbstainDecisions" value=“true”>
<property name="allowIfEqualGrantedDeniedDecisions" value=“true”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
其中属性allowIfAllAbstainDecisions,默认值是false,意味着当配置的投票器只投了弃权票时,不允许继续执行。相比之下,true意味着允许都投弃权票!
allowIfEqualGrantedDeniedDecisions,默认值为true,意味着在赞成票不为0的情况下,且赞成票和反对票相等,则允许通过!false意味着,在赞成票不为0 的情况下,赞成票和反对票相等,不允许通过!
AffirmativeBased类只要有一个或多个投票赞成就会给予通过,同样我们可以设置在都投弃权票的时候是否通过:
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value=“false”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
UnanimousBased类会在所有的非弃权票都投赞成票时才通过,同样的设置出现在都投弃权票的时候:
<bean id="httpRequestAccessDecisionManager"
class="org.acegisecurity.vote.UnanimousBased">
<property name="allowIfAllAbstainDecisions" value=“true”>
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
Acegi提供了AccessDecisionVoter的两个实现类。对于RoleVoter,我们需要配置它的rolePrefix属性(如ROLE_),当ConfigAttribute以rolePrefix开头时,RoleVoter才投票,否则投弃权票(ACCESS_ABSTAIN)。开始投票时,如果存在一个GrantedAuthority和被访问资源的一个或多个ConfigAttributes完全匹配时,投赞成票,否则投反对票
针对web资源,业务方法,领域对象的用户授权,acegi内置了各种投票器!其中RoleVoter主要针对web资源和业务方法,而AclEntryVoter主要针对领域对象!怎样使用,在后面的章节将!
事后审查:
一旦用户操控到目标资源后,调用结果可能会返回一个领域对象集合,比如,getAll()方法便会返回所有的对象集合!但是并不是任意用户都能够操作到任意领域对象的,我们需要保护他们!
在实施领域对象级的访问控制时(事后审查),acegi的AfterInvocationManager充当了非常主要的角色!
<bean id="contactManagerSecurity" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="businessAccessDecisionManager"/></property>
<property name="afterInvocationManager"><ref local="afterInvocationManager"/></property>
<property name="objectDefinitionSource">
<value>
sample.contact.ContactManager.create=ROLE_USER
sample.contact.ContactManager.getAllRecipients=ROLE_USER
sample.contact.ContactManager.getAll=ROLE_USER,AFTER_ACL_COLLECTION_READ
sample.contact.ContactManager.getById=ROLE_USER,AFTER_ACL_READ
sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN
sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN
</value>
</property>
</bean>
AfterInvocationManager会对业务对象操作的结果进行审查,比如操作结果中是否含有当前用户无权操作的领域对象,如果有,AfterInvocationManager会从操作结果中将这一领域对象删除!具体怎么回事?后面会讲.
保护web资源:FilterSecurityInterceptor
重新认证:
一旦用户登陆到目标系统后,他便依据其本来的身份进行各种操作,但是在一些场合用户的身份可能会被动态的改变!为了将RDBMS中角色的信息的变化同步到已经登陆的用户中,必须启用alwaysReauthenticate属性,默认值是false:
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="alwaysReauthenticate" value="true"></property>
.......
</bean>
RoleVoter投票器:
对于RoleVoter,默认时候只会对那些前缀为ROLE_的角色进行投票处理,吐过角色信息未使用ROLE_开头,我们需要配置它的rolePrefix属性,当ConfigAttribute以rolePrefix开头时,RoleVoter才投票,否则投弃权票(ACCESS_ABSTAIN)。开始投票时,如果存在一个GrantedAuthority和被访问资源的一个或多个ConfigAttributes完全匹配时,投赞成票,否则投反对票
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" >
<property name="rolePrefix" value="Acegi_" />
</bean>
开发者配置的各个角色,必须存在相应的投票器对他们进行处理,否则目标Acegi应用不能正常启动!除非validateConfigAttributes属性被显式的置为false。不过建议设置为true!
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="validateConfigAttributes" value="true" />
.......
</bean>
把web资源授权信息存储在RDBMS中:
分析FilterInvocationDefinfitionSource的运行:
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/index.jsp=ROLE_ANONYMOUS,ROLE_USER
/hello.htm=ROLE_ANONYMOUS,ROLE_USER
/logoff.jsp=ROLE_ANONYMOUS,ROLE_USER
/acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
/**=ROLE_USER
</value>
</property>
</bean>
其中objectDefinitionSource属性属于FilterInvocationDefinfitionSource类型!
PATTERN_TYPE_APACHE_ANT:启用了apache ant路径风格的url匹配模式(PathBasedFilterInvocationDefinfitionMap),默认的是正则表达式(RegExpBasedFilterInvocationDefinfitionMap),他们都是FilterInvocationDefinfitionSource的子类!为了通过DBMS维护web资源授权信息,必须自己提供FilterInvocationDefiniftionSource子类!
实现基于DBMS的FilterInvocationDefiniftionSource
建表:
create table webresdb(
id bigint not null primary key,
url varchar(60) not null,
roles varchar(100) not null)
insert into webresdb values(1,'/securedpage.jsp','ROLE_ADMIN');
编写RdbmsEntryHolder类:
package websourcedb;
import java.io.Serializable;
import org.acegisecurity.ConfigAttributeDefinition;
/**
*
* @author worldheart
*
*/
public class RdbmsEntryHolder implements Serializable{
//保护的URL模式
private String url;
//要求的角色集合
private ConfigAttributeDefinition cad;
public String getUrl() {
return url;
}
public ConfigAttributeDefinition getCad() {
return cad;
}
public void setUrl(String url) {
this.url = url;
}
public void setCad(ConfigAttributeDefinition cad) {
this.cad = cad;
}
}
从EDBMS装载web资源授权信息,并摆在内存中:
package websourcedb;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.SecurityConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.util.StringUtils;
/**
*
* @author worldheart
*
*/
public class RdbmsSecuredUrlDefinition extends MappingSqlQuery{
protected static final Log logger = LogFactory.getLog(RdbmsSecuredUrlDefinition.class);
protected RdbmsSecuredUrlDefinition(DataSource ds) {
super(ds, "SELECT url, roles FROM webresdb ORDER BY id");
logger.debug("进入到RdbmsInvocationDefinition构建器中.........");
compile();
}
protected Object mapRow(ResultSet rs, int rownum)
throws SQLException {
logger.debug("抽取webresdb中的记录.........");
RdbmsEntryHolder rsh = new RdbmsEntryHolder();
//设置URL
rsh.setUrl(rs.getString("url").trim());
ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
String[] tokens =
StringUtils.commaDelimitedListToStringArray(rs.getString("roles").trim());
for(int i = 0; i < tokens.length;++i)
cad.addConfigAttribute(new SecurityConfig(tokens[i]));
//设置角色集合
rsh.setCad(cad);
return rsh;
}
}
最后开发FilterInvocationDefiniftionSource实现类RdbmsFilterInvocationDefiniftionSource是针对web资源的,因此她实现的supports()方法需要评估FilterInvocation对象,并且采用了Spring EhCache集成服务!
package websourcedb;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.intercept.web.FilterInvocation;
import org.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
*
* @author worldheart
*
*/
public class RdbmsFilterInvocationDefinitionSource extends JdbcDaoSupport
implements FilterInvocationDefinitionSource {
protected static final Log logger = LogFactory.getLog(RdbmsFilterInvocationDefinitionSource.class);
private RdbmsSecuredUrlDefinition rdbmsInvocationDefinition;
private PathMatcher pathMatcher = new AntPathMatcher();
private Ehcache webresdbCache;
public ConfigAttributeDefinition getAttributes(Object object)
throws IllegalArgumentException {
if ((object == null) || !this.supports(object.getClass())) {
throw new IllegalArgumentException("抱歉,目标对象不是FilterInvocation类型");
}
// 抽取出待请求的URL
String url = ((FilterInvocation) object).getRequestUrl();
List list = this.getRdbmsEntryHolderList();
if (list == null || list.size() == 0)
return null;
int firstQuestionMarkIndex = url.indexOf("?");
if (firstQuestionMarkIndex != -1) {
url = url.substring(0, firstQuestionMarkIndex);
}
Iterator iter = list.iterator();
while (iter.hasNext()) {
RdbmsEntryHolder entryHolder = (RdbmsEntryHolder) iter.next();
boolean matched = pathMatcher.match(entryHolder.getUrl(), url);
if (logger.isDebugEnabled()) {
logger.debug("匹配到如下URL: '" + url + ";模式为 "
+ entryHolder.getUrl() + ";是否被匹配:" + matched);
}
if (matched) {
return entryHolder.getCad();
}
}
return null;
}
public Iterator getConfigAttributeDefinitions() {
Set set = new HashSet();
Iterator iter = this.getRdbmsEntryHolderList().iterator();
while (iter.hasNext()) {
RdbmsEntryHolder entryHolder = (RdbmsEntryHolder) iter.next();
set.add(entryHolder.getCad());
}
return set.iterator();
}
public boolean supports(Class clazz) {
if (FilterInvocation.class.isAssignableFrom(clazz)) {
return true;
} else {
return false;
}
}
protected void initDao() throws Exception {
this.rdbmsInvocationDefinition =
new RdbmsSecuredUrlDefinition(this.getDataSource());
if(this.webresdbCache == null)
throw new IllegalArgumentException("必须为RdbmsFilterInvocationDefinitionSource配置一EhCache缓存");
}
private List getRdbmsEntryHolderList(){
List list = null;
Element element = this.webresdbCache.get("webres");
if(element != null){
list = (List)element.getValue();
} else {
list = this.rdbmsInvocationDefinition.execute();
Element elem = new Element("webres", list);
this.webresdbCache.put(elem);
}
return list;
}
public void setWebresdbCache(Ehcache webresdbCache) {
this.webresdbCache = webresdbCache;
}
}
spring中配置一下:
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="validateConfigAttributes" value="true" />
<property name="alwaysReauthenticate" value="true"></property>
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="objectDefinitionSource" ref="rdbmsFilterInvocationDefinitionSource" />
</bean>
<bean id="rdbmsFilterInvocationDefinitionSource"
class="websourcedb.RdbmsFilterInvocationDefinitionSource">
<property name="dataSource" ref="dataSource" />
<property name="webresdbCache" ref="webresCacheBackend" />
</bean>
<bean id="webresCacheBackend"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>webresdbCache</value>
</property>
</bean>
针对用户授权的标签库:
ifAnyGranted含义为:一旦登陆用户至少具备被指定的角色集合中的一个,则<authz:authorize/>标签包围的html标签便会被渲染:
<authz:authorize ifAnyGranted="ROLE_USER">
<authz:authentication operation="username" />
已经具有“ROLE_USER”角色了!<br/>
</authz:authorize>
<authz:authorize ifAnyGranted="ROLE_SUPERVISOR">
<authz:authentication operation="username" />
已经具有“ROLE_SUPERVISOR”角色了!<br/>
</authz:authorize>
<authz:authorize ifAnyGranted="ROLE_SUPERVISOR,ROLE_USER">
<authz:authentication operation="username" />
已经具有<authz:authentication operation="authorities" />角色了!<br/>
</authz:authorize>
ifAllGranted含义为:只有登陆用户具备被指定的角色集合中的所有角色时,则<authz:authorize/>标签包围的html标签便会被渲染:
<authz:authorize ifAllGranted="ROLE_SUPERVISOR,ROLE_USER">
<authz:authentication operation="username" />
已经具有“ROLE_SUPERVISOR,ROLE_USER”角色了!<br/>
</authz:authorize>
ifNotGranted含义为:只有登陆用户不具备被指定的角色集合中的任意角色时,则<authz:authorize/>标签包围的html标签便会被渲染:
<authz:authorize ifNotGranted="ROLE_SUPERVISOR">
<authz:authentication operation="username" />
不具有“ROLE_SUPERVISOR”角色!<br/>
</authz:authorize>
<authz:authorize ifNotGranted="ROLE_SUPERVISOR" ifAllGranted="ROLE_USER"
ifAnyGranted="ROLE_USER">
<authz:authentication operation="username" />
不具有“ROLE_SUPERVISOR”角色,但具有“ROLE_USER”角色!<br/>
</authz:authorize>
<authz:authorize ifNotGranted="ROLE_SUPERVISOR" ifAnyGranted="ROLE_SUPERVISOR">
不应该出现的内容!<br/>
</authz:authorize>
针对用户认证的acegi标签库:
在acegi-securoty-1.x.jar存档的META-INF目录能够找到authz.tld,在使用之前应该
<%@ taglib prefix="authz" uri="http://acegisecurity.org/authz" %>
(如果开发者使用的jsp1.2规范的web容器,还需要将zuthz.tld抽取到某个地方,并在web.xml中注册):
<taglib>
<taglib-uri>http://acegisecurity.org/authz</taglib-uri>
<taglib-location>
/WEB-INF/authz.tld
</taglib-location>
</taglib>
下面展示了anthentication标签的使用:
operation(必须给定)和methodPrefix属性,默认时候methodPrefix取get,为了将UserDetails中的getUserName方法的结果展示出来:
<authz:authentication operation="username" methodPrefix=“get”/>
把getAuthorities()方法的结果输出到页面上:
<authz:authentication operation="authorities" />
相关推荐
AceGI安全策略与CAS(Central Authentication Service)整合是企业级应用中常见的安全解决方案,它能够为Web应用程序提供统一的身份验证和授权服务。本文档旨在详细阐述这一整合过程,包括配置步骤、所需资源以及...
Acegi(后更名为Spring Security)是Spring框架中的一个子项目,专注于提供强大的安全功能,包括认证、授权以及保护web应用程序等。Acegi的设计理念旨在解决传统Java认证和授权服务(JAAS)中存在的不足,如政策文件...
而Acegi Security是Spring社区早期的一个安全模块,它提供了全面的身份验证、授权和会话管理功能,为基于Spring的应用程序提供了强大的安全性支持。本实战教程将深入探讨如何将Acegi Security集成到Spring框架中,...
Acegi Security(现为Spring Security)是一个全面且高度可配置的安全框架,它提供了认证、授权和会话管理等功能,旨在简化企业级Web应用的安全实现。该框架与Spring无缝集成,允许开发者通过配置和编程接口轻松地...
2. **配置Spring**:在Spring的配置文件中,定义Acegi的安全上下文,包括认证和授权策略。 3. **定义安全元数据**:创建XML配置或使用注解来指定哪些URL、方法需要进行安全控制。 4. **实现认证和授权逻辑**:根据...
配置包括定义访问控制规则、配置认证和授权策略,以及设置安全过滤器链。 例如,以下是一个简单的XML配置示例,定义了一个URL访问规则: ```xml <bean id="filterSecurityInterceptor" class="org.acegisecurity....
总的来说,Spring Acegi Security提供了一套全面的Web应用安全框架,它允许开发者通过注解或XML配置来实现灵活的认证和授权策略。通过将安全逻辑从业务代码中分离,不仅可以简化代码,也便于管理和维护。尽管Acegi ...
Acegi是一个强大的、可配置的安全框架,它提供了认证、授权、会话管理等功能,能够与Spring无缝集成,为Web应用程序提供全面的安全保护。Acegi的核心概念包括安全上下文、访问决策管理器和权限评估器。 2. **认证...
- Acegi支持多种授权策略,如基于角色的访问控制(RBAC)、表达式语言等。 - 一旦决定允许或拒绝访问,Acegi将相应地修改请求的流程。 在实际应用中,我们可能需要配置Acegi以适应我们的需求。这包括设置认证提供...
配置中包含了定义安全拦截器链、指定访问决策策略、设置认证和授权规则等内容。 5. **会话管理**:Acegi还提供了会话管理功能,如会话固定攻击防护(Session Fixation Protection)、会话超时控制等,增强了应用的...
通过在目标对象上创建代理,Acegi可以在方法调用前后插入安全检查逻辑,确保只有经过授权的用户才能调用受保护的方法。 - **Spring IoC**:Acegi充分利用了Spring的依赖注入功能,允许开发者通过配置文件来定义和...
同时,Acegi提供了细粒度的授权机制,可以基于角色、URL、方法级别进行访问控制,确保只有具备相应权限的用户才能访问特定资源。 2. **过滤器链**: Acegi通过Servlet过滤器实现其安全功能。当用户请求到达服务器...
"acegisample"可能是一个解压后的war文件内容,包含了Web应用的所有源代码和资源文件,方便开发者查看和学习Acegi和LDAP的集成方式。 通过这个实例,开发者可以学习如何配置Acegi的安全策略,如何在Spring应用中...
在 `acegi-config.xml` 文件中,你可以定义认证和授权策略,比如用户存储(UserDetailsService)、认证管理器(AuthenticationManager)和权限访问决策器(AccessDecisionManager)。 Acegi 还提供了许多预定义的...
总结,Acegi Security提供了全面的Web安全解决方案,通过详细的配置和代码实现,我们可以确保应用程序的安全性。`spring_security.sql`和`acegiTest`文件是这个过程的重要组成部分,分别负责数据存储和功能验证。...
为了防止跨站请求伪造(CSRF)攻击,Acegi提供了一套完整的CSRF保护机制,包括生成和验证CSRF令牌。 **过滤器链**: Acegi通过Servlet过滤器实现其安全功能。Spring SecurityFilterChain负责拦截HTTP请求,执行身份...
Acegi安全框架为Spring应用提供了全面的安全保护方案,通过灵活配置上述核心组件,开发者可以轻松实现不同层次的安全控制,确保应用程序的安全性和稳定性。无论是Web层的安全防护还是业务逻辑层的方法调用控制,...
3. **过滤器安全链(Filter Security Interceptor)**:Acegi的核心组件之一,它是Servlet过滤器,负责拦截请求并根据配置的策略进行身份验证和授权。 4. **安全性配置(Security Configurations)**:在Spring应用...
通过学习这个压缩包中的内容,开发者可以了解到如何在实际项目中使用Acegi(或者Spring Security)来保护数据库,避免未授权的访问,同时也能掌握如何编写和配置安全策略,以确保应用的安全性和稳定性。这不仅对提升...