利用AOP和自定义标签完成权限控制
2020-03-19 09:19:19
李中华


六年前,我开始从无到有写Lerx的时候,我看的是陈宝峰和张冰的教程。我就猜想是不是可以把权限的事与业务的事分开来搞?张冰的AOP教程中说可能可以实现,他把重点放在dobefore上,认为可以在执行之前检查后决定是否允许执行某个业务方法。当时我才接触SSH2框架,心高,但水平有限,摸索了几天都没搞起来。

这段时间有个业务在手上编写,因为Struts2框架漏洞频频,近期搞得我手忙脚乱。于是我决定抛弃Struts2奔向SpringMVC的怀抱。淘宝上4块钱买的SpringMVC 3和10块钱买的4的教程。看了一遍就开始下手,利用新的业务系统来练手。在练手的时候,我一边在整理自己的“框架”,目的就是大幅减少代码,缩短开发周期,提高效率。当我把后台业务差不多完成时,我考虚权限的事,AOP的经历浮现,然后我就开始再一次尝试了。网上找了一些材料,也有是用自定义注解,但是不是真正的AOP,有的是拦截器,有的是说不上来的东西。最终我如愿以偿。

最后就象盖公章一样,在需要权限检查的方法上盖个章(加个注释)就好了。猛然想起大话西游中的盘丝大仙,给至尊宝盖的那个章(三颗痣)。哈哈,盘丝大法。

好了,以上是背景,下面就直接把方法大致说一下。

1.建立一个自定义标签(上图的RoleCheck.java)


package com.krd.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.web.bind.annotation.ResponseBody;

//自定义注解相关设置
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RoleCheck {

	/*
	 * role 用于标记权限的等级。比如:0的管理员,1是操作员,2是普通用户
	 * ajax 用于标记@ResponseBody注解的ajax返回。这类由于没有视图,可以返回一个字符串用于JavaScript判断和提示
	 * 
	 */
	public int role() default -1;
	public boolean ajax() default false;

}

2.编写AuthAspect,用于切入检查。我用的是环绕通知,并没有用前置通知。前权限检查成功时,通过return pjp.proceed();执行目标方法,否则返回无权限提示页forbid.jsp。



package com.krd.aop;

import javax.servlet.http.HttpSession;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.krd.annotation.RoleCheck;
import com.lerx.sys.util.StringUtil;

/*
 * 试图通过aop实现权限控制功能,未能成功。以后再深入了解
 * 6.7日晚补充:已成功!
 */

@Aspect
@Component
public class AuthAspect {
	
	@Around(value="execution(* com.krd.handlers.*.*(..)) && @annotation(annotation)")
	public Object around(ProceedingJoinPoint pjp,RoleCheck annotation) throws Throwable{
		Boolean auth=false;
		int role=annotation.role();
		boolean ajax=annotation.ajax();
		ServletRequestAttributes ra =(ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
		HttpSession session=ra.getRequest().getSession();
		
		if (session.getAttribute("mask")==null || !StringUtil.isNumber(""+session.getAttribute("mask"))){
			return "forbid";
		}
		int mask=Integer.valueOf(""+session.getAttribute("mask"));
		boolean adminLogin;
		String adminLoginStr=""+session.getAttribute("admin_login_");
		if (session.getAttribute("admin_login_")!=null && adminLoginStr.trim().equals("true")){
			adminLogin=true;
		}else{
			adminLogin=false;
		}
		if (adminLogin){
			switch (role){
			case 0:
				if (mask==0){					//当是0时,只有0--管理员有权限
					auth=true;
				}else{
					auth=false;
				}
				break;
			case 1:
				if (mask>=0){					//当是1时,0--管理员和菜场管理员有权限
					auth=true;
				}else{
					auth=false;
				}
				break;
			default:
				auth=false;
			}
		}else{
			auth=false;
		}
        
		if (auth){
			return pjp.proceed();  //这一行很重要!
		}else{
			if (ajax){
				return "-1";
			}else{
				return "forbid";
			}
			
		}
		
	}
	
	/*@Before(value = "execution(* com.krd.handlers.*.*(..))")
	public void doBefore(JoinPoint point) {  
       
    }  
    
	@Before(value = "execution(* com.krd.handlers.*.*(..))")
    public void doAfter() {  
    }  */

}


3.配置springmvc.xml



<!-- 上略 -->
	<!-- AOP -->
	<aop:aspectj-autoproxy proxy-target-class="true" />
	<mvc:annotation-driven />
	<bean id="aspect" class="com.krd.aop.AuthAspect"/>
<!-- 下略 -->

4.下面就简单了。


在方法前加注解就可以了。如下图。




具体分析我懒得写了,有空再补充吧。

发布:lzh