之前的博客介绍了拦截器的概念以及Struts2自带以及自定义拦截器的一些基础知识,但是拦截器究竟如何应用在实际项目里,或许你还很迷惑,那么本次博客我们就一起来实战一下,也算是对拦截器的一个总结和应用实践。
我们在做任何的信息管理系统的时候,无可避免的要进行权限控制,对于登录用户的身份以及所拥有的权限进行验证,不让不合法的用户随意更改我们的数据和程序,以保证系统的安全性。这样一个非常普遍的功能,我们要使用Struts2的拦截器去实现权限检查,当浏览者需要请求执行某个操作时,应用系统需要先检查用户是否登录,以及是否有足够的权限来执行该操作。
在这个Demo中,我们要求用户必须登录,并且必须为指定用户名的用户才可以查看系统中的某个视图资源,否则,系统直接转入登录页面。对于这样的需求,可以在每个Action执行实际的处理逻辑之前,先进行权限验证逻辑,但是这种做法不利于代码的复用,因为大部分Action里的权限验证代码都大同小异,故将这些权限验证的代码放在拦截器中将显得更加方便和灵活,也更为专业。
检查用户是否登录,通常都是通过跟踪用户的Session来完成的,通过ActionContext即可访问到session中的属性,拦截器的intercept方法的invocation参数可以很容易地访问到请求相关的ActionContext实例。
实现了上述的权限验证拦截器,就可以在配置文件中随意使用该拦截器对需要实现权限控制的Action进行配置,使之具有权限控制的功能。具体在struts.xml文件中如何定义和应用于Action中,请看如下具体配置:
考虑到该拦截器的复用性问题,系统可能每个页面都需要进行权限控制,因此可以将login的结果映射定义为全局结果映射,具体配置方法看上面代码。如果要简化struts.xml文件的配置量,避免在每个Action中重复配置该拦截器,可以将该拦截器与Struts2默认的拦截器栈放在一起,定义成新的默认拦截器栈mydefaultstack,这样就不用在每个Action中重复定义权限验证拦截器了,默认已有,这体现了抽象—>封装的思想,跟我们编写代码时遇到重复代码就抽出来进行封装复用是一个道理。
我们在做任何的信息管理系统的时候,无可避免的要进行权限控制,对于登录用户的身份以及所拥有的权限进行验证,不让不合法的用户随意更改我们的数据和程序,以保证系统的安全性。这样一个非常普遍的功能,我们要使用Struts2的拦截器去实现权限检查,当浏览者需要请求执行某个操作时,应用系统需要先检查用户是否登录,以及是否有足够的权限来执行该操作。
在这个Demo中,我们要求用户必须登录,并且必须为指定用户名的用户才可以查看系统中的某个视图资源,否则,系统直接转入登录页面。对于这样的需求,可以在每个Action执行实际的处理逻辑之前,先进行权限验证逻辑,但是这种做法不利于代码的复用,因为大部分Action里的权限验证代码都大同小异,故将这些权限验证的代码放在拦截器中将显得更加方便和灵活,也更为专业。
检查用户是否登录,通常都是通过跟踪用户的Session来完成的,通过ActionContext即可访问到session中的属性,拦截器的intercept方法的invocation参数可以很容易地访问到请求相关的ActionContext实例。
我们先来编写权限验证拦截器类的代码,具体代码如下
// 权限检查拦截器继承AbstractInterceptor类
public class AuthorityInterceptor
extends AbstractInterceptor
{
// 拦截Action处理的拦截方法
public String intercept(ActionInvocation invocation)
throws Exception
{
// 取得请求相关的ActionContext实例
ActionContext ctx = invocation.getInvocationContext();
Map session = ctx.getSession();
// 取出Session里的user属性
String user = (String)session.get("user");
//如果没有登录,或者登录所用的用户名不是admin,都返回重新登录
if (user != null && user.equals("admin") )
{
return invocation.invoke();
}
// 如果没有登录,将服务器提示放入ActionContext中
ctx.put("tip" ,"您还没有登录,请输入管理员账号登录系统");
// 返回login的逻辑视图
return Action.LOGIN;
}
}
从上面的代码中可以看到,先通过ActionInvocation参数获取用户的Session实例的引用,然后从中取出user属性,通过判断该属性值来确定用户是否登录系统,从而判断是否需要转入登录页面。实现了上述的权限验证拦截器,就可以在配置文件中随意使用该拦截器对需要实现权限控制的Action进行配置,使之具有权限控制的功能。具体在struts.xml文件中如何定义和应用于Action中,请看如下具体配置:
<package name="lee" extends="struts-default">
<!-- 用户拦截器定义在该元素下 -->
<interceptors>
<!-- 定义了一个名为authority的拦截器 -->
<interceptor name="authority"
class="org.ljw.app.interceptor.AuthorityInterceptor"/>
</interceptors>
<!-- 定义全局Result -->
<global-results>
<!-- 当返回login视图名时,转入loginForm.jsp页面 -->
<result name="login">/WEB-INF/content/loginForm.jsp</result>
</global-results>
<action name="login" class="org.ljw.app.action.LoginAction">
<result name="error">/WEB-INF/content//error.jsp</result>
<result>/WEB-INF/content/welcome.jsp</result>
</action>
<!-- 定义一个名为viewBook的Action,其实现类为ActionSupport -->
<action name="viewBook">
<!-- 返回success视图名时,转入viewBook.jsp页面 -->
<result>/WEB-INF/content/viewBook.jsp</result>
<interceptor-ref name="defaultStack"/>
<!-- 应用自定义拦截器 -->
<interceptor-ref name="authority"/>
</action>
<action name="*">
<result>/WEB-INF/content/{1}.jsp</result>
</action>
</package>
上面名为viewBook的Action,没有指定class属性,默认使用ActionSupport类,配置该Action时,只指定了一个结果映射,指定系统返回success字符串时,系统将转入viewBook页面,但并未配置login视图名对应的jsp页面。考虑到该拦截器的复用性问题,系统可能每个页面都需要进行权限控制,因此可以将login的结果映射定义为全局结果映射,具体配置方法看上面代码。如果要简化struts.xml文件的配置量,避免在每个Action中重复配置该拦截器,可以将该拦截器与Struts2默认的拦截器栈放在一起,定义成新的默认拦截器栈mydefaultstack,这样就不用在每个Action中重复定义权限验证拦截器了,默认已有,这体现了抽象—>封装的思想,跟我们编写代码时遇到重复代码就抽出来进行封装复用是一个道理。