安全優化---數學公式圖形驗證碼
秒殺介面地址的隱藏可以防止惡意使用者通過頻繁呼叫介面來請求的操作,但是無法防止利用按鍵精靈或者機器人頻繁點選按鈕來刷介面的操作。
而且,對於高併發下,在某一個時間段,也就是剛剛開始秒殺的那一瞬間,迎來的併發量是最大的,如何做到併發量分流也是一種減少資料庫以及系統壓力的措施。
數學圖形驗證碼:
1.防機器人刷介面
2.分散使用者請求
思路:點選秒殺按鈕之前,需要輸入驗證碼驗證
1.生成驗證碼的介面
2.在獲取秒殺路徑的同時,驗證驗證碼
步驟
1.改造前端頁面,驗證碼只在秒殺進行中的時候存在,<img id > 用id控制屬性,一開始驗證碼和輸入框是不可見的(未開始的時候)。當倒計時結束,開始秒殺的時候,使其可見並且src="/getVerifyCode" 請求後端,生成圖片。
/**加入秒殺數學驗證碼 功能
* 1.一開始圖形驗證碼和輸入框都是隱藏的
* 2.當秒殺進行的時候,顯示驗證碼和輸入框
* */
$("#verifyCodeImg").attr("src", "/miaosha/verifyCode?goodsId="+$("#goodsId").val());//訪問驗證碼介面
$("#verifyCodeImg").show();
$("#verifyCode").show();
2.傳參為goodsid。根據使用者id和goodsid 生成數學公式驗證碼,
圖片是利用BufferedImage 類生成,生成大小長80 寬50畫素的灰底圖片。 利用是Graphics 類作為畫筆,swing),
/** * 秒殺驗證碼 介面 */ @RequestMapping(value = "/verifyCode", method = RequestMethod.GET)// 只允許POST 提交 @ResponseBody public Result<Integer> Miaosha(HttpServletResponse response,MiaoshaUser user, Model model, @RequestParam("goodsId") long goodsId) { /*判斷是否登陸*/ if (user == null) { return Result.error(CodeMsg.SESSION_ERROR); } BufferedImage image = miaoshaService.createVerifyImg(user, goodsId); try { OutputStream out = response.getOutputStream();//用response的輸出流輸出這個圖片 ImageIO.write(image,"JPEG",out);//圖片寫入輸出流 out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); return Result.error(CodeMsg.SERVER_ERROR);//失敗就失敗 } return null; } public BufferedImage createVerifyImg(MiaoshaUser user, long goodsId) { if (user == null || goodsId <= 0){ return null; } /**生成驗證碼圖片的程式碼 * */ int width = 80;//定義 影象寬高 int height = 32; //create the image BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);//生成一個記憶體中影象物件 ,寬高,型別 Graphics g = image.getGraphics();//獲取影象的graphics 物件,利用它就可以畫圖 // set the background color g.setColor(new Color(0xDCDCDC));//設定背景顏色 g.fillRect(0, 0, width, height);//背景顏色的填充 // draw the border g.setColor(Color.black);//黑色的邊框 g.drawRect(0, 0, width - 1, height - 1); // create a random instance to generate the codes Random rdm = new Random();//隨機數 // make some confusion for (int i = 0; i < 50; i++) { int x = rdm.nextInt(width); int y = rdm.nextInt(height); g.drawOval(x, y, 0, 0);//在圖片上生成 50個 干擾的點 } // generate a random code String verifyCode = generateVerifyCode(rdm); //生成我們的驗證碼 g.setColor(new Color(0, 100, 0));//驗證碼的顏色 g.setFont(new Font("Candara", Font.BOLD, 24));//字型 g.drawString(verifyCode, 8, 24);//將 這個String 型別驗證碼 寫在 圖片上 g.dispose();//關掉這個畫筆 //把驗證碼存到redis中 int rnd = calc(verifyCode);//計算這個數學公式驗證碼的值 //將這個計算的值放入 redis中等待 對比 redisService.set(MiaoshaKey.getMiaoshaVerifyCode, user.getId()+","+goodsId, rnd); //輸出圖片 return image; }
對於數學公式的生成,專門有一個方法:
生成3 個 0到9之間的數,然後在生成一個字元陣列,用於存放 + - * (加減乘)三個數學運算子,
然後對其進行拼接,一個字+運算子+字+運算子+字 生成一個字串
private static char [] chars = {'+','-','*'};
//生成這個驗證碼
private String generateVerifyCode(Random rdm) {
int num1 = rdm.nextInt(10);
int num2 = rdm.nextInt(10);
int num3 = rdm.nextInt(10);//生成三個隨機數(10以內)
char charOne = chars[rdm.nextInt(3)];//生成兩個運算子// n 表示 10以內,不包括10的整數 0-9
char charTwo = chars[rdm.nextInt(3)];
//對其進行拼接,獲得數學表示式
String verifyCode = ""+num1+charOne+num2+charTwo+num3;
return verifyCode;
}
然後利用drawString 方法將這個字串寫在生成的圖片上。
同時,我們利用scriptEngine類,呼叫javascript的eval() 方法,計算這個公式的值,將這個值,以驗證vericode 的key存入快取,
然後將這個驗證碼圖片 用輸出流 作為response輸出。
3.前端得到這個驗證碼圖片,顯示該驗證碼,然後在使用者需要在輸入驗證碼 將這個驗證碼作為引數,將這個verifyCode 作為引數一同傳輸給後端(這個操作是在/getPath 這個介面中進行校驗),後端接收的時候,接收到引數,再秒殺之前,進行驗證碼比對,快取中取出該驗證碼進行校驗。如果不通過,不生成秒殺介面地址。
注意:Bug eval() 計算得到的是double 值,但我們需要的int 值,需要強轉
以及一個重新整理驗證碼的操作,
在 圖片上定義個oncilck 操作,點選後在請求 獲取圖片驗證碼的介面,但是瀏覽器會有快取,要加上timestamp 這個引數,瀏覽器才會送請求中取資料,而不是讀直接快取(對於相同url ,瀏覽器會選擇讀快取,而不是請求)