零基础学Struts
上QQ阅读APP看书,第一时间看更新

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">
                    <! -- 定义loginAction,其实现类为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>
                    <! -- 定义showUserAction,其实现类为net.hncu.struts2.action.ShowUser-->
                    <action name="showUser" class="net.hncu.struts2.action.ShowUser">
                          <! -- 定义处理结果与视图资源之间的关系-->
                          <result name="success">showUserInfo.jsp</result>
                    </action>
              </package>
              <! -- 指定资源文件baseNamemessageResource -->
              <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();
                    //取得sessionlogin属性值
                    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>
        <! -- 定义loginAction,其实现类为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>
        <! -- 定义showUserAction,其实现类为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使用相同的操作,就可以考虑将它放置到拦截器中。