细粒度 自定义注解 权限控制具体实现
https://www.yqxbc.com win10系统 发布时间:2018-08-08 13:03 来源:一起学编程 浏览:加载中

第一步:

注解和xml一样只是起到一个配置的作用。注解的本身是不可能完成这种拦截功能的。我们后面会通过注册一个驱动了去解析这个注解,完成这个注解背后所代表的功能。

新建注解:

public @interface Permission {

 

}

 

我们这个项目是通过两个属性来代表这个权限的,所以我们这里也需要两个属性。

public @interface Permission {

    String module();

    String privilege();

}

标注@Retention

通过这个注解来表标注:这个注解的配置在这个java类中的存放的范围,比如:

@Retention(RetentionPolicy.SOURCE)
只能保留在源代码上面,一但被编译成class之后这个注解就会丢失

@Retention(RetentionPolicy.CLASS)

在源代码中和编译过后的class中都回存在这个注解,但是当这个类被装载进java虚拟机后,这个注解就会丢失了

@Retention(RetentionPolicy.RUNTIME)

代表这个注解要保留至运行期

所以我们这里一定要标注为运行期

@Retention(RetentionPolicy.RUNTIME)

public @interface Permission {

    String module();

    String privilege();

}

@Target

用来标识这个注解他可以标注在什么地方。

@Target({ElementType.FIELD})

可以标注在字段上面

@Target({ElementType.FIELD,ElementType.METHOD})

也可以标注在方法上面

@Target({ElementType.FIELD,ElementType.METHOD,ElementType.TYPE})

也可以标注在类型上面

我们这里只需标注在方法上面就可以了:

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface Permission {

      String module();

      String privilege();

}

那么我们就开始第二步应用了:

比如在部门管理action里面:

(我们前面初始化的时候就初始化了一些权限,那么我们就可以把初始化的数据拷贝过来)

/**

 * 部门管理

 */

@Controller("/control/department/manage")

public class DepartmentManageAction extends DispatchAction {

    @Resource DepartmentService departmentService;

   

    /**

     * 部门添加界面

     */

    @Permission(module="department",privilege="insert")

    public ActionForward addDepartmentUI(ActionMapping mapping, ActionForm form,

           HttpServletRequest request, HttpServletResponse response) throwsException {

       DepartmentForm formbean = (DepartmentForm)form;

       return mapping.findForward("add");

}

。。。。。。。。

这样我们就标注上了这个方法需要department模块里面的insert权限。这样就可以细粒度的表面,用户想要执行这个方法就需要这个方式上面标注的权限。

第三步:

我们最关键的步骤,那么我们如何当用户执行这个方法的之前拦截,来判断他是否具有这个权限。如果没有权限就让他回到一个提示页面。首先要获取他的注解,

 

那么我们会想到使用aop切面编程:因为我们需要方法执行前需要做一个权限判断工作

通知:拦截到这个方法调用的时候需要执行的工作。

对所有的action方法进行拦截  –> 切入点

其中的一个方法  à 连接点

切面编程 à 我们对横切关注点

Aop步骤:拦截action  à 获取方法上的权限注解 à 判断登陆的用户是否有用该注解  à 如果存在就允许执行 ;否则提示没有权限界面

环绕通知:around

前绕通知:如果前面执行没抛出以外,后面的方法还是会正常执行。

首先我们要确定是否已经被spring进行管理才能用aop,目前我们的action都已经被spring进行管理了。

定义切面的配置方式:

一:xml

 

二:注解

我们定义一个类,加上注解把他定义为切面。

@Aspect

@Component

public class Interceptor {

    @Pointcut("execution(org.apache.struts.action.ActionForward cn.itcast.web.action..*.*(org.apache.struts.action.ActionMapping,org.apache.struts.action.ActionMapping,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse))")

    private void actionMethod(){

      

    }

   

    @Around("actionMethod()")

