余 清, 软件工程师, IBM
简介: 使用 Annotation 可以非常方便的根据用户的不同角色,分配访问 Java 方法的权限。在 Java Web 开发中,使用这种方法,可以提高系统的松耦合度,方便维护。
发布日期: 2013 年 5 月 13 日 
访问情况 : 4021 次浏览 
评论:  (查看 | 添加评论 - 登录)
平均分 (11个评分)
为本文评分
在 Web 开发过程中,一个非常理想的开发过程是,开发人员在开发中并不需要关心权限问题,不需要在 Java 方法中写很多逻辑判断去判断用户是否具有合适的角色和权限,这样开发会花费非常多的人力成本,因为所有的开发人员都需要了解关于权限的详细内容,也非常不容易进行后期维护。我们希望有专门的很少数量的开发人员了解权限内容,并且可以随时方便的修改和配置。于是,我们使用 Annotation,在 Java 方法之前使用 Annotation 可以非常方便的添加,修改和删除对于权限的管理功能。
本文描述了在开发过程中经常遇到的关于权限验证问题的一个典型应用案例,这个案例描述如下:系统要求只有登录用户才可以下定单。通过这个简单的例子,我们将看到如何完成整个系统的权限控制。
本文的开发环境如下:
Struts2
Spring 3.0
JDK1.6
AspectJ 6.9
本文将分为以下几个章节,详细描述提出的权限验证方法:
AOP 的基本概念
权限验证系统架构详细讲解
AOP 的基本概念
AOP 是 Aspect Oriented Programming 的缩写,意思是面向方面的编程。我们在系统开发中可以提取出很多共性的东西作为一个 Aspect,可以理解为在系统中,我们需要很多次重复实现的功能。比如计算某个方法运行了多少毫秒,判断用户是不是具有访问权限,用户是否已登录,数据的事务处理,日志记录等等。
一般我们描述一个故事,都会说什么时间什么地点发生了什么事情,那 Join Point 的意思是,发生的地点,Advice 就是发生了什么事,Aspect 就是这个故事的整体,包含了 Join Point 和 Advice。PointCut 又把地点进行了规律性的总结,比如使用正则表达式 (com.example.service.*,即所有在 service 包下面的方法),把所有 Advice 发生的地点进行描述。
读者现在应该已经大概了解了 AOP 的基本概念,下面我们再来详细介绍一下:
Join Point:表示在程序中明确定义的执行点,典型的 Join Point 包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 Join Point。
PointCut:表示一组 Join Point,这些 Join Point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
Advice:Advice 定义了在 PointCut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 Join Point 之前、之后还是代替执行的代码。
回页首
基于 Annotation 的 Spring AOP 权限验证方法的实现
Spring AOP 目前只支持基于 method 的 Join Points,而不支持基于 fileds 的 Join Points,也可以使用 AspectJ 去实现基于 fields 的 AOP,这并不会破坏 Spring 的核心 API。 Spring AOP 更倾向于配合 Spring IoC 去解决在企业级系统中更为普遍的问题。
在这个具体的例子中,我们实现了这样一个场景,在用户下订单的时候,先判断用户是否已经登录,如果用户没有登录,系统将转到登录页面,要求用户登录。
1. 配置 applicationContext
在 Spring 中支持 AOP 的配置非常的简单,只需要在 Spring 配置文件 applicationContext.xml 中添加:
<aop:aspectj-autoproxy/>
同时在 applicationContext.xml 的 schema 中配置:
清单 1
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/cache 
http://www.springframework.org/schema/cache/spring-cache.xsd 
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> 
在配置时,我们需要将引用的 jar 包放置在 WEB-INF/lib 目录下面:
需要的 jar 包有
配置这些之后 Spring AOP 就可以开始工作了。
2. 定义 Annotation
首先,我们定义一个常量来表示用户是否登录:
清单 2
p
package com.example.myenum; 
public enum ISLOGIN { 
YES, 
LOGOUT, 
NO 
这里也可以选择不使用 enum,UserAccessAnnotation 中的 isLogin() 方法也可以返回整数或 String 类型,返回类型并没有限制。常量定义之后,我们再定义 Annotation,在 UserAccessAnnotation 中定义 isLogin(),表示用户是否已经登录:
清单 3
p
package com.example.annotation; 
i
import java.lang.annotation.ElementType; 
i
import java.lang.annotation.Retention; 
i
import java.lang.annotation.RetentionPolicy; 
i
import java.lang.annotation.Target; 
i
import com.example.myenum.ISLOGIN; 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface UserAccessAnnotation { 
/** 
* User has been login or not. 
*/ 
ISLOGIN isLogin(); 
定义好之后,这个 Annoatation 将可以被放置在需要验证用户是否登录的方法前面,就像下面这样:
清单 4
p
package com.example.aspect; 
p
public class OrderAction extends BaseAction{ 
……
@UserAccessAnnotation(>isLogin=ISLOGIN.YES) 
public String Order(){ 
try{ 
Boolean result = orderService.order(Quote quote); 
if(result) return SUCCESS; 
}catch(Exception e) { 
logger.debug(e); 
this.addActionError(getText("user_no_permission_error")); 
return INPUT; 
……
在这里我们使用 UserAccessAnnotation 来表示需要在 Order 方法执行之前判断用户是否已经登录,如果没有登录,在 struts2 中,通过下面定义的 Exception 的捕获机制,将页面转到登录页面。
3. 在 applicationContext.xml 中定义 Aspect
清单 5
<
<bean id="permission" class="com.example.aspect.PermissionAspect" scope="prototype"> 
<property name="authService" ref="AuthService" /> 
<
</bean> 
我们要在 Spring 中定义 PermissionAspect。在 Struts+Spring 架构中可以把 Aspect 看作是一个 Action,只不过 Aspect 是其他 Action 的前提条件或者结束动作。Aspect 定义中的 Service 属性和 Action 中的 Service 属性没有任何区别。这里我们用 AuthService 类来实现判断用户是否已经登录的逻辑。
4. 定义 PointCut
清单 6
@
@Aspect 
p
public class SystemArchitecture { 
/** 
* A Join Point is defined in the action layer where the method needs 
* a permission check. 
*/ 
@Pointcut("@annotation(com.example.annotation.UserAccessAnnotation)") 
public void userAccess() {} 
}
PointCut 即切入点,就是定义方法执行的点,before、after 或者 around。 一般情况下,我们把 PointCut 全部集中定义在 SystemArchitecture 类中,以方便修改和管理。
当实现 Aspect 时可以很方便的使用我们在 SystemArchitecture 中定义的 PointCut。
5. 实现 Aspect
清单 7
package com.example.aspect; 
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Before; 
import com.example.annotation.UserAccessAnnotation; 
import com.example.base.action.BaseAction; 
import com.example.myenum.USERTYPE; 
import com.example.service.AuthService; 
@Aspect 
public class PermissionAspect extends BaseAction{ 
……
AuthService authService = null; 
@Before(value="com.example.aspect.SystemArchitecture.userAccess()&&"+ 
"@annotation(userAccessAnnotation)",argNames="userAccessAnnotation") 
public void checkPermission(UserAccessAnnotation userAccessAnnotation) 
throws Exception{ 
IsLogin isLogin = userAccessAnnotation.isLogin (); 
if(!authService.userLogin(user).equals(isLogin.toString())){ 
throw new NoPermissionException(getText("user_no_permission_error")); 
……
在 checkPermission 方法前,我们首先定义 PointCut:
@Before(value="com.example.aspect.SystemArchitecture.userAccess()&&"+ 
"
"@annotation(userAccessAnnotation)",argNames="userAccessAnnotation").
argNames="userAccessAnnotation" 的意思是把 Annotation 当做参数传递进来,并判断用户登录状态是否与 Annotation 中的定义一致。如果不一致,就要抛出 NoPermissionException,通知系统该用户没有权限。
6. 在 Struts action 配置文件中定义 Global Exception
在 Struts.xml 中配置:
清单 8
<global-results> 
<result name="loginerror">/WEB-INF/jsp/login.jsp</result> 
</global-results> 
<global-exception-mappings> 
<exception-mapping exception="com.example.exceptions.NoPermissionException" 
result="loginerror"/> 
</global-exception-mappings> 
经过上面的配置,在 NoPermissionException 抛出之后,Struts2 会 catch 这个 exception,并转到 login.jsp 页面。
Annotation 的放置位置时非常灵活的,并不局限于放置在 Struts2 的 Action 之前,若您没有使用 struts,也可以放置在 Service 类的实现方法之前,让调用方法捕捉 exception。Aspect 如何处理用户没有登录的情况也可以根据实际需要去实现,同样不局限于抛出 exception 这种方式。总之,处理方法是非常灵活的,根据读者的需要可以随机应变。
回页首
总结
综上所述,我们利用在 Struts Action 之前增加 Annotation 的方式非常方便的验证用户在系统中的访问权限。需要验证登录与否的方法之前,只要简单的添加 Annotation,就可以进行登录判断。可见,通过这种方式,只需要很少的人力就可以管理整个系统的权限控制。可以很好的控制项目开发的成本,增加系统的灵活性。

基于 Annotation 拦截的 Spring AOP 权限验证方法相关推荐

  1. Spring AOP根据JdbcTemplate方法名动态设置数据源

    2019独角兽企业重金招聘Python工程师标准>>> 说明:现在的场景是,采用数据库(Mysql)复制(binlog)的方式在两台不同服务器部署并配置主从(Master-Slave ...

  2. spring AOP对父类方法加强分析

    spring AOP可以对方法进行加强,就是在方法前执行一些想要的事情,执行方法后想执行一些信息,原理就是利用动态代理,具体不在阐述 今天要讨论的是一个springBean继承了父类,在父类里进行了方 ...

  3. java aop管理权限_基于spring aop 权限管理系统原型 - andyj2ee - BlogJava

    此权限管理系统把待访问的业务层方法做为权限管理中的资源,通过spring aop 对接口方法进行拦截,来实现权限的管理,可以实现细粒度的权限控制. 在上文体验了spring aop 一些特性,aop ...

  4. Spring学习总结(17)——Spring AOP权限管理

    每个项目都会有权限管理系统 无论你是一个简单的企业站,还是一个复杂到爆的平台级项目,都会涉及到用户登录.权限管理这些必不可少的业务逻辑.有人说,企业站需要什么权限管理阿?那行吧,你那可能叫静态页面,就 ...

  5. 基于mybatis拦截器实现数据权限

    数据权限是很多系统常见的功能,实现的方式也是很多的,最近在做项目的时候,自己基于mybatis拦截器做了一个数据权限的功能. **功能设计 a)  需要做数据权限功能的表加上一个权限id字段. 权限i ...

  6. spring AOP实现——xml方法

    上一文中 讲了Annotation如何配置AOP,地址如下:http://5148737.blog.51cto.com/5138737/1428048 使用同样的bean,用xml来实现一下: Hel ...

  7. Struts2自定义拦截器实例—登陆权限验证

    版本:struts2.1.6 此实例实现功能:用户需要指定用户名登陆,登陆成功进入相应页面执行操作,否则返回到登陆页面进行登陆,当直接访问操作页面(登陆后才能访问的页面)时则不允许,须返回登陆页面. ...

  8. Struts2自己定义拦截器实例—登陆权限验证

    版本号:struts2.1.6 此实例实现功能:用户须要指定username登陆,登陆成功进入对应页面运行操作,否则返回到登陆页面进行登陆,当直接訪问操作页面(登陆后才干訪问的页面)时则不同意,须返回 ...

  9. initbinder对ajax不起作用,Spring MVC InitBinder验证方法

    使用InitBinder做验证的情况一般会在此Controller中提交的数据需要有一些是业务性质的,也即比较复杂的验证情况下才会使用.大部份简单的表单验证,使用annotation验证即可以解决. ...

最新文章

  1. js 数组遍历符合条件跳出循环体_Javascript数组循环遍历之forEach详解
  2. Wpf Binding.Path设置
  3. 海上瓶子下有东西吗_洗衣液瓶子我从来不扔,瓶身这样剪几刀,解决了很多家庭的大烦恼...
  4. python的列表就是数组吗_python中list和数组的区别是什么?
  5. Python学习入门基础:注释、变量基本使用、变量的命名
  6. 终端中用命令成功修改linux~Ubuntu PATH环境变量
  7. Python3之文件的读、写、修改操作
  8. wince系统_汽车操作系统分类
  9. 南阳ACM 题目275:队花的烦恼一 Java版
  10. 天猫tf卡速度测试软件,没有对比就没有伤害,老司机实测告诉你高速TF卡究竟有什么好处...
  11. linux下淘宝支付宝安全控件安装
  12. 第二章 SPSS 的数据管理
  13. win10桌面计算机打不开,win10开机后桌面无响应,win10开机后啥都打不开
  14. qq空间显示手机型号android,手机QQ空间说说怎么显示手机型号
  15. sslcontext java_java – SSLContext初始化
  16. Python学习,用python制作字符版gif图
  17. Solution: Cannot start Microsoft outlook. Cannot open the outlook window. Invalid xml
  18. 山东大学为什么火了_比校花更诱人,山东大学因为它,火了!
  19. bat 打开常用软件
  20. 比程序员更好的职业_立即成为更好的程序员的20条技巧

热门文章

  1. csgo卡程序关不掉_微信推QQ小程序,取代QQ?网友:这功能有用?
  2. Kotlin-如何创建一个好用的协程作用域
  3. 2021-01-07 Python Opencv转换颜色空间 RGB转为HSV
  4. python PIL 单张图像变换大小—— img.resize()
  5. html5和响应式,35个响应式HTML5和CSS3模版
  6. java编译器id_JAVA 词法编译器
  7. mybatis清除一级缓存的几种方法
  8. JAVA模拟某信网登录信息采集
  9. 【Linux】19.Linux环境变量名LD_LIBRARY_PATH 和 ldd命令详解
  10. Spring AOP中定义切点(PointCut)和通知(Advice)