權限系統是稍成規模的系統中一個必不可少的部分,操作的可否執行,流程的具體走向,業務的具體步驟都會涉及權限的處理。
具體來說有兩種權限的控制方式:一種是
等級權限控制方式,一種是
角色權限控制方式。前者可用于范圍控制,適用于用戶權力大小不同的場合;后者可用于單點控制,適用于用戶權力多寡有異的場合。現實世界中,軍隊中官銜類似于等級權限控制,現代企業中各司其職的權力分配類似于角色控制。范圍控制前面已經提到過了,今天來談談角色權限控制。
角色權限控制是把單項權限一項項的賦予用戶,如同現實世界中把具體職位一個個的賦予某個員工一樣。在他執行操作前,先看他是否擁有執行此操作的權限,如果有則執行,否則不執行。
在這里我們還是采用上一講的業務,實現IDocService的實現類DocService,但要把等級權限控制方式修改成角色權限控制方式,原來的處置是用戶權限高于某個值就能執行操作,現在如果一個用戶有“添加”角色,他就可以添加文檔;如果他缺乏“修改”角色,他就不能修改文檔。
實現用戶角色權限控制并不復雜,下面請看具體思路:
1.創建兩個領域對象類:用戶類User和角色類Role,他們之間是一對多的關系,他們對應的表用主外鍵關聯起來。使用Hibernate很容易實現這一關系。
2.使用AOP,實現IDocService接口的實現類DocService的代理,使用UserRoleController作為前置通知,角色權限控制放入其中。
3. UserRoleController中,查看用戶是否有執行某項操作的權限,沒有則拋出異常NoRoleException,否則執行的DocService中的函數。
領域對象類的基類BaseDomainObj,User,Role,Doc等都是它的子類。
package com.heyang.domain;


/** *//**
* 領域對象基類
* @author 何楊
* @version 1.00
* @since 2009-1-5 上午10:26:32
*
*/

public abstract class BaseDomainObj
{
// ID
protected long id;
// 名稱
protected String name;

public String toString()
{
return name;
}


public long getId()
{
return id;
}


public void setId(long id)
{
this.id = id;
}


public String getName()
{
return name;
}


public void setName(String name)
{
this.name = name;
}
}
User類:
package com.heyang.domain;

import java.util.LinkedHashSet;
import java.util.Set;


/** *//**
* 領域對象用戶類
* @author 何楊
* @version 1.00
* @since 2009-1-5 上午10:23:25
*
*/

public class User extends BaseDomainObj
{
// 用戶所擁有的權限
private Set<Role> roles=new LinkedHashSet<Role>();

public User()
{
}

public User(String name)
{
this.name=name;
}

/** *//**
* 判斷用戶是否擁有角色。若參數角色名在用戶角色集合中能找到則認為用戶有此角色,即名相同則有,否則無
* @param roleName :角色名
* @return
*/

public boolean hasRole(String roleName)
{

for(Role role:roles)
{

if(role.getName().equals(roleName))
{
return true;
}
}
return false;
}


public Set<Role> getRoles()
{
return roles;
}


public void setRoles(Set<Role> roles)
{
this.roles = roles;
}
}
User類的Hibernate映射文件,Set部分是關鍵:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.auction">
<class name="com.heyang.domain.User"
table="AOPRoleSample_User" lazy="false">
<id name="id" column="ID" >
<generator class="native"/>
</id>
<property name="name" column="name" />
<set name="roles" cascade="all" lazy="false">
<key column="userid"/>
<one-to-many class="com.heyang.domain.Role"/>
</set>
</class>
</hibernate-mapping>

Role類:
package com.heyang.domain;


/** *//**
* 領域對象角色類
* @author 何楊
* @version 1.00
* @since 2009-1-5 上午10:32:16
*
*/

public class Role extends BaseDomainObj
{

public Role()
{
}

public Role(String name)
{
this.name=name;
}
}

Role類的映射文件:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.auction">
<class name="com.heyang.domain.Role"
table="AOPRoleSample_Role" lazy="false">
<id name="id" column="ID" >
<generator class="native"/>
</id>
<property name="name" column="name" />
</class>
</hibernate-mapping>

DocService類,有了AOP的幫忙,其中不需要任何權限控制的代碼,它甚至不知道權限控制子系統的存在:

public class DocService extends BaseService implements IDocService
{

/** *//**
* 按ID取得文檔
* @param id
* @return
*/

public Doc getDoc(long id)
{
return (Doc)dao.get(Doc.class,id);
}

public void add(Doc doc, User user)
{
System.out.println("將" + doc + "交由dao處理(存入數據庫)");
dao.create(doc);
}


public void delete(Doc doc, User user)
{
System.out.println("將" + doc + "交由dao處理(從數據庫刪除)");
dao.delete(doc);
}


public void update(Doc doc, User user)
{
System.out.println("將" + doc + "交由dao處理(修改數據庫中對應的記錄)");
dao.update(doc);
}
}

UserRoleController類,它作為DocService的前置處理器,在真正的數據庫操作開始前進行權限處理:
package com.heyang.service;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

import com.heyang.domain.User;
import com.heyang.exception.NoRoleException;


/** *//**
* 實現角色子系統---用戶角色控制
* @author: 何楊(heyang78@gmail.com)
* @date: 2009-1-2-下午04:19:13
*/

