Struts2基础-4-2 -struts拦截器实现权限控制案例+ 模型驱动处理请求参数 + Action方法动态调用
1.新建项目,添加jar包到WEB-INF目录下的lib文件夹,并添加到builde path里面
整体目录结构如下
2.新建web.xml,添加struts2核心过滤器,和默认首页
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 5 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 6 <welcome-file-list> 7 <welcome-file>main.jsp</welcome-file> 8 </welcome-file-list> 9 10 <filter> 11 <filter-name>StrutsPrepareAndExecuteFilter</filter-name> 12 <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 13 </filter> 14 15 <filter-mapping> 16 <filter-name>StrutsPrepareAndExecuteFilter</filter-name> 17 <url-pattern>/*</url-pattern> 18 </filter-mapping> 19 20 </web-app>
3. 新建一个User Javabean ,编写loginAction
在 cn.test.domain 包下建User类
1 package cn.test.domain; 2 3 public class User { 4 private String username; 5 private String password; 6 7 public String getUsername() { 8 return username; 9 } 10 11 public void setUsername(String username) { 12 this.username = username; 13 } 14 15 public String getPassword() { 16 return password; 17 } 18 19 public void setPassword(String password) { 20 this.password = password; 21 } 22 23 @Override 24 public String toString() { 25 return "User [password=" + password + ", username=" + username + "]"; 26 } 27 28 }
在 cn.test.action 包下 建 LoginActoin,模拟用户登陆,判断登陆用户是否为指定用户,不是,就返回逻辑视图名" input ",跳转到login.jsp 去让用户登陆
1 package cn.test.action; 2 3 import cn.test.domain.User; 4 5 import com.opensymphony.xwork2.ActionContext; 6 import com.opensymphony.xwork2.ActionSupport; 7 import com.opensymphony.xwork2.ModelDriven; 8 9 public class LoginActoin extends ActionSupport implements ModelDriven<User>{ 10 11 private static final long serialVersionUID = 1L; 12 private User user = new User(); //必须要有该Javabean的实例 13 @Override 14 public User getModel() { 15 return user; 16 } 17 @Override 18 public String execute() throws Exception { 19 // 获取ActionContext 20 ActionContext context = ActionContext.getContext(); 21 if("admin".equals(user.getUsername())&& "123".equals(user.getPassword())){ 22 //把用户存储在session中 23 context.getSession().put("user", user); 24 return SUCCESS; 25 }else { 26 context.put("msg", "用户名密码不正确"); 27 return INPUT; 28 } 29 30 } 31 32 33 }
注:这里使用了struts2的模型驱动来获取请求参数值。
在 Struts2中, Action处理请求参数还有另外一种方式,叫做模型驱动( Modeldriven)。过实现 Modeldriven接口来接收请求参数, Action类必须实现 Modeldriven接口,引入JavaBea的实例 ,并且重写 get Model()方法,这个方法返回的就是 Action所使用的数据模型对象。
模型驱动方式通过 Javabean模型进行数据传递。只要是普通的 Javabean,就可以充当模型部分。采用这种方式, Javabean所封装的属性与表单的属性一一对应, Javabean将成为数据传递的载体。使用模型驱动方式, Action类通过get*()的方法来获取模型,其中 * 代表具体的模型对象,代码如上LoginActoin 中背景颜色部分所示。
使用模型驱动时,其登录页面 userlogin.jsp也要做相应调整。使用ModelDriver的方式,一个Action只能对应一个Model,因此不需要添加 user前缀,页面上的username对应到这个Model的username属性。属性驱动的方法和模刑驱动的方法各有优缺点,实际开发中,应根据不同情况来选择使用。
4.在 cn.test.action 包下创建 BookAction ,写增删改查四个方法
1 package cn.test.action; 2 3 import com.opensymphony.xwork2.ActionSupport; 4 5 public class BookAction extends ActionSupport { 6 7 private static final long serialVersionUID = 1L; 8 public String add(){ 9 System.out.println("book add"); 10 return SUCCESS; 11 } 12 public String del(){ 13 System.out.println("book del"); 14 return SUCCESS; 15 } 16 public String update(){ 17 System.out.println("book update"); 18 return SUCCESS; 19 } 20 public String find(){ 21 System.out.println("book find"); 22 return SUCCESS; 23 } 24 25 }
5.在 cn.test.interceptor 包下创建自定义拦截器类PrivilegeInterceptor ,根据session中有无指定用户,进行权限控制
1 package cn.test.interceptor; 2 3 import com.opensymphony.xwork2.Action; 4 import com.opensymphony.xwork2.ActionContext; 5 import com.opensymphony.xwork2.ActionInvocation; 6 import com.opensymphony.xwork2.interceptor.AbstractInterceptor; 7 8 /** 9 * 自定义拦截器类,获取session中有无登陆用户,完成页面跳转的权限控制 10 * 11 * @author Administrator 12 * 13 */ 14 public class PrivilegeInterceptor extends AbstractInterceptor { 15 16 private static final long serialVersionUID = 1L; 17 18 @Override 19 public String intercept(ActionInvocation invocation) throws Exception { 20 // 获取actionContext 21 ActionContext actionContext = invocation.getInvocationContext(); 22 // 获取session中的用户 23 Object user = actionContext.getSession().get("user"); 24 if (null != user) { 25 return invocation.invoke(); // 继续向下执行 26 } else { 27 actionContext.put("msg", "对不起,您还未登陆!"); 28 return Action.LOGIN; // 返回逻辑视图名"login",由对应的result配置,跳转到登陆页面 29 } 30 } 31 32 }
6.在webcontent目录下创建4个视图页面:主页main.jsp ,登陆页login.jsp ,操作成功页success.jsp , 404错误页error.jsp
(1)main.jsp 定义增删改查4个链接,请求路径中action的命名按照 book_* 的方式命名,方便struts里面配置Action的动态调用
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <% 3 String path = request.getContextPath(); 4 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 5 %> 6 7 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 8 <html> 9 <head> 10 <base href="<%=basePath%>"> 11 <title>main.jsp</title> 12 </head> 13 14 <body> 15 <a href="<%=basePath%>book_add">bookAdd</a> <br> 16 <a href="<%=basePath%>book_del">bookDel</a> <br> 17 <a href="<%=basePath%>book_update">bookUpdate</a> <br> 18 <a href="<%=basePath%>book_find">bookFind</a> <br> 19 </body> 20 </html>
(2),登陆页login.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <% 3 String path = request.getContextPath(); 4 String basePath = request.getScheme() + "://" 5 + request.getServerName() + ":" + request.getServerPort() 6 + path + "/"; 7 %> 8 9 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 10 <html> 11 <head> 12 <base href="<%=basePath%>"> 13 <title>登陆</title> 14 </head> 15 <body> 16 ${requestScope.msg } 17 <br> 18 <form action="<%=basePath%>login.action" method="post"> 19 <table> 20 <tr> 21 <td><label style="text-align:right;">用户名:</label></td> 22 <td><input type="text" name="username"/></td> 23 </tr> 24 <tr> 25 <td><label style="text-align:right;">密码:</label></td> 26 <td><input type="password" name="password"/></td> 27 </tr> 28 <tr > 29 <td colspan="2" align="right"><input type="submit" value="登陆"/></td> 30 </tr> 31 </table> 32 </form> 33 </body> 34 </html>
(3)操作成功页success.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 3 <html> 4 <head> 5 <title>success.jsp</title> 6 </head> 7 8 <body> 9 用户${user.username }操作成功! 10 </body> 11 </html>
(4) 404错误页error.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 3 <html> 4 <head> 5 <title>error page</title> 6 </head> 7 8 <body> 9 您访问的页面不存在<br> 10 </body> 11 </html>
7.配置struts。xml,声明自定义拦截器,拦截器栈以及对book操作的action,和处理404错误的action
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 <struts> 6 7 <package name="intercepter" namespace="/" extends="struts-default"> 8 <!-- 声明拦截器 --> 9 <interceptors> 10 <interceptor name="privilege" class="cn.test.interceptor.PrivilegeInterceptor"></interceptor> <!--自定义拦截器--> 11 <!-- 自定义拦截器栈,放入自定义的拦截器和struts的默认拦截器 --> 12 <interceptor-stack name="myStack"> 13 <interceptor-ref name="privilege"></interceptor-ref> 14 <interceptor-ref name="defaultStack"></interceptor-ref> 15 </interceptor-stack> 16 </interceptors> 17 18 <!-- 默认的 Action配置要放在拦截器配置的后面 --> 19 <default-action-ref name="defaultAction"></default-action-ref> 20 21 <!-- 用户登陆操作 action里面不写方法名,默认之心execute()方法!!!--> 22 <action name="login" class="cn.test.action.LoginActoin" > 23 <result>/main.jsp</result> 24 <result name="input">/login.jsp</result> 25 </action> 26 <!-- book操作action --> 27 <action name="book_*" class="cn.test.action.BookAction" method="{1}"> 28 <result name="success">/success.jsp</result> 29 <result name="login">/login.jsp</result> 30 <!-- 在action中使用自定义拦截器,对book操作的actoin进行权限控制 31 放在哪个action,就对那个action进行权限控制的拦截--> 32 <interceptor-ref name="myStack"></interceptor-ref> 33 </action> 34 35 <!-- 注意,package元素里面的子元素是有顺序要求的,如果写错位置,启动时会报错,一般把action写在最后面 --> 36 <action name="defaultAction"> 37 <result>/error.jsp</result> 38 </action> 39 </package> 40 41 </struts>
book操作action类中只返回了一个success作为result 的name属性值, 下面action中配置的 <result name="login">/login.jsp</result> ,这个result是拦截器用的,当拦截器检测到用户没有登陆时,就返回“login”这个逻辑视图名;
关于动态调用,action 的method={1},表示 method属性值取 action的name属性的值里边的第一个*的值。如果请求里是book_add 那么method 就是add,则执行Action中的add方法。
<!-- book操作action --> <action name="book_*" class="cn.test.action.BookAction" method="{1}"> <result name="success">/success.jsp</result> <result name="login">/login.jsp</result> <interceptor-ref name="myStack"></interceptor-ref> <!--调用拦截器栈 --> </action>
8. 部署并测试
(1)访问http://localhost:8080/strutsstu4-2/ 自动跳转到main.jsp
(2)随便点击一个链接,比如update ,提示还未登陆,红框里面的提示消息是 PrivilegeInterceptor 拦截器类中返回的 ,说明拦截器工作了。
(3)输入错误用户名密码测试,提示用户名密码不正确,同时,页面跳转到了登陆页,可以看到浏览器中的地址成了login.action,
(4)输入admin 123登陆
可以在main.jsp中加入 当前登陆用户:${username } <br/> ,效果会看的更明显
点击bookFind,可以看到浏览器地址发生变化,请求book_find这个action
(5) 请求一个不存在的actoin:
遇到的问题:添加默认的 Action配置时,给放在了拦截器配置的前面,导致启动时报错,提示信息如下:
Caused by: 元素类型为 "package" 的内容必须匹配 "(result-types?,interceptors?,default-interceptor-ref?,default-action-ref?,default-class-ref?,global-results?,global-exception-mappings?,action*)"。 - file:/D:/Tomcat/apache-tomcat-6.0.30/webapps/strutsstu4-2/WEB-INF/classes/struts.xml:42:12
其实括号里面的红字部分就已经提示了package中各个元素要遵从的顺序,可以看到,default-action-ref是在default-interceptor-ref?的后面,也就是说默认Action应该配置在拦截器后面
示例代码下载地址 https://github.com/liuch0228/Struts2SSH.git