二維碼生成和校驗
在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是傳遞過來的圖片生成字符數組流:
<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>