此权限管理系统把待访问的业务层方法做为权限管理中的资源,通过spring aop 对接口方法进行拦截,来实现权限的管理,可以实现细粒度的权限控制。
在上文体验了spring aop 一些特性,aop 接口:MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice 实现这三个接口分别对方法执行前,后,执行中抛异常等情况进行的,我们要是想做overload 这样的操作时,要用MethodInterceptor 接口,此接口好在有返回值,
public Object invoke(
MethodInvocation invocation)
throws Throwable
{
//.
}
上文做法有些牵强业务逻辑还有throws PermissionDeniedException 感觉不爽,现在用MethodInterceptor 接口,来写这个demo,把权限与业务分开。
advice 如下:
public class PermissionCheckAroundAdvice implements MethodInterceptor {
SecurityManager securityMgr = new SecurityManager();
/**//**
* @param securityMgr The securityMgr to set.
*/
public void setSecurityMgr(SecurityManager securityMgr) {
this.securityMgr = securityMgr;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("(被调用方法接口类名: "
+ invocation.getMethod().getDeclaringClass().getName() + ")");
System.out.println("(被调用方法名:" + invocation.getMethod().getName()+ ")");
String methodName = invocation.getMethod().getDeclaringClass()
.getName() + "." + invocation.getMethod().getName();
System.out.println("(被调用方法全名:" + methodName + ")");
System.out.println("有否权限:(" + securityMgr.checkPermission(methodName)+ ")");
if(securityMgr.checkPermission(methodName))
return invocation.proceed();
System.out.println("Goodbye! NO Permission!(by " + this.getClass().getName() + ")");
return "--";
}
}
服务层业务接口修改如下:
public interface Service {
public String getBeanInfo();
}
服务层业务实现类如下:
public class ServiceBean implements Service {
ResourceBean bean;
/**//**
* @param bean The bean to set.
*/
public void setBean(ResourceBean bean) {
this.bean = bean;
}
public String getBeanInfo(){
String result="";
result+= bean.getMethod1();
result+= bean.getMethod2();
result+= bean.getMethod3();
return result;
}
}
资源层,接口 ,类如下:
public interface Resource {
}
public interface ResourceBean extends Resource{
public void theMethod();
public String getMethod1();
public String getMethod2();
public String getMethod3();
}
public class ResourceBeanImpl implements ResourceBean,InitializingBean{
public void theMethod(){
System.out.println(this.getClass().getName()
+ "." + new Exception().getStackTrace()[0].getMethodName()
+ "()"
+ " says HELLO!");
}
public String getMethod1(){
return "张三";
}
public String getMethod2(){
return "李四";
}
public String getMethod3(){
return "王五";
}
public void afterPropertiesSet() throws Exception {
System.out.println("事件监听:类ResourceBeanImpl属性设置完毕");
}
}
权限管理类:
public class User {
List privilages = new java.util.ArrayList();
String name;
public User(){
}
/**//**
* @param privilages The privilages to set.
*/
public void setPrivilages(List privilages) {
this.privilages = privilages;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public boolean isPermission(String pri){
java.util.Iterator it = privilages.iterator();
String p = "";
boolean pass=false;
while(it.hasNext()){
p=(String)it.next();
System.out.println(p);
if(p.equals(pri)){
pass = true;
break;
}
}
return pass;
}
}
public class SecurityManager {
User user;
public void setUser(User user){
this.user = user;
}
public boolean checkPermission(String privilege){
return checkPermission(user,privilege);
}
public boolean checkPermission(User user, String privilege){
return user.isPermission(privilege);
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--CONFIG-->
<bean id="bean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.jhalo.jsecurity.aop.ResourceBean</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>permissionAroundAdvisor</value>
</list>
</property>
</bean>
<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.jhalo.jsecurity.aop.Service</value>
</property>
<property name="target">
<ref local="serviceBean"/>
</property>
<property name="interceptorNames">
<list>
<value>permissionAroundAdvisor</value>
</list>
</property>
</bean>
<!--CLASS-->
<bean id="resourceMgr" class="com.jhalo.jsecurity.aop.ResourceManager"/>
<bean id="beanTarget" class="com.jhalo.jsecurity.aop.ResourceBeanImpl"/>
<bean id="beanTarget2" class="com.jhalo.jsecurity.aop.ResourceBean2Impl"/>
<bean id="user" class="com.jhalo.jsecurity.aop.User">
<property name="name">
<value>tester</value>
</property>
<property name="privilages">
<list>
<value>com.jhalo.jsecurity.aop.ResourceBean.getMethod3</value>
<value>com.jhalo.jsecurity.aop.Service.getBeanInfo</value>
<value>com.jhalo.jsecurity.aop.ResourceBean.getMethod1</value>
</list>
</property>
</bean>
<bean id="securityMgr" class="com.jhalo.jsecurity.aop.SecurityManager">
<property name="user">
<ref local="user"/>
</property>
</bean>
<bean id="serviceBean" class="com.jhalo.jsecurity.aop.ServiceBean">
<property name="bean">
<!-- <ref local="beanTarget"/>-->
<ref local="bean"/>
</property>
</bean>
<!--ADVISOR-->
<!--Note: An advisor assembles pointcut and advice-->
<!-- -->
<!-- permission around advisor -->
<bean id="permissionAroundAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="thePermissionAroundAdvice"/>
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean>
<!--ADVICE-->
<bean id="thePermissionCheckBeforeAdvice" class="com.jhalo.jsecurity.aop.PermissionCheckAdvice"/>
<bean id="thePermissionThrowsAdvice" class="com.jhalo.jsecurity.aop.PermissionThrowsAdvice"/>
<bean id="thePermissionAroundAdvice" class="com.jhalo.jsecurity.aop.PermissionCheckAroundAdvice">
<property name="securityMgr">
<ref local="securityMgr"/>
</property>
</bean>
</beans>
User 所拥有的权限是在spring 配置文件中手工配置的,在实际应用中不可行,可以从DB中取得。
测试类:
public class SpringAopTest {
public static void main(String[] args) {
//Read the configuration file
ApplicationContext ctx
= new FileSystemXmlApplicationContext("springconfig.xml");
String name = "";
Service sb = (Service)ctx.getBean("service");
// System.out.println("---"+ctx.isSingleton("service")+"---");
name = sb.getBeanInfo();
System.out.println("test result::" +name);
}
}
测试结果 :
(xml.XmlBeanDefinitionReader 119 ) Loading XML bean definitions from file [D:\projects\actives\jsecurity\springconfig.xml]
(support.FileSystemXmlApplicationContext 90 ) Bean factory for application context [org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=25853693]: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [bean,service,resourceMgr,beanTarget,beanTarget2,user,securityMgr,serviceBean,permissionAroundAdvisor,thePermissionCheckBeforeAdvice,thePermissionThrowsAdvice,thePermissionAroundAdvice]; root of BeanFactory hierarchy
(support.FileSystemXmlApplicationContext 287 ) 12 beans defined in application context [org.springframework.context.support.FileSystemXmlApplicationContext;hashCode=25853693]
(support.FileSystemXmlApplicationContext 395 ) Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.StaticMessageSource: {}]
(support.FileSystemXmlApplicationContext 417 ) Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@5e5a50]
(support.FileSystemXmlApplicationContext 439 ) Refreshing listeners
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'resourceMgr'
(support.FileSystemXmlApplicationContext 448 ) Application listener [com.jhalo.jsecurity.aop.ResourceManager@a3d4cf] added
(support.DefaultListableBeanFactory 221 ) Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [bean,service,resourceMgr,beanTarget,beanTarget2,user,securityMgr,serviceBean,permissionAroundAdvisor,thePermissionCheckBeforeAdvice,thePermissionThrowsAdvice,thePermissionAroundAdvice]; root of BeanFactory hierarchy]
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'bean'
(core.CollectionFactory 55 ) Using JDK 1.4 collections
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'beanTarget'
事件监听:类ResourceBeanImpl属性设置完毕
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'permissionAroundAdvisor'
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'thePermissionAroundAdvice'
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'securityMgr'
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'user'
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'service'
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'serviceBean'
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'beanTarget2'
事件监听:类ResourceBean2Impl属性设置完毕
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'thePermissionCheckBeforeAdvice'
(support.DefaultListableBeanFactory 236 ) Creating shared instance of singleton bean 'thePermissionThrowsAdvice'
--------ContextRefreshedEvent called
(被调用方法接口类名: com.jhalo.jsecurity.aop.Service)
(被调用方法名:getBeanInfo)
(被调用方法全名:com.jhalo.jsecurity.aop.Service.getBeanInfo)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
com.jhalo.jsecurity.aop.Service.getBeanInfo
有否权限:(true)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
com.jhalo.jsecurity.aop.Service.getBeanInfo
(被调用方法接口类名: com.jhalo.jsecurity.aop.ResourceBean)
(被调用方法名:getMethod1)
(被调用方法全名:com.jhalo.jsecurity.aop.ResourceBean.getMethod1)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
com.jhalo.jsecurity.aop.Service.getBeanInfo
com.jhalo.jsecurity.aop.ResourceBean.getMethod1
有否权限:(true)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
com.jhalo.jsecurity.aop.Service.getBeanInfo
com.jhalo.jsecurity.aop.ResourceBean.getMethod1
(被调用方法接口类名: com.jhalo.jsecurity.aop.ResourceBean)
(被调用方法名:getMethod2)
(被调用方法全名:com.jhalo.jsecurity.aop.ResourceBean.getMethod2)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
com.jhalo.jsecurity.aop.Service.getBeanInfo
com.jhalo.jsecurity.aop.ResourceBean.getMethod1
有否权限:(false)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
com.jhalo.jsecurity.aop.Service.getBeanInfo
com.jhalo.jsecurity.aop.ResourceBean.getMethod1
Goodbye! NO Permission!(by com.jhalo.jsecurity.aop.PermissionCheckAroundAdvice)
(被调用方法接口类名: com.jhalo.jsecurity.aop.ResourceBean)
(被调用方法名:getMethod3)
(被调用方法全名:com.jhalo.jsecurity.aop.ResourceBean.getMethod3)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
有否权限:(true)
com.jhalo.jsecurity.aop.ResourceBean.getMethod3
test result::张三--王五
这样就完全把企业业务逻辑与权限管理系统分开了,服务层,资源层与权限检查分离,用spring aop 通过配置文件把它们粘合在一起。实现了细粒度(对资源层数据)的权限检查。
接下来在此权限管理系统中引用角色概念,向 rbac 系统进军.(未完待续)
参考资料:
An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 1 by Russell Miles -- The Spring framework, which supports development of the different facets of J2EE, provides an aspect-oriented programming module that gives Spring developers the opportunity to apply aspects to their applications. This article shows you how to work with AOP in Spring.
An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 2 by Russell Miles -- Russ Miles continues his introduction to Aspect-Oriented Programming (AOP) in Spring by delving into the around advice, which allows you to not just add to an existing method implementation, but to completely replace it.
方向:分布式系统设计