权限控制是每一个系统都应该有的一个功能,有些只需要简单控制一下就可以了,然而有些却需要进行更加深入和细致的权限控制,尤其是对于一些MIS类系统,基于方法的权限控制就更加重要了。
用反射和自定义注解来实现基于struts2的方法级别的权限控制的主要思想是这样的。
1、先定义一个用于识别在进行action调用的时候标注该方法调用是否需要权限控制,需要什么样的权限的注解类。该注解类一般会包括两个属性,一个是需要的权限,一个是对应的action模块。
2、然后就是在需要进行权限控制的action方法上加上该注解类,并标明其应该拥有的权限和对应的action。这样一来在进行action调用的时候可以实现一个自己定义的interceptor来拦截所有的请求,这样在拦截到请求的时候就可以通过ActionInvocation获取到对应的action类的类文件和对应请求的方法名称,然后利用反射来取得action类文件里面对应的请求方法Method,这样就可通过该Method来判断其是否拥有对应的权限控制注解,即看其是否需要进行权限控制,如果需要进行权限控制,就取得该注解并取得其对应的action名称和需要的权限,然后通过session取得当前的用户,并判断当前用户是否拥有对应的某种权限,如果其拥有该权限则继续往下执行,否则,转到自己处理无权限的机制上去。
下面是一段示例代码:
1、定义一个注解,权限配置
package cn.sigangjun.struts2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 定义一个注解,权限配置
*
* @author sigangjun
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authority {
/** 模块 */
String module();
/** 权限值 */
String privilege();
}
2、用于拦截请求判断是否拥有权限的拦截器AuthorityInterceptor
package cn.sigangjun.struts2;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
/**
* 用于拦截请求判断是否拥有权限的拦截器
*
* @author sigangjun
*
*/
@SuppressWarnings("serial")
public class AuthorityInterceptor extends AbstractInterceptor {
private static final Logger logger = Logger.getLogger(AuthorityInterceptor.class);
public String intercept(ActionInvocation invocation) throws Exception {
String methodName = invocation.getProxy().getMethod();
Class clazz = invocation.getAction().getClass(); // 获取类对象
Method currentMethod = clazz.getMethod(methodName);
// 检查Action类AnnotationTest是否含有@Authority注解
if (currentMethod.isAnnotationPresent(Authority.class)) {
// 从session里取得当前的用户
String currentUser = (String) ServletActionContext.getRequest().getSession().getAttribute("currentUser");
// 取得权限验证的注解
Authority authority = currentMethod.getAnnotation(Authority.class);
// 取得当前请求的注解的action
String module = authority.module();
// 取得当前请求需要的权限
String privilege = authority.privilege();
/**
* 然后可以在此判断当前用户是否拥有对应的权限,如果没有可以跳到指定的无权限提示页面, 如果拥有则可以 继续往下执行。 if (拥有对应的权限) { return invocation.invoke(); } else { return "无权限"; }
*/
logger.warn("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
logger.warn("用户[" + currentUser + "]在" + new SimpleDateFormat("yyyy-MM-dd hh24:mm:ss").format(new Date()) + "调用了[" + clazz.getName() + "]类的[" + methodName + "]方法,所在模块[" + module + "],拥有权限[" + privilege + "]。");
logger.warn("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
return invocation.invoke();
}
}
3、需要进行权限控制的Action
package cn.sigangjun.struts2;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.convention.annotation.InterceptorRef;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
* 请求Action
* @author sigangjun
*
*/
@SuppressWarnings("serial")
@Namespace(value = "/user")
@Results(value = {
@Result(name = "success", location = "/index.jsp"),
@Result(name = "add", location = "/add.jsp"),
})
@Action(value = "action") //默认为:AnnotationAction-->annotation
//@Action(value = "test")
//@Action(value = "/admin/top-set/save", interceptorRefs = {
// @InterceptorRef(value = "fileUpload", params = { "allowedExtensions", "jpg,png,gif" }),
// @InterceptorRef(value = "crudStack"),
// })
public class AnnotationAction extends ActionSupport {
// 访问路径:http://localhost:8080/02struts2/action!list.htm
@Authority(module = "annotation", privilege = "list")
public String list() throws Exception {
ActionContext.getContext().getSession().put("currentUser", "uuu");
System.out.println("-------execute---------");
return SUCCESS;
}
// 访问路径:http://localhost:8080/02struts2/action!add.htm
@Authority(module = "annotation", privilege = "add")
public String add() {
System.out.println("-------test---------");
return "add";
}
}
4、在struts2的配置文件里面配置自定义的权限控制拦截器
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<!--个人发现注解该项必须配置,否则会有问题,如拦截器无效等。 -->
<constant name="struts.convention.default.parent.package" value="default-package" />
<!-- 可选配置项 -->
<!-- 是否支持方法动态调用,即action名!方法名,如user!addUser.action调用user.action中的addUser方法.
不建议使用,这种方法安全性较差 <constant name="struts.enable.DynamicMethodInvocation" value="false" /> -->
<!-- 开发模式下使用,这样可以打印出更详细的错误信息 -->
<constant name="struts.devMode" value="true" />
<!-- 指定Web应用的默认编码集,相当于调用HttpServletRequest的setCharacterEncoding方法 -->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。 -->
<constant name="struts.action.extension" value="jhtml" />
<!-- 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 -->
<constant name="struts.serve.static.browserCache" value="false" />
<!-- 当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 -->
<constant name="struts.configuration.xml.reload" value="true" />
<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
<!-- 配置log4j的日志级别 -->
<!-- <constant name="log4j.logger.org.apache.struts2.convention" value="DEBUG" /> -->
<package name="default-package" extends="convention-default" abstract="true">
<!-- 定义拦截器 -->
<interceptors>
<!-- 申明自定义的权限控制拦截器 -->
<interceptor name="authorityInterceptor" class="cn.sigangjun.struts2.AuthorityInterceptor" />
<!-- 把自定义的权限控制拦截器和默认的拦截器栈加到新的自定义的拦截器栈 -->
<interceptor-stack name="myInterceptors">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="authorityInterceptor" />
</interceptor-stack>
</interceptors>
<!-- 指定新的自定义的拦截器栈为默认的拦截器栈,这样自定义的权限控制拦截器就可以发挥作用了 -->
<default-interceptor-ref name="myInterceptors"></default-interceptor-ref>
</package>
</struts>
5、log4j.properties
log4j.rootCategory=INFO, stdout , R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=D:\\logs\\qc.log
log4j.appender.R.layout=org.apache.log4j.PatternLayout
1log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
log4j.logger.com.neusoft=DEBUG
log4j.logger.com.opensymphony.oscache=ERROR
log4j.logger.net.sf.navigator=ERROR
log4j.logger.org.apache.commons=ERROR
log4j.logger.org.apache.struts=WARN
log4j.logger.org.displaytag=ERROR
log4j.logger.org.springframework=DEBUG
log4j.logger.com.ibatis.db=WARN
log4j.logger.org.apache.velocity=FATAL
log4j.logger.com.canoo.webtest=WARN
log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
log4j.logger.org.hibernate=DEBUG
log4j.logger.org.logicalcobwebs=WARN
log4j.rootCategory=INFO, stdout , R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.File=D:\\logs\\qc.log
log4j.appender.R.layout=org.apache.log4j.PatternLayout
1log4j.appender.R.layout.ConversionPattern=%d-[TS] %p %t %c - %m%n
log4j.logger.com.neusoft=DEBUG
log4j.logger.com.opensymphony.oscache=ERROR
log4j.logger.net.sf.navigator=ERROR
log4j.logger.org.apache.commons=ERROR
log4j.logger.org.apache.struts=WARN
log4j.logger.org.displaytag=ERROR
log4j.logger.org.springframework=DEBUG
log4j.logger.com.ibatis.db=WARN
log4j.logger.org.apache.velocity=FATAL
log4j.logger.com.canoo.webtest=WARN
log4j.logger.org.hibernate.ps.PreparedStatementCache=WARN
log4j.logger.org.hibernate=DEBUG
log4j.logger.org.logicalcobwebs=WARN
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
5、pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.sigangjun</groupId>
<artifactId>02struts2</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>02struts2 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.3.4.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<finalName>02struts2</finalName>
</build>
</project>