`

Acegi+hibernate 动态实现基于角色的权限管理

阅读更多
最近在做项目遇到了权限管理,用户要求可以自己建立不同的角色对系统的资源进行控制, 不同的用户有不同的角色,又恰恰框架中用到了struts+spring+hibernate,要求在web层调用 业务逻辑层 时不考虑权限,web层可以控制用户的显示界面,逻辑层处理用户权限问题。
想来想去好像只有spring 的aop 可以做到,在调用到 接口 中的方法时,首先检查用户的权限,如果检查通过则继续执行,否则抛出异常。但是新的问题又出现了,如何在逻辑层上来得到当前用户的id,以致用户的 角色,总不能每次都要从web中传来一个 httprequest,或者 session 这类的吧。在网上看了很多资料,发现了acegi,恰好解决了以上的难题,具体的实现原理这里就不多说了,网上有很多相关资料。
说正题,首先来看看acegi 的官方 example ,我下载的是acegi-security-1.0.0-RC1,解压缩后可以看到acegi-security-sample-contacts-filter.war,打开配置文件有这样几句
java代码:


<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>


可以看到它是通过读配置文件来判断执行某个方法所需要的角色的,再看这几句
java代码:


<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
<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
/switchuser.jsp=ROLE_SUPERVISOR
/j_acegi_switch_user=ROLE_SUPERVISOR
/acegilogin.jsp*=ROLE_ANONYMOUS,ROLE_USER
/**=ROLE_USER
</value>
</property>
</bean>


同样是将页面的访问权限写死在配置文件中,再来看看它的tag是如何处理的
java代码:


<auth:authorize ifAnyGranted="ROLE_DELETE">
<a href="">删除</a>
</auth:authorize>


可见它是要求我们对链接或者其他资源的保护时提供 用户角色,可是既然角色是用户自己添加的我们又如何来写死在这里呢?
还有就是它对用户验证默认使用的是jdbc,即 JdbcDaoImpl

java代码:


<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref local="dataSource"/></property>
</bean>



而我们希望基于Hibernate的Dao来实现。
可见仅仅使用现有的acegi 是 无法满足我们项目开发的需求的。
解决方法:

1: 开发基于数据库的保护资源。

看过acegi的源代码就会知道,对保护资源的定义是通过实现ObjectDefinitionSource这个接口来实现的,而且acegi为我们提供了默认实现的抽象类
java代码:


public abstract class AbstractMethodDefinitionSource
implements MethodDefinitionSource {
//~ Static fields/initializers =============================================

private static final Log logger = LogFactory.getLog(AbstractMethodDefinitionSource.class);

//~ Methods ================================================================

public ConfigAttributeDefinition getAttributes(Object object)
throws IllegalArgumentException {
Assert.notNull(object, "Object cannot be null");

if (object instanceof MethodInvocation) {
return this.lookupAttributes(((MethodInvocation) object).getMethod());
}

if (object instanceof JoinPoint) {
JoinPoint jp = (JoinPoint) object;
Class targetClazz = jp.getTarget().getClass();
String targetMethodName = jp.getStaticPart().getSignature().getName();
Class[] types = ((CodeSignature) jp.getStaticPart().getSignature())
.getParameterTypes();

if (logger.isDebugEnabled()) {
logger.debug("Target Class: " + targetClazz);
logger.debug("Target Method Name: " + targetMethodName);

for (int i = 0; i < types.length; i++) {
if (logger.isDebugEnabled()) {
logger.debug("Target Method Arg #" + i + ": "
+ types[i]);
}
}
}

try {
return this.lookupAttributes(targetClazz.getMethod(targetMethodName, types));
} catch (NoSuchMethodException nsme) {
throw new IllegalArgumentException("Could not obtain target method from JoinPoint: " + jp);
}
}

throw new IllegalArgumentException("Object must be a MethodInvocation or JoinPoint");
}

public boolean supports(Class clazz) {
return (MethodInvocation.class.isAssignableFrom(clazz)
|| JoinPoint.class.isAssignableFrom(clazz));
}


protected abstract ConfigAttributeDefinition lookupAttributes(Method method);
}


我们要做的就是实现它的
protected abstract ConfigAttributeDefinition lookupAttributes(Method method);方法,
以下是我的实现方法,大致思路是这样,通过由抽象类传来的Method 对象得到
调用这个方法的 包名,类名,方法名 也就是secureObjectName, 查询数据库并将结果映射为Function 也就是secureObject ,由于Function 与 Role 的多对多关系 可以得到 Function所对应的 Roles ,在将role 包装成GrantedAuthority (也就是acegi中的角色)。其中由于频繁的对数据库的查询 所以使用Ehcache 来作为缓存。
java代码:


package sample.auth;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.acegisecurity.ConfigAttributeDefinition;
import org.acegisecurity.ConfigAttributeEditor;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.intercept.method.AbstractMethodDefinitionSource;
import org.springframework.util.Assert;

import sample.auth.cache.AuthorityBasedFunctionCache;
import sample.auth.cache.info.FunctionByNameCache;
import sample.dao.IBaseDao;
import sample.mappings.function.Function;
import sample.mappings.role.Role;

public class DatabaseDrivenMethodDefinitionSourcew extends
AbstractMethodDefinitionSource {

// baseDao 提供通过HIbenate对数据库操作的实现
private IBaseDao baseDao;
// AuthorityBasedFunctionCache 通过Function 查 Role 时缓存
private AuthorityBasedFunctionCache cache;
// FunctionByNameCache 由反射到的方法名查找 数据库对应的Function 时的缓存
private FunctionByNameCache functionCache;

public FunctionByNameCache getFunctionCache() {
return functionCache;
}

public void setFunctionCache(FunctionByNameCache functionCache) {
this.functionCache = functionCache;
}

protected ConfigAttributeDefinition lookupAttributes(Method mi) {

Assert.notNull(mi,"lookupAttrubutes in the DatabaseDrivenMethodDefinitionSourcew is null");
String secureObjectName=mi.getDeclaringClass().getName() +"."+ mi.getName();
//Function 为数据库中保护资源的映射
Function secureObject=functionCache.getFunctionByCache(secureObjectName);

if(secureObject==null)//if secure object not exist in database
{
secureObject=(Function)baseDao.loadByKey(Function.class, "protectfunction", secureObjectName);
functionCache.putFunctionInCache(secureObject);
}

if(secureObject==null)
Assert.notNull(secureObject,"secureObject(Function) not found in db");
//retrieving roles associated with this secure object

Collection roles = null;
GrantedAuthority[] grantedAuthoritys = cache.getAuthorityFromCache(secureObject.getName());
// 如果是第一次 cache 为空
if(grantedAuthoritys == null){

Set rolesSet = secureObject.getRoles();
Iterator it = rolesSet.iterator();
List list = new ArrayList();
while(it.hasNext()){

Role role = (Role)it.next();
GrantedAuthority g = new GrantedAuthorityImpl(role.getName());
list.add(g);
}
grantedAuthoritys = (GrantedAuthority[])list.toArray(new GrantedAuthority[0]);
cache.putAuthorityInCache(secureObject.getName(),grantedAuthoritys);
roles = Arrays.asList(grantedAuthoritys);
}else{

roles = Arrays.asList(grantedAuthoritys);
}

if(!roles.isEmpty()){
ConfigAttributeEditor configAttrEditor=new ConfigAttributeEditor();
StringBuffer rolesStr=new StringBuffer();
for(Iterator it = roles.iterator();it.hasNext();){
GrantedAuthority role=(GrantedAuthority)it.next();
rolesStr.append(role.getAuthority()).append(",");
}

configAttrEditor.setAsText( rolesStr.toString().substring(0,rolesStr.length()-1) );
ConfigAttributeDefinition configAttrDef=(ConfigAttributeDefinition)configAttrEditor.getValue();
return configAttrDef;
}

Assert.notEmpty(roles,"collection of roles is null or empty");
return null;


}

public Iterator getConfigAttributeDefinitions() {

return null;
}


public IBaseDao getBaseDao() {
return baseDao;
}


public void setBaseDao(IBaseDao baseDao) {
this.baseDao = baseDao;
}

public AuthorityBasedFunctionCache getCache() {
return cache;
}

public void setCache(AuthorityBasedFunctionCache cache) {
this.cache = cache;
}

}




2:定义 基于方法的 自定义标志

通过以上的分析 , 要想使用acegi 做页面的显示控制仅仅靠角色(Role)是不行的,因为用户可能随时定义出新的角色,所以只能 基于方法(Function)的控制。可是acegi 只是提供了基于 角色的 接口GrantedAuthority ,怎么办? Wink ,如法炮制。 首先定义出我们自己的GrantedFunction,实现也雷同 GrantedAuthorityImpl

java代码:


package sample.auth;

import java.io.Serializable;
public class GrantedFunctionImpl implements GrantedFunction , Serializable{

private String function;

//~ Constructors ===========================================================

public GrantedFunctionImpl(String function) {
super();
this.function = function;
}

protected GrantedFunctionImpl() {
throw new IllegalArgumentException("Cannot use default constructor");
}

//~ Methods ================================================================

public String getFunction() {
return this.function;
}

public boolean equals(Object obj) {
if (obj instanceof String) {
return obj.equals(this.function);
}

if (obj instanceof GrantedFunction) {
GrantedFunction attr = (GrantedFunction) obj;

return this.function.equals(attr.getFunction());
}

return false;
}

public int hashCode() {
return this.function.hashCode();
}

public String toString() {
return this.function;
}

}




以下是我的标志实现,大致思路是 根据 页面 的传来的 方法名(即 FunctionName)查询出对应的Functions,并且包装成grantedFunctions ,然后根据用户的角色查询出用户对应的Functions ,再取这两个集合的交集,最后再根据这个集合是否为空判断是否显示标志体的内容。

java代码:


package sample.auth;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;

import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.util.ExpressionEvaluationUtils;

import sample.web.action.AppContext;
/**
*
* @author limq
*
*/

public class AuthorizeActionTag extends TagSupport{

private String ifAllGranted = "";
private String ifAnyGranted = "";
private String ifNotGranted = "";

public void setIfAllGranted(String ifAllGranted) throws JspException {
this.ifAllGranted = ifAllGranted;
}

public String getIfAllGranted() {
return ifAllGranted;
}

public void setIfAnyGranted(String ifAnyGranted) throws JspException {
this.ifAnyGranted = ifAnyGranted;
}

public String getIfAnyGranted() {
return ifAnyGranted;
}

public void setIfNotGranted(String ifNotGranted) throws JspException {
this.ifNotGranted = ifNotGranted;
}

public String getIfNotGranted() {
return ifNotGranted;
}

public int doStartTag() throws JspException {
if (((null == ifAllGranted) || "".equals(ifAllGranted))
&& ((null == ifAnyGranted) || "".equals(ifAnyGranted))
&& ((null == ifNotGranted) || "".equals(ifNotGranted))) {
return Tag.SKIP_BODY;
}

final Collection granted = getPrincipalFunctionByAuthorities();

final String evaledIfNotGranted = ExpressionEvaluationUtils
.evaluateString("ifNotGranted", ifNotGranted, pageContext);

if ((null != evaledIfNotGranted) && !"".equals(evaledIfNotGranted)) {
Set grantedCopy = retainAll(granted,
parseSecurityString(evaledIfNotGranted));

if (!grantedCopy.isEmpty()) {
return Tag.SKIP_BODY;
}
}

final String evaledIfAllGranted = ExpressionEvaluationUtils
.evaluateString("ifAllGranted", ifAllGranted, pageContext);

if ((null != evaledIfAllGranted) && !"".equals(evaledIfAllGranted)) {
if (!granted.containsAll(parseSecurityString(evaledIfAllGranted))) {
return Tag.SKIP_BODY;
}
}

final String evaledIfAnyGranted = ExpressionEvaluationUtils
.evaluateString("ifAnyGranted", ifAnyGranted, pageContext);

if ((null != evaledIfAnyGranted) && !"".equals(evaledIfAnyGranted)) {
Set grantedCopy = retainAll(granted,
parseSecurityString(evaledIfAnyGranted));

if (grantedCopy.isEmpty()) {
return Tag.SKIP_BODY;
}
}

return Tag.EVAL_BODY_INCLUDE;
}
/**
* 得到用户的Authentication,并且从Authentication中获得 Authorities,进而得到 授予用户的 Function
* @return
*/

private Collection getPrincipalFunctionByAuthorities() {


Authentication currentUser = SecurityContextHolder.getContext()
.getAuthentication();
if (null == currentUser) {
return Collections.EMPTY_LIST;
}

if ((null == currentUser.getAuthorities())
|| (currentUser.getAuthorities().length < 1)) {
return Collections.EMPTY_LIST;
}
// currentUser.getAuthorities() 返回的是 GrantedAuthority[]
List granted = Arrays.asList(currentUser.getAuthorities());
AuthDao authDao =(AuthDao) AppContext.getInstance().getAppContext().getBean("authDao");
Collection grantedFunctions = authDao.getFunctionsByRoles(granted);
return grantedFunctions;
}

/**
* 得到用户功能(Function)的集合,并且验证是否合法
* @param c Collection 类型
* @return Set类型
*/

private Set SecurityObjectToFunctions(Collection c) {
Set target = new HashSet();

for (Iterator iterator = c.iterator(); iterator.hasNext();) {
GrantedFunction function = (GrantedFunction) iterator.next();

if (null == function.getFunction()) {
throw new IllegalArgumentException(
"Cannot process GrantedFunction objects which return null from getFunction() - attempting to process "
+ function.toString());
}

target.add(function.getFunction());
}

return target;
}

/**
* 处理页面标志属性 ,用' ,'区分
*/

private Set parseSecurityString(String functionsString) {
final Set requiredFunctions = new HashSet();
final String[] functions = StringUtils
.commaDelimitedListToStringArray(functionsString);

for (int i = 0; i < functions.length; i++) {
String authority = functions[i];

// Remove the role's whitespace characters without depending on JDK 1.4+
// Includes space, tab, new line, carriage return and form feed.
String function = StringUtils.replace(authority, " ", "");
function = StringUtils.replace(function, "\t", "");
function = StringUtils.replace(function, "\r", "");
function = StringUtils.replace(function, "\n", "");
function = StringUtils.replace(function, "\f", "");

requiredFunctions.add(new GrantedFunctionImpl(function));
}

return requiredFunctions;
}
/**
* 获得用户所拥有的Function 和 要求的 Function 的交集
*
分享到:
评论

相关推荐

    acegi+ssh动态实现基于角色的权限管理

    ### acegi+SSH动态实现基于角色的权限管理 在企业级应用开发中,权限管理和用户认证是必不可少的安全机制。本文将详细介绍如何利用Acegi安全框架(现称为Spring Security)结合SSH(Struts + Spring + Hibernate)...

    动态实现基于角色的权限管理(Acegi+hibernate )

    本主题将深入探讨如何使用Acegi安全框架和Hibernate ORM工具动态实现基于角色的权限管理。Acegi(现已被Spring Security替代)是一个用于Java平台的安全框架,而Hibernate则是一个流行的关系型数据库持久化解决方案...

    Acegi + Spring + Hibernate + Struts 2搭建基于角色的权限控制系统

    Acegi(现已被Spring Security替代)是一个强大的安全框架,可以与Spring、Hibernate和Struts 2等其他技术结合,实现基于角色的权限控制(Role-Based Access Control, RBAC)。这篇文档将探讨如何使用Acegi,以及SSH...

    Acegi + Spring + Hibernate + Struts2搭建

    本篇文章将探讨如何使用Acegi、Spring、Hibernate和Struts2这四大组件共同构建一个基于角色的权限控制系统(Role-Based Access Control, RBAC),以确保系统的安全性。 首先,我们需要理解认证和授权这两个基本的...

    easyui+spring+hibernate示例,带权限管理

    在权限管理方面,Spring Security(原名Acegi)模块是关键,它提供了一套完整的认证和授权机制。通过Spring Security,我们可以定义用户角色、权限规则,实现登录验证、访问控制等功能,确保只有合法用户能访问特定...

    Spring+Hibernate+Acegi 的初次体验

    然后,Acegi会基于角色和权限设置控制用户可以访问的页面,例如,教师会被重定向到`admin.jsp`,学生会被重定向到`stu.jsp`,管理员则会去`teacher.jsp`。 总结一下,这个例子展示了如何结合Spring、Hibernate和...

    struts2+spring+hibernate实验设备管理系统

    在本系统中,可能已经通过Spring Security实现了用户登录验证、角色分配和权限控制,确保只有授权用户才能访问特定的资源。 至于数据库连接,通常会有一个配置文件如`jdbc.properties`,用于存储数据库连接信息,如...

    struts2+spring+hibernate权限系统

    同时,Spring Security(原名Acegi)是其安全模块,提供了用户认证和授权的功能,可以实现角色级别的权限控制,支持基于URL、方法或特定业务规则的访问控制。 3. **Hibernate框架**:Hibernate是Java领域的一个ORM...

    权限管理系统struts2+spring+hibernate+mysql

    同时,Spring Security(原Acegi)是Spring生态中的安全模块,提供了全面的身份验证和授权服务,可以方便地集成到系统中,实现用户登录、角色管理、访问控制列表(ACL)等功能。 3. **Hibernate**:Hibernate是一个...

    struts1.2+hibernate3框架的权限管理系统

    - **角色和权限**:权限管理通常基于角色,不同角色拥有不同的操作权限。系统需要定义角色并分配权限,然后根据用户的角色来确定其可以访问的资源。 - **过滤器(Filter)**:在Web应用中,使用Filter可以在请求...

    权限控制(struts2+hibernate+spring+struts-menu+valuelist)

    在权限管理中,Hibernate可以用来实现用户角色与权限的关联,如通过查询获取用户所拥有的所有权限,进而决定其在系统中的操作范围。 Spring框架则扮演了整合其他组件的角色,提供了依赖注入(DI)和面向切面编程...

    SSH+Extjs4 0实现的权限管理系统 基于角色的权限设计

    《SSH+Extjs4.0实现的权限管理系统:基于角色的权限设计》 在IT行业中,权限管理系统是保障系统安全性和数据隐私的关键组件。本文将深入探讨如何利用SSH(Spring、Struts2、Hibernate)框架结合Extjs4.0前端库,...

    权限管理系统(Struts2+Spring4+Hibernate5框架整合)

    此外,Spring Security(原Acegi Security)是Spring4的一个扩展,专门用于处理安全问题,包括用户认证和授权,是构建权限管理系统的重要组件。 **Hibernate5** 是一个持久层框架,它简化了Java对象与数据库之间的...

    struts2.0+spring2.0+hibernate3.1+web应用

    在上述组合中,ACEGI(现在已经被Spring Security替代)被用来实现权限管理。它是一个基于Spring AOP的安全框架,能够无缝集成到Spring应用中,实现细粒度的访问控制。通过配置,可以定义哪些用户或角色有权访问特定...

    spring4+hibernate4+security4整合

    接下来,Spring Security(之前称为Acegi Security)是Spring生态中的安全模块,主要用于实现身份验证(Authentication)和授权(Authorization)。Security 4加强了对Spring 4的支持,提供了基于表达式语言的访问...

    Struts2+Hibernate3+Spring3(jar包)

    Spring Security3(之前称为Acegi Security)是Spring生态的一部分,专门用于应用安全,提供身份验证、授权等功能,能够实现用户的登录、权限控制、会话管理等。 在这个组合中,Spring3还扮演了整合者的角色,它...

    基于s2sh_springn security的权限管理系统

    3. 角色与权限设计:系统采用RBAC(Role-Based Access Control)模型,即基于角色的访问控制。角色是一组权限的集合,用户通过被赋予不同的角色来获得相应的权限。这样,权限的管理更加灵活,减少了直接对用户进行...

    spring security3,spring3.0,hibernate3.5,struts2

    它与Spring框架紧密结合,支持基于角色的访问控制(RBAC)和自定义安全策略。 Spring 3.0是Spring框架的一个重要版本,它提供了许多改进和新特性,如对JSR-303/JSR-349(Bean Validation)的支持,改进的AOP(面向...

    acegi实现用户权限

    标题中的“acegi实现用户权限”指的是在Java Web开发中使用Acegi Security框架进行权限管理的技术。Acegi Security是Spring框架的一个扩展,它提供了一套完整的安全解决方案,用于控制应用程序的访问权限。在这个...

    xdoclet_appfuse打包

    6. "Acegi+hibernate 动态实现基于角色的权限管理.doc":Acegi(现为Spring Security的一部分)是一个安全框架,配合Hibernate进行动态角色权限管理,这个文档可能会阐述如何设置和使用这种系统。 7. "appfuse开发...

Global site tag (gtag.js) - Google Analytics