权限系统是稍成规模的系统中一个必不可少的部分,操作的可否执行,流程的具体走向,业务的具体步骤都会涉及权限的处理。
具体来说有两种权限的控制方式:一种是
等级权限控制方式,一种是
角色权限控制方式。前者可用于范围控制,适用于用户权力大小不同的场合;后者可用于单点控制,适用于用户权力多寡有异的场合。现实世界中,军队中官衔类似于等级权限控制,现代企业中各司其职的权力分配类似于角色控制。范围控制前面已经提到过了,今天来谈谈角色权限控制。
角色权限控制是把单项权限一项项的赋予用户,如同现实世界中把具体职位一个个的赋予某个员工一样。在他执行操作前,先看他是否拥有执行此操作的权限,如果有则执行,否则不执行。
在这里我们还是采用上一讲的业务,实现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.blogjava.net/Files/heyang/AOPRoleSample20090106060255.rar
需要的库请自行补充(基本ProjectTodolist的lib全有)。