極驗有一款行為驗證的插件拆魏,其實就是個驗證碼插件气筋,包括滑塊和點選的驗證方式缝驳,這里記錄一下如何接入基于 jQuery + SpringMVC 的 Web 端項目
更多精彩
- 更多技術(shù)博客连锯,請移步 IT人才終生實訓(xùn)與職業(yè)進階平臺 - 實訓(xùn)在線
寫在前面的話
- 接入這個行為驗證是因為自己網(wǎng)站的注冊驗證碼短信接口被人攻擊了归苍,所以就打算在發(fā)送注冊驗證碼之前加一個驗證
- 網(wǎng)上搜索一番看到極驗的這個插件,發(fā)現(xiàn)還不少公司用的运怖,像經(jīng)常去的 B 站霜医,和原來偶爾會逛得數(shù)字尾巴都有用
- 這個插件本身效果也還蠻不錯的,所以就選擇接入
- 插件本身提供免費試用驳规,只是有幾個限制,免費版只提供滑塊驗證署海,以及每小時最高 500 次的交互量吗购,咱們這小網(wǎng)站感覺夠用了
- 不過昨天晚上市場那邊正好在做一個推廣活動,突然給我打電話說用戶反饋注冊時候無法通過行為驗證
- 我上去一看砸狞,行為驗證報錯了捻勉,問了下他們多少人在注冊,那邊說就 200 個人刀森,我想的是這應(yīng)該也達不到 500 次的峰值
- 這得一個人驗證錯 3 次踱启,才能達到 600 次,所以我就體所應(yīng)當(dāng)?shù)囊詾槭呛蠖斯蛄搜械祝鋵崨]有
- 后來去極驗的后端一看埠偿,竟然交互量達到了 636 次,超過了峰值榜晦,所以接口返回為空了冠蒋,汗顏
- 既然市場這么給力,那我今天就聯(lián)系一下極驗的客服問下升級套餐唄乾胶,結(jié)果問完之后我就開始著手下線這個功能了
- 他們的價格差不多是這樣的抖剿,也不復(fù)雜,默認套餐價格區(qū)間是 6-20w 一年 识窿,6w 是支持每小時 1w 次的峰值交互量
- 我說 1w 都多了斩郎,我目前只需要 2000 次,那邊給了一個特惠套餐喻频,3w 一年缩宜,每小時 5000 次的峰值交互量
- 我順便問了一下,這個 3w 一年只僅僅這個插件的授權(quán)使用半抱,還是他們幾個產(chǎn)品都能用(他們貌似還有其他幾個產(chǎn)品脓恕,雖然并不感興趣)
- 那邊說只是驗證碼,這個我還真的比較汗顏窿侈,覺得他們這相當(dāng)于一個接口賣 3w 一年了炼幔,不過其實應(yīng)該是我酸,科科
- 總之這里只是記錄一下我的接入流程史简,因為接下來我準(zhǔn)備刪掉這個功能換成別的實現(xiàn)方式了
- 我現(xiàn)在還在想昨天 200 個人的注冊真的可以達到 600+ 次的交互量嗎乃秀,在有人引導(dǎo)注冊的前提下肛著,該不會是 …. ?
相關(guān)網(wǎng)址
后端對接實現(xiàn)
下載并接入集成包
- 在前面提供的 geetest-java 中可以下載到插件后端環(huán)境的集成包跺讯,也可以通過
git clone https://github.com/GeeTeam/gt3-java-sdk.git
直接下載 - 下載下來其實是 Java 語言的本地文檔枢贿、DEMO 、SDK 刀脏、以及和后端語言配對的前端依賴 JS
- 我覺得這里后端如果能提供一個在線的 Maven 或 Gradle 依賴地址其實更好
- 將 /src/sdk/GeetestLib.java 和 src/demo/demo1/GeetestConfig.java 引入到自己項目即可
- 打開 GeetestConfig.java 局荚,將其中的
geetest_id
和geetest_key
修改成自己的即可- 這兩個參數(shù)需要到他們 極驗后臺登錄 獲取,登錄后選擇行為驗證
- 之后還需要新增驗證愈污,填寫基本信息后即可拿到這兩個值耀态,如下圖
- 左下角的 ID 對應(yīng)
geetest_id
,KEY 對應(yīng)geetest_key
編寫驗證碼服務(wù)
- 后端驗證的邏輯分為兩步驗證暂雹,第一步初始化首装,其實就是相當(dāng)于生成驗證碼,第二步才是用戶發(fā)起驗證請求
- 文檔中分別使用了
doGet
演示初始化杭跪,doPost
演示接收驗證請求 - 實際項目中當(dāng)然不會使用 Servlet 來做這些事情仙逻,這里使用的是 SpringMVC
- 兩個操作可以放在同一個 Service 中,例如 CaptchaServiceImpl.java
封裝請求參數(shù)
- 單獨封裝起來是因為兩步驗證都會用到
- 請求參數(shù)其實就是每個用戶的唯一標(biāo)識涧尿,文檔中使用的是 用戶 ID 系奉、終端類型 以及 IP 地址
- 我這里因為是未登錄狀態(tài)下的驗證,所以拿不到用戶 ID 姑廉,所以直接使用的 IP 地址
private HashMap<String, String> getParams() {
String clientIp = URIUtil.getClientIp(request);
HashMap<String, String> params = Maps.newHashMap();
params.put("user_id", clientIp);
params.put("client_type", "web");
params.put("ip_address", clientIp);
return params;
}
生成驗證碼喜最,對應(yīng)第一步初始化
-
getParams()
獲取參數(shù)就是上一個函數(shù)
public String generateCaptcha() {
// 初始化極驗服務(wù)
GeetestLib lib = new GeetestLib(GeetestConfig.getGeetest_id(), GeetestConfig.getGeetest_key(), GeetestConfig.isnewfailback());
// 驗證預(yù)處理
int status = lib.preProcess(getParams());
// 將服務(wù)狀態(tài)存放到 Session 中,在第二步驗證時會用到
request.getSession().setAttribute(lib.gtServerStatusSessionKey, status);
// 返回生成字串
return lib.getResponseStr();
}
接收用戶驗證請求庄蹋,并返回存儲處理結(jié)果
-
request.getParameter()
可以直接使用瞬内,是因為我的 Service 集成了一個通用的父類,在里面直接注入了HttpServletRequest
- 通過
request
獲取的參數(shù)都不是自定義的限书,由極驗前端的 gt.js 內(nèi)部提供 -
saveMessage()
是驗證成功后將當(dāng)前參與驗證的手機號和結(jié)果存儲到 Redis 中虫蝶,時效 5 分鐘- 通過行為驗證,正式發(fā)起驗證碼請求時倦西,會先通過請求的手機號去 Redis 中獲取行為驗證結(jié)果
- 存在且成功才會發(fā)驗證碼能真,否則會提示進行行為驗證
-
TSharkException()
是自定義的異常處理,由 Controller 捕獲后拋到前端彈出提示框告知用戶
public void checkCaptcha(String mobile) {
// 初始化極驗服務(wù)
GeetestLib lib = new GeetestLib(GeetestConfig.getGeetest_id(), GeetestConfig.getGeetest_key(), GeetestConfig.isnewfailback());
// 接收前端參數(shù)扰柠,由前端 JS 內(nèi)部封裝處理
String challenge = request.getParameter(GeetestLib.fn_geetest_challenge);
String validate = request.getParameter(GeetestLib.fn_geetest_validate);
String seccode = request.getParameter(GeetestLib.fn_geetest_seccode);
// 取出第一步初始化驗證時存儲的服務(wù)狀態(tài)
int status = (Integer) request.getSession().getAttribute(lib.gtServerStatusSessionKey);
int result = 0;
if (status == 1) {
// 服務(wù)器在線
result = lib.enhencedValidateRequest(challenge, validate, seccode, getParams());
} else {
// 服務(wù)器離線
result = lib.failbackValidateRequest(challenge, validate, seccode);
}
if (result == 1) {
// 保存驗證信息
saveMessage(mobile, new MessageBean(mobile, result));
} else {
throw new TSharkException("行為驗證失敗粉铐,請檢查使用環(huán)境");
}
}
控制層定義請求
- 因為是傳統(tǒng)項目(非前后分離),所以使用的注解依舊是以下方式
- 請求相同卤档,分別使用
RequestMethod.GET
和RequestMethod.POST
來區(qū)分第一步驗證和第二步驗證
- 請求相同卤档,分別使用
-
SimpleActionHandler
是自定義封裝的 Service 異常處理類-
request
在繼承的AbstractBaseController
自定義父類中統(tǒng)一聲明
-
-
ResponseData
是自定義封裝的返回結(jié)果類 - 后端的服務(wù)就完成了蝙泼,實現(xiàn)起來還是非常簡潔的,點個贊
@Controller
@RequestMapping("/captcha")
public class CaptchaController extends AbstractBaseController {
@Autowired
private CaptchaServiceImpl captchaService;
@RequestMapping(value = "", method = RequestMethod.GET)
@ResponseBody
public ResponseData init() {
return new SimpleActionHandler(request) {
@Override
protected void doHandle(ResponseData responseData) throws Exception {
responseData.setData(captchaService.generateCaptcha());
}
}.handle();
}
@RequestMapping(value = "", method = RequestMethod.POST)
@ResponseBody
public ResponseData check(@RequestParam final String mobile) {
return new SimpleActionHandler(request) {
@Override
protected void doHandle(ResponseData responseData) throws Exception {
captchaService.checkCaptcha(mobile);
}
}.handle();
}
}
前端對接實現(xiàn)
前端依賴 JS 在哪里
- 前端依賴的 JS 在 geetest-WEB-front 接口文檔中是找不到的劝枣,因為不同服務(wù)端語言對應(yīng)的 JS 不一樣汤踏,所以將其放置在了后端集成包中
-
具體位置如下圖所示
-
官方也給了相關(guān)提示织鲸,如下圖
引入前端依賴 JS
- 將 JS 加入到項目并引入即可
- 因為我的項目里用到了 jQuery ,所以以下代碼中先引入了
<script type="text/javascript" src="${ctx }/assets/plugins/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="${ctx }/assets/plugins/geetest/gt.js"></script>
準(zhǔn)備一個 HTML 區(qū)域用于顯示行為驗證組件
- 具體樣式因人而異溪胶,總之就是提供一個
div
用于放置初始化好的驗證組件即可
<section>
<label class="label">行為驗證 <span class="text-danger pull-right"></span></label>
<label class="input captcha">
<div class="captcha-tip">行為驗證加載中</div>
</label>
</section>
初始化行為驗證插件
- 插件的初始化函數(shù)是
initGeetest()
搂擦,來自上文中的 gt.js - 但其實在初始化之前需要先發(fā)送一個異步請求,去調(diào)用服務(wù)端的初始化接口哗脖,因為初始化函數(shù)中的值瀑踢,是由服務(wù)端初始化接口返回的
- 初始化成功后在回調(diào)函數(shù)中通過
captchaObj.appendTo()
將行為驗證組件加入指定的頁面容器中 - 因為組件初始化需要先訪問后端接口,考慮到網(wǎng)絡(luò)延遲才避,所以一般會先給個提示丘损,比如顯示一句話 “行為驗證加載中”
- 如果要在初始化結(jié)束后隱藏,在
captchaObj.onReady()
中調(diào)用即可工扎,這些官方文檔都有介紹
- 如果要在初始化結(jié)束后隱藏,在
// 發(fā)起第一步驗證
$.ajax({
url: '${ctx}/captcha',
type: 'GET',
dataType: 'json',
success: function (data) {
var result = JSON.parse(data.data)
// 初始化行為驗證
initGeetest({
gt: result.gt,
challenge: result.challenge,
new_captcha: result.new_captcha,
offline: !result.success,
product: 'float',
width: '100%'
}, function (captchaObj) {
// 顯示行為驗證操作元素
captchaObj.appendTo($memberRegisterPanel.find('.captcha'))
captchaObj.onReady(function () {
getCurrentPanel().find('.captcha-tip').hide()
})
// .. 執(zhí)行后續(xù)操作
})
}
})
用戶前端驗證成功后,發(fā)起后端核驗請求
- 這一步的操作會在用戶進行滑塊驗證并成功后被調(diào)用
- 如果滑塊都沒有弄對衔蹲,則不會發(fā)起后端核驗
- 這里發(fā)起的驗證實際上就是去調(diào)用后端的第二步驗證接口
-
mobile
是傳入的手機號 - 其他的三個參數(shù)都是極驗插件內(nèi)部返回的驗證結(jié)果肢娘,用于發(fā)送到后端進行二次核驗
-
- 在請求的回調(diào)中可以判斷核驗結(jié)果,并執(zhí)行后續(xù)操作
captchaObj.onSuccess(function () {
var result = captchaObj.getValidate()
// 發(fā)起之前可以先進行一些自定義驗證舆驶,比如手機號是否填寫橱健,格式是否錯誤
// 如果自定義驗證沒有通過,可以使用 captchaObj.reset() 重置驗證組件狀態(tài)
// 發(fā)起二次驗證
$.ajax({
url: '${ctx}/captcha',
type: 'POST',
data: {
mobile: mobileVal,
geetest_challenge: result.geetest_challenge,
geetest_validate: result.geetest_validate,
geetest_seccode: result.geetest_seccode
},
dataType: 'json',
success: function (data) {
if (!data.success) {
// 驗證成功后的具體操作
}
}
})
})
一句話總結(jié)
- 極驗的這個行為驗證組件接入的方式比較簡單沙廉,實現(xiàn)的效果也蠻不錯的拘荡,但我還是認為就一個驗證碼功能一年收費 6w 起,太貴了撬陵,搞不起搞不起珊皿,科科