    public Object intercept(ProceedingJoinPoint pjp) throws Throwable {

       System.out.println("被拦截到的方法为:"+ pjp.getSignature().getName());

       return pjp.proceed();//执行被拦截的方法

    }

   

}

我们可以这样测试,有没有拦截到我们要执行的方法。

测试结果:execute方法可以被拦截,自定义方法无法拦截。如果继承的是action,可以被拦截到,如果继承的是dispatchAction,不能被拦截到

原因:简单说,如果是通过反射技术调用的方法不能拦截到,否则就可以被拦截。

那么为什么反射技术就不能被拦截到?

之所以我们在执行action执行,他能够被拦截,执行之前输出一句话,客户端从spring里面获得action bean是一个代理对象,代理对象中的方法内部呢:才会调用目标对象的方法,他会在执行代理对象之前呢,先执行输出方法,再执行目标方法,那么我们这样就在执行之前输出了这句话。

DispatchAction 继承 BaseAction ,BaseAction 继承 Action ,最终还是继承的Action,那么我们请求交给DispatchAction 执行时,他还是要执行execute方法,我们的DispatchAction对execute方法做了些手脚,重写了这个方法,他会获得配置文件里面的方法名称parameter,这个执行获取方法会通过反射技术获得method里面的方法。他里面没有接口,如果这个action没有接口,就会采用CGlib重写父类非final方法,

 

public class DepartmentManageAction extends DispatchAction {

    @Resource DepartmentService departmentService;

   

    /**

     * 部门添加界面

     */

    @Permission(module="department",privilege="insert")

    public ActionForward addDepartmentUI(ActionMapping mapping, ActionForm form,

           HttpServletRequest request, HttpServletResponse response) throwsException {

       DepartmentForm formbean = (DepartmentForm)form;

       return mapping.findForward("add");

}

Public class Proxy98 extends DepartmentManageAction{

         Private Object target;//目标对象

         public ActionForward addDepartmentUI(ActionMapping mapping, ActionForm form,

           HttpServletRequest request, HttpServletResponse response) throwsException {

       他会调用我们定义的那个通知里面的环绕通知

       Return Interceptor。Intercept(pjp){

    System.out.println("被拦截到的方法为:"+ pjp.getSignature().getName());

       return pjp.proceed();//执行被拦截的方法

              判断后面还有没有要执行的切面,没有就会执行

Targer。EditDepartmentmentUi;

我们这样开来,他应该回输出啊,会执行我们定义的输出方法啊,但是你要记住我们无乱使用的是action还是DispatchAction都回先执行execute方法。Spring不会为代理对象重写的父类方法加入通知,他只会调用目标对象的execute方法。

}

}

 

}

 

总结:为什么DispatchAction不可以

我们环绕通知切面编程原理,如果要拦截的对象没有实现接口,那么就会对他的内部方法也就目标对象生成代理对象(也就是加入通知),但是我们他不会对被拦截的类的父类方法加入通知,而我们的DispatchAction方法最终还是集成的Action方法,所以会先执行execute方法,execute方法所以不会加入通知,只会直接执行目标对象execute方法,而我们DispatchAction方法重写了execute方法,execute内部会通过invoke反射调用我们的目标方法,也就是上面的addDepartmentUI,我们目标对象addDepartmentUI方法内部本身并没有我们单独加上的处理(比如上面的输出),也就是说真实的执行过程,压根就没调用我们的代理对象,而是直接走的没有加入通知的execute方法。

正确的解决思路:

必须要对spring代理对象的execute方法进行改造,不建议改写。

我们最终的目的是拦截action的方法,我们可以自己来实现aop功能:

         继承requestProssor(请求处理器),实现对action的方法进行拦截。

RequestProcessor:

RequestProcessor原理:当一个请求到来时,他会先交给RequestProcessor对象,RequestProcessor会根绝用户的请求路径找到匹配的action,再把请求交给对应的action进行处理。我们就可以在调用action之前做一下权限校验。

RequestProcessor内部具体操作:

首先%3