6.6 使用拦截器完成权限控制
权限控制是大部分Web应用应有的功能,当用户请求某个操作时,Web应用都应该检查用户的合法性以及操作权限等,从而来控制用户是否可以进行某个操作。
6.6.1 完善登录案例
前面都是讲的拦截器的语法,下面来看如何通过自定义拦截器来完成权限控制。
首先修改LoginAction,使用Session来保存用户的登录状态。因为Action已经和Servlet API完全的解耦了,所以可以通过ActionContext的getSession方法来返回Session对象。这里返回的Session类型为Map而不是HttpSession。使用Map类的put()方法在session范围中添加属性login,并设置其属性值为true,代码如下所示。
public String execute() throws Exception { //new一个LoginCheck对象 LoginCheck lc = new LoginCheck(); //调用业务逻辑组件来判断 if(lc.isLogin(getUname(), getUpassword())){ //如果为合法用户,在session范围中添加属性login,其属性值为true ActionContext.getContext().getSession().put("login", "true"); return SUCCESS; }else{ return ERROR; } }
修改登录成功页,将request替换成session,表示取得session属性范围中的login属性值。在登录判断中的登录成功下添加一个链接,用来跳转到showUser.action,代码如下所示。
<%@page contentType="text/html; charset=gb2312"%> <html> <head> <title>登录成功</title> </head> <body> <center> <% if(session.getAttribute("login") ! = null && session.getAttribute("login").equals("true")){ %> <h2>登录成功</h2> <a href="showUser.action">显示用户信息</a> <% } else { %> <jsp:forward page="login.jsp"></jsp:forward> <% } %> </center> </body> </html>
6.6.2 用户信息显示
新建User类,该类用来封装用户信息。为User类添加4个属性,分别为username、password、sex、age,并添加属性的setter和getter方法,代码如下所示。
package net.hncu.vo; public class User { private String username; private String password; private String sex; private int age; public User(String username, String password, String sex, int age) { this.username = username; this.password = password; this.sex = sex; this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
新建业务逻辑组件GetUserInf,用来从数据库中查找数据。这里只是简单模拟,使用新建一个User对象来代替从数据库中取得的用户信息,代码如下所示。
package net.hncu.service; import net.hncu.vo.User; public class GetUserInf { //模拟查找数据库中查找到的数据 private User user = new User("xiaoqiang", "xiaoqiang", "男",20); //返回用户信息 public User getUserInfo() { return user; } }
新建Action类ShowUser,在该Action类中调用业务逻辑组件取得用户信息,然后将用户信息设置到request属性范围中。这里并没有对用户的登录状态进行判断,目的就是希望通过拦截器来实现权限控制,代码如下所示。
package net.hncu.struts2.action; import net.hncu.service.GetUserInf; import net.hncu.vo.User; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; public class ShowUser extends ActionSupport { public String execute() throws Exception { User UserInfo = new GetUserInf().getUserInfo(); ServletActionContext.getRequest().setAttribute("uinfo", UserInfo); return SUCCESS; } }
新建显示页,在该页面中显示所有的用户信息,代码如下所示。
<%@page contentType="text/html; charset=gb2312"%> <html> <head> <title>登录成功</title> </head> <body> <center> <h1>用户信息</h1> <table border="1"> <tr> <td>用户名:</td> <td>${uinfo.username }</td> </tr> <tr> <td>用户密码:</td> <td>${uinfo.password }</td> </tr> <tr> <td>性别:</td> <td>${uinfo.sex }</td> </tr> <tr> <td>年龄:</td> <td>${uinfo.age }</td> </tr> </table> </center> </body> </html>
在“struts.xml”文件中配置Action,并定义处理结果与视图资源之间的关系,代码如下所示。
<! -- struts为配置文件根元素--> <struts> <! -- Action必须放在指定的包名空间中--> <package name="struts2" extends="struts-default"> <! -- 定义login的Action,其实现类为net.hncu.struts2.action.LoginAction--> <action name="login" class="net.hncu.struts2.action.LoginAction"> <! -- 定义处理结果与视图资源之间的关系--> <result name="success">/login_success.jsp</result> <result name="error">/login_failure.jsp</result> <result name="input">login.jsp</result> </action> <! -- 定义showUser的Action,其实现类为net.hncu.struts2.action.ShowUser--> <action name="showUser" class="net.hncu.struts2.action.ShowUser"> <! -- 定义处理结果与视图资源之间的关系--> <result name="success">showUserInfo.jsp</result> </action> </package> <! -- 指定资源文件baseName为messageResource --> <constant name="struts.custom.i18n.resources" value="messageResource"></constant> </struts>
打开用户登录页,输入正确的用户信息,用户名和密码都为“xiaoqiang”,单击“登录”按钮进行登录。在登录成功显示页中显示用户登录成功,还包括一个跳转到显示用户信息的链接,如图6.20所示。
单击“显示用户信息”链接,页面跳转到showUser.action。该Action取得用户信息并跳转到用户信息显示页,如图6.21所示。
图6.20 登录成功显示页
图6.21 用户信息显示页
通过以上步骤成功地为登录案例添加上了用户信息显示功能。
6.6.3 实现权限控制
现在还是有问题,如果不进行登录,直接浏览showUser.action,页面同样经显示用户信息。这时就必须添加用于控制权限的代码。可以在ShowUser这个Action中添加对Session范围中login属性值的判断。如果为空或者不为true,那么就认为是非法用户,页面就跳转回登录页面;如果为true,那么就认为是合法用户,页面跳转到用户信息显示页。
这样做可以实现权限控制,但是如果有多个Action呢?是不是就要为多个Action添加用户权限控制代码?可以创建一个拦截器,通过该拦截器来实现权限控制,代码如下所示。
package net.hncu.interceptor; import java.util.Map; import com.opensymphony.xwork2.Action; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class AuthInterceptor extends AbstractInterceptor { public String intercept(ActionInvocation invocation) throws Exception { //取得ActionContext实例 ActionContext ctx = invocation.getInvocationContext(); //取得session对象 Map session = ctx.getSession(); //取得session中login属性值 String login = (String)session.get("login"); //定义返回结果 String result = null; //如果为合法用户 if(login ! = null && login.equals("true")) { //进行下一步操作 result = invocation.invoke(); } //如果为非法用户 else { //跳转到用户登录页面 result = Action.LOGIN; } return result; } }
6.6.4 配置权限控制拦截器
在“struts.xml”文件中配置该拦截器,并添加ShowUser返回login处理结果对应的视图资源页面,代码如下所示。
<interceptors> <interceptor name="authInter" class="net.hncu.interceptor.AuthInterceptor"></interceptor> </interceptors> <! -- 定义login的Action,其实现类为net.hncu.struts2.action.LoginAction--> <action name="login" class="net.hncu.struts2.action.LoginAction"> <! -- 定义处理结果与视图资源之间的关系--> <result name="success">/login_success.jsp</result> <result name="error">/login_failure.jsp</result> <result name="input">/login.jsp</result> </action> <! -- 定义showUser的Action,其实现类为net.hncu.struts2.action.ShowUser--> <action name="showUser" class="net.hncu.struts2.action.ShowUser"> <! -- 定义处理结果与视图资源之间的关系--> <result name="success">/showUserInfo.jsp</result> <result name="login">/login.jsp</result> <interceptor-ref name="authInter"></interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref> </action>
这时再浏览showUser.action,页面将跳转到用户登录页面,如图6.22所示。
图6.22 用户登录页
从上面的示例中可以看出,拦截器的功能非常强大,而且非常好用。拦截器是无处不在的,Struts 2的大部分功能都是基于它而实现的,至于哪些地方可使用自定义拦截器,这个要靠用户自己慢慢摸索。可以留意那些对Action进行的操作,如果多个Action使用相同的操作,就可以考虑将它放置到拦截器中。