使用Struts2的拦截器和自定义注解方式实现权限控制

lyc1976 3年前
   <h3><strong>1. 自定义注解的编写:@interface</strong></h3>    <p>说明:使用Spring的AOP也可以实现权限的控制,但是经过Spring的AOP方法处理后再交给Struts2时,注意Struts2中上下文参数丢失问题。</p>    <p>Struts2的拦截器使用了动态代理,从动态代理类中获取调用方法名并通过invocation.getAction().getClass().getMethod(methodName)获取被调用的方法。然后根据session中保存的用户权限和从方法注解中获取的权限进行判断,看用户是否具有调用该方法的权限进行处理。</p>    <p>下面是使用Struts2的Interceptor拦截器实现权限控制的例子:</p>    <p>这里我们使用注解为每个Action的方法声明权限。</p>    <p>首先创建自定义注解Permission,用于设置权限:</p>    <pre>  <code class="language-java">@Retention(RetentionPolicy.RUNTIME)  @Target(ElementType.METHOD)  public @interface Permission {   /** 权限名称 */   String privilege();  }</code></pre>    <p>其中的Permission的module和privilege为模块和模块对应的权限,每个用户登录时都把权限信息保存到session中,方便在拦截器中对权限的判断。</p>    <h3><strong>2. Struts2 Interceptor拦截器的编写和配置</strong></h3>    <p>1、 编写一个拦截器实现Interceptor接口或者继承AbstractInterceptor</p>    <p>接下来最重要的就是编写拦截器对权限进行判断,并作出正确的处理:</p>    <pre>  <code class="language-java">public class PrivilegeInterceptor extends AbstractInterceptor {     private static final long serialVersionUID = 742285864455102141L;     /**    * 1、通过动态代理类和反射机制获取调用的方法    * 2、获取方法的Permission注解    * 3、获取Permission中包含的权限信息(构造SystemPrivilege权限类)    * 4、和用户的权限信息进行对比(PrivilegeGroup中的SystemPrivilege)判断用户的权限    */   @Override   public String intercept(ActionInvocation invocation) throws Exception {      HttpServletRequest request = ServletActionContext.getRequest();    if(WebUtil.getRequestURI(request).startsWith("/cms/control/")){     ActionProxy actionProxy = invocation.getProxy();     String methodName = actionProxy.getMethod();     if (StringUtils.isBlank(methodName)) {         methodName = "execute";//默认方法     }     Class aClass = actionProxy.getAction().getClass();     Method method = aClass.getMethod(methodName);      if(method != null && method.isAnnotationPresent(Permission.class)){      System.out.println("Call the Action Method:" + method.toString());      //获取当期执行的Action上的注解      Permission permission = method.getAnnotation(Permission.class);      if (permission != null) {          System.out.println("当前需要的权限为:" + permission.module());          //获取当期执行的Action方法需要的权限          SystemPrivilege methodPrivilege = new SystemPrivilege(permission.privilege());          //WebUtil.getEmployee(request) 方法是从session中获取保存的employee信息,包括权限集合          Employee employee = WebUtil.getEmployee(request);          //循环判断用户是否具有该权限,如果有则继续执行,否则返回提示视图          for(PrivilegeGroup group : employee.getGroups()){           if(group.getPrivileges().contains(methodPrivilege)){            return invocation.invoke();           }          }          System.out.println("权限不足");          return "privilegemessage";      }     }     System.out.println("未设置权限");     return invocation.invoke();    }    System.out.println("所有权限");    return invocation.invoke();   }  }</code></pre>    <p>2、 在struts.xml文件中注册自定义拦截器</p>    <p>3、 在需要使用的Action中引用自定义拦截器。</p>    <p>在想要设置权限的方法加入自定义的Permission注解用于标识该方法的权限:</p>    <pre>  <code class="language-java">@Permission(privilege="confirmOrder")  public String confirmOrder(){      …  }</code></pre>    <h3><strong>3. Struts2拦截器原理</strong></h3>    <p>AOP面向切面编程和动态代理</p>    <p>下面开始讲一下主菜ActionProxy了,在这之前最好先去了解一下动态Proxy的基本知识。</p>    <p>ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法。归根到底,最后调用的是DefaultActionInvocation.invokeAction()方法。</p>    <p>DefaultActionInvocation()->init()->createAction()。</p>    <p>最后通过调用</p>    <p>ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction() 这里的步骤是先由ActionProxyFactory创建ActionInvocation和ActionProxy。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0860083d5eecf1896bb728a2d16f8b32.jpg"></p>    <p>一个请求在Struts2框架中的处理分为以下几个步骤:</p>    <p>1.客户端发出一个指向servlet容器的请求(tomcat);</p>    <p>2.这个请求会经过图中的几个过滤器,最后会到达FilterDispatcher过滤器。</p>    <p>3.过滤器FilterDispatcher是struts2框架的心脏,在处理用户请求时,它和请求一起相互配合访问struts2的底层框架结构。在web容器启动时,struts2框架会自动加载配置文件里相关参数,并转换成相应的类。</p>    <p>如:ConfigurationManager、ActionMapper和ObjectFactory。ConfigurationManager 存有配置文件的一些基本信息,ActionMapper存有action的配置信息。在请求过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。过滤器会通过询问ActionMapper类来查找请求中需要用到的Action。</p>    <p>4.如果找到需要调用的Action,过滤器会把请求的处理交给ActionProxy。ActionProxy为Action的代理对象。ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类。</p>    <p>5.ActionProxy创建一个ActionInvocation的实例。ActionInvocation在ActionProxy层之下,它表示了Action的执行状态,或者说它控制的Action的执行步骤。它持有Action实例和所有的Interceptor。</p>    <p>6.ActionInvocation实例使用命名模式来调用,1. ActionInvocation初始化时,根据配置,加载Action相关的所有Interceptor。2. 通过ActionInvocation.invoke方法调用Action实现时,执行Interceptor。在调用Action的过程前后,涉及到相关拦截器(intercepetor)的调用。</p>    <p>7. 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。</p>    <p> </p>    <p>来自:http://www.linkedkeeper.com/detail/blog.action?bid=60</p>    <p> </p>