public class UserRoleController implements MethodBeforeAdvice
{
private String addDocRoleName;
private String deleteDocRoleName;
private String updateDocRoleName;

/** *//**
* 在IDocService的實際方法開始前進行前置處理--用戶角色檢查
*/

public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable
{
// 取得方法名
String mothodName=arg0.getName();
// 取得用戶
User user=null;

if(arg1.length>1)
{
user=(User)arg1[1];
}
// 根據方法名判斷用戶是否擁有所需要的角色,否則拋出異常

if("add".equals(mothodName))
{

if(user.hasRole(addDocRoleName)==false)
{
throw new NoRoleException("用戶"+user+"必須擁有‘添加’角色才能執行添加文檔操作");
}
}

else if("delete".equals(mothodName))
{

if(user.hasRole(deleteDocRoleName)==false)
{
throw new NoRoleException("用戶"+user+"必須擁有‘刪除’角色才能執行刪除文檔操作");
}
}

else if("update".equals(mothodName))
{

if(user.hasRole(updateDocRoleName)==false)
{
throw new NoRoleException("用戶"+user+"必須擁有‘修改’角色才能執行修改文檔操作");
}
}
}



public String getAddDocRoleName()
{
return addDocRoleName;
}



public void setAddDocRoleName(String addDocRoleName)
{
this.addDocRoleName = addDocRoleName;
}



public String getDeleteDocRoleName()
{
return deleteDocRoleName;
}



public void setDeleteDocRoleName(String deleteDocRoleName)
{
this.deleteDocRoleName = deleteDocRoleName;
}



public String getUpdateDocRoleName()
{
return updateDocRoleName;
}



public void setUpdateDocRoleName(String updateDocRoleName)
{
this.updateDocRoleName = updateDocRoleName;
}
}
全體配置文件:
<?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="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"
value="org.gjt.mm.mysql.Driver">
</property>
<property name="url" value="jdbc:mysql://127.0.0.1/test">
</property>
<property name="username" value="root"></property>
<property name="password" value="hy"></property>
</bean>
<!-- Hibernate Session Factory,使用了上面配置的數據源 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>com/heyang/domain/User.hbm.xml</value>
<value>com/heyang/domain/Role.hbm.xml</value>
<value>com/heyang/domain/Doc.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.hbm2ddl.auto=Acreate
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.format_sql=true
</value>
</property>
</bean>
<!-- Hibernate Template,使用了上面配置的sessionFactory -->
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<!-- 使用了上面配置的hibernateTemplate的BaseDao -->
<bean id="dao" class="com.heyang.dao.BaseDao">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate"/>
</property>
</bean>
<!-- 使用了上面配置的dao的UserService -->
<bean id="userService" class="com.heyang.service.UserService">
<property name="domainClass">
<value>User</value>
</property>
<property name="dao">
<ref bean="dao"/>
</property>
</bean>
<!-- 用于文件處理的IDocService實現類DocService -->
<bean id="docService" class="com.heyang.service.DocService">
<property name="dao">
<ref bean="dao"/>
</property>
</bean>
<!-- 在執行docService的實際方法前進行用戶角色檢查 -->
<bean id="userRoleController" class="com.heyang.service.UserRoleController">
<property name="addDocRoleName" value="添加" />
<property name="deleteDocRoleName" value="刪除" />
<property name="updateDocRoleName" value="修改" />
</bean>
<!-- docService的代理對象 -->
<bean id="docServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.heyang.service.IDocService</value>
</property>
<property name="interceptorNames">
<list>
<value>userRoleController</value>
</list>
</property>
<property name="target">
<ref bean="docService"/>
</property>
</bean>
</beans>

模擬處理:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService)ctx.getBean("userService");
IDocService docService=(IDocService)ctx.getBean("docServiceProxy");
User user=userService.getUser(1);
Doc doc1;
// 模擬添加操作
doc1=new Doc("論次貸危機對美國的影響");

try
{
docService.add(doc1, user);
}

catch(NoRoleException ex)
{
ex.printStackTrace();
}
// 模擬修改操作
doc1=docService.getDoc(1);
doc1.setName("論次貸危機對中國的影響");

try
{
docService.update(doc1, user);
}

catch(NoRoleException ex)
{
ex.printStackTrace();
}
// 模擬刪除操作
doc1=docService.getDoc(1);
doc1.setName("論次貸危機對世界的影響");

try
{
docService.delete(doc1, user);
}

catch(NoRoleException ex)
{
ex.printStackTrace();
}
小結:
權限子系統是企業應用中不可或缺的環節,它具體的權限控制方式有兩種:一種是等級權限控制方式,一種是角色權限控制方式,其他復雜的權限系統都可以由它們組合而來。
由于業務控制的關系,權限子系統和其他業務子系統的最容易耦合在一起,久而久之會對程序的可讀性,可維護性帶來消極影響,而AOP恰好能幫助我們有效降低權限子系統和其他業務子系統的耦合,實現他們之間的離散化。因此,AOP值得我們認真掌握,尤其是其背后面向方面編程的精神。
代碼下載:
http://www.tkk7.com/Files/heyang/AOPRoleSample20090106060255.rar
需要的庫請自行補充(基本ProjectTodolist的lib全有)。