圖形驗證碼包含兩部分:圖片和文字驗證碼碍讯。在JSP時代淆两,圖形驗證碼生成和驗證是通過Session機制來實現(xiàn)的:后端生成圖片和文字驗證碼锯玛,并將文字驗證碼放在session中网棍,前端填寫驗證碼后提交到后臺,通過與session中的驗證碼比較來實現(xiàn)驗證历等。
在前后端分離的項目中讨惩,登錄使用的是Token驗證,而不是Session寒屯。后臺必須保證當前用戶輸入的驗證碼是用戶開始請求頁面時候的驗證碼荐捻,必須保證驗證碼的唯一性。
那么怎樣來實現(xiàn)呢寡夹,思路就是:
為每個驗證碼code分配一個主鍵codeId处面。前端請求驗證碼code時,將codeId在前端生成并發(fā)送給后端菩掏;后端生成code及圖形驗證碼后魂角,并與codeId關(guān)聯(lián)保存;前端填寫好code后智绸,將code和codeId一并提交到后端野揪,后端對code和codeId進行比較,完成驗證传于。
下面貼上代碼囱挑。
1、前端請求驗證碼
<div class="captcha-img">
<input type="hidden" name="r" id="r" value="">
<img id="captchaPic" src="#" onclick="getVerifiCode()">
</div>
//生成圖片驗證碼
function getVerifiCode() {
var r = Math.random();//codeId沼溜,隨機數(shù)
$("#r").val(r);
$("#captchaPic").prop('src',commonObj.hostUrl + '/imgCode?r=' + r);
}
在頁面放了一個隱藏input用來保存codeId平挑,請求時將codeId帶上
2、后端生成驗證碼
//生成驗證碼
@GetMapping("/imgCode")
public void creaeCode(HttpServletRequest request, HttpServletResponse response){
ImageCode imagecode=new ImageCode();//圖形驗證碼工具類
BufferedImage img=imagecode.getImage();
try {
String codeId = request.getParameter("r");//從前端傳codeId
String code = imagecode.getValidateCode();//驗證碼
service.saveImgCode(codeId, code);//后端保存驗證碼code和codeId
response.setContentType("image/jpeg");
imagecode.saveImage(img, response.getOutputStream());
} catch (IOException e) {
logger.error("生成圖片驗證碼異常", e);
}
}
附上圖形驗證碼生成類
public class ImageCode {
int w=79;//寬度
int h=30;//高度
private String validateCode = "";//驗證碼
Random r=new Random();
public BufferedImage CreateImage(){//生成圖片的方法
BufferedImage img=new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);//內(nèi)存中創(chuàng)建一張圖片
Graphics gps=img.getGraphics();//獲取當前圖片的畫筆
gps.setColor(new Color(240,240,240));//設(shè)置畫筆
gps.fillRect(0, 0, w, h);//填充一個與圖片一樣大小的矩形(其實就是為了設(shè)置背景顏色)
return img;
}
public BufferedImage getImage(){//得到圖片的方法
BufferedImage img=CreateImage();
Graphics gps=img.getGraphics();//獲取當前圖片的畫筆
StringBuilder sb = new StringBuilder();
//開始畫東西
for(int i=0;i<4;i++){
String ch=this.getContent();
sb.append(ch);
gps.setColor(this.getColor());
gps.setFont(this.getFont());
gps.drawString(ch, w/4*i, h-5);//寬度讓其不滿圖片
}
drawLine(img);
validateCode = sb.toString();
return img;
}
public void saveImage(BufferedImage img,OutputStream out) throws IOException {
//這里為了測試將生成的圖片放入f盤,在實際的項目開發(fā)中是需要將生成的圖片寫入客戶端的:
ImageIO.write(img, "JPEG", out);//response.getOutputStream()
//ImageIO.write(img, "JPEG", new FileOutputStream("F:\\a.jpg"));//保存到硬盤
}
//在圖片中插入字母和十個數(shù)字
String str="abcdefghijklmnupqrstuvwxyzABCDEFGHIJKLMNUPQRSTUVWZYZ1234567890";
public String getContent(){
int index=r.nextInt(str.length());
return str.charAt(index)+"";
}
String[] font={"宋體","華文楷體","華文隸書","黑體","華文新魏"};//字體
int[] fontSize={24,25,26,27,28};//字號大小
int[] fontStyle={0,1,2,3};//字體樣式
public Font getFont(){
int index1=r.nextInt(font.length);
String name=font[index1];
int style=r.nextInt(4);
int index2=r.nextInt(fontSize.length);
int size=fontSize[index2];
return new Font(name,style,size);
}
public Color getColor(){//得到不同的顏色
int R=r.nextInt(256);//取值范圍是0-255
int G=r.nextInt(256);
int B=r.nextInt(256);
return new Color(R,G,B);
}
public void drawLine(BufferedImage img){//畫干擾線
Graphics2D gs=(Graphics2D) img.getGraphics();
gs.setColor(Color.BLACK);
gs.setStroke(new BasicStroke(1.0F));//設(shè)置線的寬度
for(int i=0;i<5;i++){//橫坐標不能超過寬度通熄,縱坐標不能超過高度
int x1=r.nextInt(w);
int y1=r.nextInt(h);
int x2=r.nextInt(w);
int y2=r.nextInt(h);
gs.drawLine(x1, y1, x2, y2);
}
}
public String getValidateCode(){
return this.validateCode;
}
}
3唆涝、登陸時提交驗證碼
登錄界面:
提交登錄代碼:
//執(zhí)行登錄
var regData = {
accName:data.username,
accPwd:hex_md5(data.password),
code:data.captcha,
codeId:$("#r").val()
};
//提交數(shù)據(jù)到后臺
commonObj.ajaxData("/api/account/login", "post", JSON.stringify(regData), function (res) {
//登錄成功
var data = res.data;
window.location.href='index.html';
});
4、后臺校驗驗證碼
public Object doLogin(Map<String, Object> data) throws Exception {
String accName = (String) data.get("accName");
String accPwd = (String) data.get("accPwd");
String code = (String) data.get("code");//驗證碼
String codeId = (String) data.get("codeId");//驗證碼id
Assert.hasText(accName, "賬戶名不能為空");
Assert.hasText(accPwd, "密碼不能為空");
//驗證碼驗證
Assert.hasText(codeId, "驗證碼錯誤");
PhoneCode phoneCode = codeService.validImgCode(codeId, code);
...
//執(zhí)行登錄過程
//todo
}