行為驗證碼通過用戶的操作來完成驗證,常見的行為驗證碼有拖動式和點觸式,拖動式驗證就是根據(jù)圖片顯示,將指定的圖形拖動到指定位置完成驗證。而點觸式驗證碼就是通過鼠標點擊出示例中出現(xiàn)的圖形完成驗證。
今天推薦一款非常優(yōu)秀的行為驗證碼AJ-Captcha(項目地址https://gitee.com/anji-plus/captcha),這個項目包含了滑動拼圖和文字點選兩種類型的驗證碼,除了嵌入式交互,還提供了彈出式交互的方式,完全不影響原UI布局。
AJ-Captcha的驗證流程如下:
如果你是Maven開發(fā)者,使用起來非常方便,項目的維護人員已經將依賴推送至中央倉庫。只需要引入依賴就完成了90%的工作量。接下來只需要在登錄接口中進行二次驗證就可以了。
項目集成了包括html、vue、flutter、uni-app、Android Kotlin、IOS、php等多種前端語言,可以輕松將AJ_Captcha集成到項目中。
接下來我們以Spring Boot+html為例看看如何快速集成AJ_Captcha完成行為驗證碼的交互流程。
<dependency>
<groupId>com.anji-plus</groupId>
<artifactId>spring-boot-starter-captcha</artifactId>
<version>1.2.9</version>
</dependency>
AJ_Captcha默認實現(xiàn)了驗證碼生成和驗證接口,驗證碼生成接口的默認請求地址是/captcha/get,驗證接口的默認請求地址為/captcha/check。也就是說完成以上步驟,就可以提供給前端獲取和驗證驗證碼的接口了。如果你還想讓你的驗證碼生成的個性一點,可以配置以下屬性:
# 滑動驗證,底圖路徑,不配置將使用默認圖片
# 支持全路徑
# 支持項目路徑,以classpath:開頭,取resource目錄下路徑,例:classpath:images/jigsaw
aj.captcha.jigsaw=classpath:images/jigsaw
# 滑動驗證,底圖路徑,不配置將使用默認圖片
# 支持全路徑
# 支持項目路徑,以classpath:開頭,取resource目錄下路徑,例:classpath:images/pic-click
aj.captcha.pic-click=classpath:images/pic-click
# 對于分布式部署的應用,我們建議應用自己實現(xiàn)CaptchaCacheService,比如用Redis或者memcache,
# 參考CaptchaCacheServiceRedisImpl.java
# 如果應用是單點的,也沒有使用redis,那默認使用內存。
# 內存緩存只適合單節(jié)點部署的應用,否則驗證碼生產與驗證在節(jié)點之間信息不同步,導致失敗。
# !!! 注意啦,如果應用有使用spring-boot-starter-data-redis,
# 請打開CaptchaCacheServiceRedisImpl.java注釋。
# redis -----> SPI: 在resources目錄新建META-INF.services文件夾(兩層),參考當前服務resources。
# 緩存local/redis...
aj.captcha.cache-type=local
# local緩存的閾值,達到這個值,清除緩存
#aj.captcha.cache-number=1000
# local定時清除過期緩存(單位秒),設置為0代表不執(zhí)行
#aj.captcha.timing-clear=180
#spring.redis.host=10.108.11.46
#spring.redis.port=6379
#spring.redis.password=
#spring.redis.database=2
#spring.redis.timeout=6000
# 驗證碼類型default兩種都實例化。
aj.captcha.type=default
# 漢字統(tǒng)一使用Unicode,保證程序通過@value讀取到是中文,可通過這個在線轉換;yml格式不需要轉換
# https://tool.chinaz.com/tools/unicode.aspx 中文轉Unicode
# 右下角水印文字(我的水印)
aj.captcha.water-mark=\u6211\u7684\u6c34\u5370
# 右下角水印字體(不配置時,默認使用文泉驛正黑)
# 由于宋體等涉及到版權,我們jar中內置了開源字體【文泉驛正黑】
# 方式一:直接配置OS層的現(xiàn)有的字體名稱,比如:宋體
# 方式二:自定義特定字體,請將字體放到工程resources下fonts文件夾,支持ttf\ttc\otf字體
# aj.captcha.water-font=WenQuanZhengHei.ttf
# 點選文字驗證碼的文字字體(文泉驛正黑)
# aj.captcha.font-type=WenQuanZhengHei.ttf
# 校驗滑動拼圖允許誤差偏移量(默認5像素)
aj.captcha.slip-offset=5
# aes加密坐標開啟或者禁用(true|false)
aj.captcha.aes-status=true
# 滑動干擾項(0/1/2)
aj.captcha.interference-options=2
aj.captcha.history-data-clear-enable=false
# 接口請求次數(shù)一分鐘限制是否開啟 true|false
aj.captcha.req-frequency-limit-enable=false
# 驗證失敗5次,get接口鎖定
aj.captcha.req-get-lock-limit=5
# 驗證失敗后,鎖定時間間隔,s
aj.captcha.req-get-lock-seconds=360
# get接口一分鐘內請求數(shù)限制
aj.captcha.req-get-minute-limit=30
# check接口一分鐘內請求數(shù)限制
aj.captcha.req-check-minute-limit=60
# verify接口一分鐘內請求數(shù)限制
aj.captcha.req-verify-minute-limit=60
<script>
$('#content').slideVerify({
baseUrl:'http://localhost:8080/', //服務器請求地址, 默認地址為安吉服務器;
containerId:'btn',//pop模式 必填 被點擊之后出現(xiàn)行為驗證碼的元素id
mode:'pop', //展示模式
imgSize : { //圖片的大小對象,有默認值{ width: '310px',height: '155px'},可省略
width: '400px',
height: '200px',
},
barSize:{ //下方滑塊的大小對象,有默認值{ width: '310px',height: '50px'},可省略
width: '400px',
height: '40px',
},
beforeCheck:function(){ //檢驗參數(shù)合法性的函數(shù) mode ="pop"有效
let flag = true;
//實現(xiàn): 參數(shù)合法性的判斷邏輯, 返回一個boolean值
return flag
},
ready : function() {}, //加載完畢的回調
success : function(params) { //成功的回調
// params為返回的二次驗證參數(shù) 需要在接下來的實現(xiàn)邏輯回傳服務器
例如: login($.extend({}, params))
},
error : function() {} //失敗的回調
});
</script>
驗證碼驗證成功之后,會返回一個用于二次驗證的串碼。
客戶端登錄的時候攜帶驗證成功后返回的串碼,在登錄接口中進行二次驗證,驗證流程完畢。
@Autowired
private CaptchaService captchaService;
/**
* 頁面獲取token
* 大屏數(shù)據(jù)校驗
* @param user
* @return
*/
@PostMapping("getWebToken")
public ResultBean getWebToken(@RequestBody LoginUser user,String captchaVerification){
ResultBean resultBean = new ResultBean();
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(captchaVerification);
ResponseModel responseModel = captchaService.verification(captchaVO);
if(!responseModel.isSuccess()){
resultBean.fillCode(0,responseModel.getRepMsg());
return resultBean;
}
// 驗證通過后,繼續(xù)登錄流程
}
今天的內容就介紹到這里了,趁這個機會,試著使用這款高顏值的行為驗證碼來替換項目中的圖形驗證碼吧。
,工具類記錄
原鏈接:https://blog.csdn.net/qq_37651267/article/details/99305573,非常感謝原鏈接博主
此驗證碼的實現(xiàn)沒有用到太多的插件,話不多說直接上代碼,大家拿過去就可以用。
1.驗證碼類
package com.youyou.login.util.validatecode;
import lombok.Data;
/**
* 驗證碼類
*/
@Data
public class VerifyCode {
private String code;
private byte[] imgBytes;
private long expireTime;
}
2.驗證碼生成接口
package com.youyou.login.util.validatecode;
import java.io.IOException;
import java.io.OutputStream;
/**
* 驗證碼生成接口
*/
public interface IVerifyCodeGen {
/**
* 生成驗證碼并返回code,將圖片寫的os中
*
* @param width
* @param height
* @param os
* @return
* @throws IOException
*/
String generate(int width, int height, OutputStream os) throws IOException;
/**
* 生成驗證碼對象
*
* @param width
* @param height
* @return
* @throws IOException
*/
VerifyCode generate(int width, int height) throws IOException;
}
3.驗證碼生成實現(xiàn)類
package com.youyou.login.util.validatecode;
import com.youyou.util.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
/**
* 驗證碼實現(xiàn)類
*/
public class SimpleCharVerifyCodeGenImpl implements IVerifyCodeGen {
private static final Logger logger = LoggerFactory.getLogger(SimpleCharVerifyCodeGenImpl.class);
private static final String[] FONT_TYPES = { "\u5b8b\u4f53", "\u65b0\u5b8b\u4f53", "\u9ed1\u4f53", "\u6977\u4f53", "\u96b6\u4e66" };
private static final int VALICATE_CODE_LENGTH = 4;
/**
* 設置背景顏色及大小,干擾線
*
* @param graphics
* @param width
* @param height
*/
private static void fillBackground(Graphics graphics, int width, int height) {
// 填充背景
graphics.setColor(Color.WHITE);
//設置矩形坐標x y 為0
graphics.fillRect(0, 0, width, height);
// 加入干擾線條
for (int i = 0; i < 8; i++) {
//設置隨機顏色算法參數(shù)
graphics.setColor(RandomUtils.randomColor(40, 150));
Random random = new Random();
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
graphics.drawLine(x, y, x1, y1);
}
}
/**
* 生成隨機字符
*
* @param width
* @param height
* @param os
* @return
* @throws IOException
*/
@Override
public String generate(int width, int height, OutputStream os) throws IOException {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
fillBackground(graphics, width, height);
String randomStr = RandomUtils.randomString(VALICATE_CODE_LENGTH);
createCharacter(graphics, randomStr);
graphics.dispose();
//設置JPEG格式
ImageIO.write(image, "JPEG", os);
return randomStr;
}
/**
* 驗證碼生成
*
* @param width
* @param height
* @return
*/
@Override
public VerifyCode generate(int width, int height) {
VerifyCode verifyCode = null;
try (
//將流的初始化放到這里就不需要手動關閉流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
) {
String code = generate(width, height, baos);
verifyCode = new VerifyCode();
verifyCode.setCode(code);
verifyCode.setImgBytes(baos.toByteArray());
} catch (IOException e) {
logger.error(e.getMessage(), e);
verifyCode = null;
}
return verifyCode;
}
/**
* 設置字符顏色大小
*
* @param g
* @param randomStr
*/
private void createCharacter(Graphics g, String randomStr) {
char[] charArray = randomStr.toCharArray();
for (int i = 0; i < charArray.length; i++) {
//設置RGB顏色算法參數(shù)
g.setColor(new Color(50 + RandomUtils.nextInt(100),
50 + RandomUtils.nextInt(100), 50 + RandomUtils.nextInt(100)));
//設置字體大小,類型
g.setFont(new Font(FONT_TYPES[RandomUtils.nextInt(FONT_TYPES.length)], Font.BOLD, 26));
//設置x y 坐標
g.drawString(String.valueOf(charArray[i]), 15 * i + 5, 19 + RandomUtils.nextInt(8));
}
}
}
4.工具類
package com.youyou.util;
import java.awt.*;
import java.util.Random;
public class RandomUtils extends org.apache.commons.lang3.RandomUtils {
private static final char[] CODE_SEQ = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7', '8', '9' };
private static final char[] NUMBER_ARRAY = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
private static Random random = new Random();
public static String randomString(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(String.valueOf(CODE_SEQ[random.nextInt(CODE_SEQ.length)]));
}
return sb.toString();
}
public static String randomNumberString(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(String.valueOf(NUMBER_ARRAY[random.nextInt(NUMBER_ARRAY.length)]));
}
return sb.toString();
}
public static Color randomColor(int fc, int bc) {
int f = fc;
int b = bc;
Random random = new Random();
if (f > 255) {
f = 255;
}
if (b > 255) {
b = 255;
}
return new Color(f + random.nextInt(b - f), f + random.nextInt(b - f), f + random.nextInt(b - f));
}
public static int nextInt(int bound) {
return random.nextInt(bound);
}
}
經過以上代碼,我們的驗證碼生成功能基本上已經實現(xiàn)了,現(xiàn)在還需要一個controller來調用它。
@ApiOperation(value = "驗證碼")
@GetMapping("/verifyCode")
public void verifyCode(HttpServletRequest request, HttpServletResponse response) {
IVerifyCodeGen iVerifyCodeGen = new SimpleCharVerifyCodeGenImpl();
try {
//設置長寬
VerifyCode verifyCode = iVerifyCodeGen.generate(80, 28);
String code = verifyCode.getCode();
LOGGER.info(code);
//將VerifyCode綁定session
request.getSession().setAttribute("VerifyCode", code);
//設置響應頭
response.setHeader("Pragma", "no-cache");
//設置響應頭
response.setHeader("Cache-Control", "no-cache");
//在代理服務器端防止緩沖
response.setDateHeader("Expires", 0);
//設置響應內容類型
response.setContentType("image/jpeg");
response.getOutputStream().write(verifyCode.getImgBytes());
response.getOutputStream().flush();
} catch (IOException e) {
LOGGER.info("", e);
}
}
搞定!后臺編寫到此結束了。那么又會有博友說了:“說好的實現(xiàn)效果呢?”
好吧,那么我們繼續(xù)前端的代碼編寫。
前端代碼:
<html>
<body>
<div>
<input id="code" placeholder="驗證碼" type="text" class=""
style="width:170px">
<!-- 驗證碼 顯示 -->
<img οnclick="javascript:getvCode()" id="verifyimg" style="margin-left: 20px;"/>
</div>
<script type="text/javascript">
getvCode();
/**
* 獲取驗證碼
* 將驗證碼寫到login.html頁面的id = verifyimg 的地方
*/
function getvCode() {
document.getElementById("verifyimg").src = timestamp("http://127.0.0.1:81/verifyCode");
}
//為url添加時間戳
function timestamp(url) {
var getTimestamp = new Date().getTime();
if (url.indexOf("?") > -1) {
url = url + "×tamp=" + getTimestamp
} else {
url = url + "?timestamp=" + getTimestamp
}
return url;
};
</script>
</body>
</html>
可以實現(xiàn)點擊圖片更換驗證碼。
實現(xiàn)效果:
?
當然文章開頭的截圖是我系統(tǒng)中的截圖,需要大家自己去根據(jù)自己的情況去開發(fā)前端了。
驗證碼是用來防止惡意破解密碼、防止機器登錄的,我們常見的驗證碼形式五花八門,有圖片驗證碼、短信驗證碼、語音驗證碼、拼圖驗證、找圖驗證、點字驗證還有智能檢測,今天我來跟大家分享的是普通的圖片驗證碼。
12306驗證碼
網(wǎng)友惡搞的驗證碼
QQ驗證碼
ThinkPHP內置了Verify類,生成驗證碼僅需兩步:
生成驗證碼代碼
然后在index.html中使用img標簽顯示驗證碼:
<img src="{:U('Index/verifyCode')}">
最終輸出結果如下:
驗證碼顯示結果
上面的用法是通過默認配置來輸出的驗證碼,5位字母、添加混淆線、添加雜點、字體隨機、顏色隨機,如果這種驗證碼已經可以滿足你的要求,直接使用即可,如果無法滿足要求,可以通過下面的配置來自定義驗證碼:
圖片來自ThinkPHP官方文檔
默認情況下,驗證碼的字體是隨機使用 ThinkPHP/Library/Think/Verify/ttfs/
目錄下面的字體文件,我們可以指定驗證碼的字體,例如:
$Verify = new \Think\Verify();
// 驗證碼字體使用 ThinkPHP/Library/Think/Verify/ttfs/5.ttf
$Verify->fontttf = '5.ttf';
$Verify->entry();
$Verify = new \Think\Verify();
// 開啟驗證碼背景圖片功能 隨機使用 ThinkPHP/Library/Think/Verify/bgs 目錄下面的圖片
$Verify->useImgBg = true;
$Verify->entry();
如果要使用中文驗證碼,可以設置:
$Verify = new \Think\Verify();
$Verify->useZh = true;
$Verify->entry();
3.2.1版本以上,我們可以指定驗證碼的字符,通過重新設置codeSet參數(shù)即可,例如設置純數(shù)字驗證碼:
$Verify = new \Think\Verify();
$Verify->codeSet = '0123456789';
$Verify->entry();
部分中文字符驗證碼:
$Verify = new \Think\Verify();
$Verify->useZh = true;
$Verify->zhSet = '們以我到他會作時要動國產的一是工就年階義發(fā)成部民可出能方進在了不和有大這';
$Verify->entry();
設置了驗證碼,我們應該怎么檢測用戶輸入是否正確呢?只需要使用Verify類下的check方法即可:
$verify = new \Think\Verify();
return $verify->check($code);
如果在同一個頁面中包含多個驗證碼,上面的方法會導致后生成的驗證碼覆蓋掉先生成的SESSION,導致驗證失敗,此時我們需要在生成驗證碼代碼中使用id標識,如下:
$Verify->entry(1);//第一個驗證碼
$Verify->entry(2);//第二個驗證碼
在檢測的時候也一樣
$verify->check($code,1);//檢測第一個驗證碼
$verify->check($code,2);//檢測第二個驗證碼
ThinkPHP內置的驗證碼類基本上可以滿足我們的需求,而且使用簡單,方便快捷,免去我們自己寫驗證碼生成的麻煩。不過圖片驗證碼已經逐步的被替代,如果你感興趣,可以前往極驗驗證(http://www.geetest.com/)了解更神奇的驗證方式(不過是收費的)。
*請認真填寫需求信息,我們會在24小時內與您取得聯(lián)系。