二维码生成和校验
在struts2.0的学习中,如果想在此基础上实现二维码的生成和验证,首先得熟悉二维码的生成和校验。其中最主要的还是得熟悉struts运行机制,处理流程。
这里先熟悉struts2.0的运行机制:
这里描绘了从前台数据提交到后台处理数据,在到数据返回的主要流程。其中关键的是拦截器和过滤器的使用和区别。
过滤器,是在javaweb中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者struts的action前统一设置字符集,或者去除掉一些非法字符
拦截器,是在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
拦截器与过滤器的区别 :
- 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
- 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
- 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
- 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
- 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
执行顺序 :过滤前 - 拦截前 -Action处理 - 拦截后 -过滤后。个人认为过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。
因此下面是实现项目所需的主要编码web.xml:这里的‘ /* ’作用是对这个项目中的所有请求都要经历此过滤器过滤。
StrutsPrepareAndExecuteFilter是自2.1.3开始就替代了FilterDispatcher,那么StrutsPrepareAndExecuteFilter可以把他拆分成StrutsPrepareFilter和StrutsExecuteFilter,可以在这两个过滤器之间加上我们自己的过滤器.!而FilterDispatcher做不到。
<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>
struts.xml编程如下,红色标记的是 为不可变的。这里定义图片的输出格式是stream,inputName是传递过来的图片生成字符数组流:
<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>
<struts> <package name="example" extends="struts-default" > <action name="loginer" class="com.action.UserAction"> <interceptor-ref name="defaultStack" /> <interceptor-ref name="token" /> <result name="input">/jsp/login.jsp</result> <result name="success">/jsp/success.jsp</result> <result name="invalid.token">/jsp/login.jsp</result> <result name="fail">/jsp/fail.jsp</result> </action> <action name="imagevalid" class="com.action.ImageValidate"> <result name="success" type="stream"> <param name="contentType">image/jpeg</param> <param name="inputName">inputStream</param> </result> </action> </package> </struts>
图片验证码生成代码,这里是二维码的生成和设置到 session中供校验使用,当然这里使用的是gui的画图方法来绘制二维码,根据下列步骤可以生成随机的二维码图片字符流:package com.action; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Random; import javax.imageio.ImageIO; import javax.imageio.stream.ImageOutputStream; import javax.servlet.http.HttpSession; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class ImageValidate extends ActionSupport { private ByteArrayInputStream inputStream; public ByteArrayInputStream getInputStream() { return inputStream; } public void setInputStream(ByteArrayInputStream inputStream) { this.inputStream = inputStream; } public String execute() throws Exception { // 在内存中创建图象 int width = 60, height = 20; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // 获取图形上下文 Graphics g = image.getGraphics(); // 生成随机类 Random random = new Random(); // 设定背景色 g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); // 设定字体 g.setFont(new Font("Times New Roman", Font.PLAIN, 18)); // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到 g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } // 取随机产生的认证码(4位数字) String sRand = "";//1234 for (int i = 0; i < 4; i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; // 将认证码显示到图象中 g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); //调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成 g.drawString(rand, 13 * i + 6, 16); } ActionContext.getContext().getSession().put("sessionValidatImage", sRand); // 将认证码存入SESSION //request.getSession().setAttribute("validateStr", sRand); // 图象生效 g.dispose(); ByteArrayOutputStream outputStream=new ByteArrayOutputStream(); try { ImageOutputStream imageOut; imageOut=ImageIO.createImageOutputStream(outputStream); ImageIO.write(image, "JPEG", imageOut); } catch (Exception e) { e.printStackTrace(); } ByteArrayInputStream input=new ByteArrayInputStream(outputStream.toByteArray()); this.setInputStream(input); return "success"; } private Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) fc = 255; if (bc > 255) bc = 255; int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b);//三原色 (三基色) 255,255,255 } }
登陆和校验:package com.action; import java.util.HashMap; import java.util.Map; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport { private String userName; private String password; private String validation; public String getValidation() { return validation; } public void setValidation(String validation) { this.validation = validation; } 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 void validate() { if (userName==null || "".equals(userName)) { this.addFieldError("userName", "请输入用户名!"); } if (password==null || "".equals(password)) { this.addFieldError("password", "请输入密码!"); } } public String execute() throws Exception { System.out.println("执行到此!"); String catchValidation=(String)ActionContext.getContext().getSession().get("sessionValidatImage"); System.out.println(catchValidation); if ("abc".equals(userName)&&"123".equals(password)&& validation.equals(catchValidation)) { return "success"; }else { return "fail"; } } }
前台页面生成登陆login.jsp代码如下,二位码的验证如果在前台进行操作时将出现不同步的问题故还是放在后台校验:<body> <s:form action="loginer" method="post" > <s:textfield name="userName" label="用户名" /><br> <s:textfield name="validation" label="验证码" id="imageTxt" οnchange='doValidate()'/><br> <s:password name="password" label="密码"/><br> <s:token></s:token> <s:property value="#session.sessionValidatImage" /> <img alt="tupian" src="<%=basePath%>/jsp/imagevalid.action"/> <s:submit value="提交" ></s:submit> </s:form> <s:debug></s:debug> </body>