geetest極驗驗證碼使用
一、服務端程式碼
1、去geetest官網註冊並獲取id和key 並引入新建geetestlib工具類
在github中clone出最新Demo專案,快速搭建本地應用:
git clone https://github.com/GeeTeam/gt-java-sdk.git
前往 專案主頁
引用SDK
將SDK中的目錄下的 src/com 目錄複製到你的專案中
import com.geetest.sdk.java.GeetestLib;
public class GeetestLib {
protected final String verName = "3.2.0";// SDK版本編號
protected final String sdkLang = "java";// SD的語言型別
protected final String apiUrl = "http://api.geetest.com"; //極驗驗證API URL
protected final String baseUrl = "api.geetest.com";
protected final String registerUrl = "/register.php"; //register url
protected final String validateUrl = "/validate.php"; //validate url
/**
* 極驗驗證二次驗證表單資料 chllenge
*/
public static final String FN_GEETEST_CHALLENGE = "geetest_challenge";
/**
* 極驗驗證二次驗證表單資料 validate
*/
public static final String FN_GEETEST_VALIDATE = "geetest_validate";
/**
* 極驗驗證二次驗證表單資料 seccode
*/
public static final String FN_GEETEST_SECCODE = "geetest_seccode";
/**
* 公鑰
*/
private String CAPTCHAID = "";
/**
* 私鑰
*/
private String PRIVATEKEY = "";
private String USERID = "";
private String RESPONSESTR = "";
/**
* 除錯開關,是否輸出除錯日誌
*/
public boolean debugCode = true;
/**
* 極驗驗證API服務狀態Session Key
*/
public String gtServerStatusSessionKey = "gt_server_status";
/**
* 帶引數建構函式
*
* @param captchaId
* @param privateKey
*/
public GeetestLib(String captchaId, String privateKey) {
this.CAPTCHAID = captchaId;
this.PRIVATEKEY = privateKey;
}
/**
* 獲取本次驗證初始化返回字串
*
* @return 初始化結果
*/
public String getResponseStr() {
return RESPONSESTR;
}
public String getVersionInfo() {
return verName;
}
/**
* 預處理失敗後的返回格式串
*
* @return
*/
private String getFailPreProcessRes() {
Long rnd1 = Math.round(Math.random() * 100);
Long rnd2 = Math.round(Math.random() * 100);
String md5Str1 = md5Encode(rnd1 + "");
String md5Str2 = md5Encode(rnd2 + "");
String challenge = md5Str1 + md5Str2.substring(0, 2);
return String.format(
"{\"success\":%s,\"gt\":\"%s\",\"challenge\":\"%s\"}", 0,
this.CAPTCHAID, challenge);
}
/**
* 預處理成功後的標準串
*
*/
private String getSuccessPreProcessRes(String challenge) {
gtlog("challenge:" + challenge);
return String.format(
"{\"success\":%s,\"gt\":\"%s\",\"challenge\":\"%s\"}", 1,
this.CAPTCHAID, challenge);
}
/**
* 驗證初始化預處理
*
* @return 1表示初始化成功,0表示初始化失敗
*/
public int preProcess() {
if (registerChallenge() != 1) {
this.RESPONSESTR = this.getFailPreProcessRes();
return 0;
}
return 1;
}
/**
* 驗證初始化預處理
*
* @param userid
* @return 1表示初始化成功,0表示初始化失敗
*/
public int preProcess(String userid){
this.USERID = userid;
return this.preProcess();
}
/**
* 用captchaID進行註冊,更新challenge
*
* @return 1表示註冊成功,0表示註冊失敗
*/
private int registerChallenge() {
try {
String GET_URL = apiUrl + registerUrl+"?gt=" + this.CAPTCHAID;
if (this.USERID != ""){
GET_URL = GET_URL + "&user_id=" + this.USERID;
this.USERID = "";
}
gtlog("GET_URL:" + GET_URL);
String result_str = readContentFromGet(GET_URL);
gtlog("register_result:" + result_str);
if (32 == result_str.length()) {
this.RESPONSESTR = this.getSuccessPreProcessRes(this.md5Encode(result_str + this.PRIVATEKEY));
return 1;
} else {
gtlog("gtServer register challenge failed");
return 0;
}
} catch (Exception e) {
gtlog("exception:register api");
}
return 0;
}
/**
* 傳送請求,獲取伺服器返回結果
*
* @param getURL
* @return 伺服器返回結果
* @throws IOException
*/
private String readContentFromGet(String getURL) throws IOException {
URL getUrl = new URL(getURL);
HttpURLConnection connection = (HttpURLConnection) getUrl
.openConnection();
connection.setConnectTimeout(2000);// 設定連線主機超時(單位:毫秒)
connection.setReadTimeout(2000);// 設定從主機讀取資料超時(單位:毫秒)
// 建立與伺服器的連線,並未傳送資料
connection.connect();
// 傳送資料到伺服器並使用Reader讀取返回的資料
StringBuffer sBuffer = new StringBuffer();
InputStream inStream = null;
byte[] buf = new byte[1024];
inStream = connection.getInputStream();
for (int n; (n = inStream.read(buf)) != -1;) {
sBuffer.append(new String(buf, 0, n, "UTF-8"));
}
inStream.close();
connection.disconnect();// 斷開連線
return sBuffer.toString();
}
/**
* 判斷一個表單物件值是否為空
*
* @param gtObj
* @return
*/
protected boolean objIsEmpty(Object gtObj) {
if (gtObj == null) {
return true;
}
if (gtObj.toString().trim().length() == 0) {
return true;
}
return false;
}
/**
* 檢查客戶端的請求是否合法,三個只要有一個為空,則判斷不合法
*
* @param request
* @return
*/
private boolean resquestIsLegal(String challenge, String validate, String seccode) {
if (objIsEmpty(challenge)) {
return false;
}
if (objIsEmpty(validate)) {
return false;
}
if (objIsEmpty(seccode)) {
return false;
}
return true;
}
/**
* 服務正常的情況下使用的驗證方式,向gt-server進行二次驗證,獲取驗證結果
*
* @param challenge
* @param validate
* @param seccode
* @return 驗證結果,1表示驗證成功0表示驗證失敗
*/
public int enhencedValidateRequest(String challenge, String validate, String seccode) {
if (!resquestIsLegal(challenge, validate, seccode)) {
return 0;
}
gtlog("request legitimate");
String host = baseUrl;
String path = validateUrl;
int port = 80;
String query = String.format("seccode=%s&sdk=%s", seccode,
(this.sdkLang + "_" + this.verName));
String response = "";
if (this.USERID != ""){
query = query + "&user_id=" + this.USERID;
this.USERID = "";
}
gtlog(query);
try {
if (validate.length() <= 0) {
return 0;
}
if (!checkResultByPrivate(challenge, validate)) {
return 0;
}
gtlog("checkResultByPrivate");
response = postValidate(host, path, query, port);
gtlog("response: " + response);
} catch (Exception e) {
e.printStackTrace();
}
gtlog("md5: " + md5Encode(seccode));
if (response.equals(md5Encode(seccode))) {
return 1;
} else {
return 0;
}
}
/**
* 服務正常的情況下使用的驗證方式,向gt-server進行二次驗證,獲取驗證結果
*
* @param challenge
* @param validate
* @param seccode
* @param userid
* @return 驗證結果,1表示驗證成功0表示驗證失敗
*/
public int enhencedValidateRequest(String challenge, String validate, String seccode, String userid) {
this.USERID = userid;
return this.enhencedValidateRequest(challenge, validate, seccode);
}
/**
* failback使用的驗證方式
*
* @param challenge
* @param validate
* @param seccode
* @return 驗證結果,1表示驗證成功0表示驗證失敗
*/
public int failbackValidateRequest(String challenge, String validate, String seccode) {
gtlog("in failback validate");
if (!resquestIsLegal(challenge, validate, seccode)) {
return 0;
}
gtlog("request legitimate");
String[] validateStr = validate.split("_");
String encodeAns = validateStr[0];
String encodeFullBgImgIndex = validateStr[1];
String encodeImgGrpIndex = validateStr[2];
gtlog(String.format(
"encode----challenge:%s--ans:%s,bg_idx:%s,grp_idx:%s",
challenge, encodeAns, encodeFullBgImgIndex, encodeImgGrpIndex));
int decodeAns = decodeResponse(challenge, encodeAns);
int decodeFullBgImgIndex = decodeResponse(challenge, encodeFullBgImgIndex);
int decodeImgGrpIndex = decodeResponse(challenge, encodeImgGrpIndex);
gtlog(String.format("decode----ans:%s,bg_idx:%s,grp_idx:%s", decodeAns,
decodeFullBgImgIndex, decodeImgGrpIndex));
int validateResult = validateFailImage(decodeAns,decodeFullBgImgIndex, decodeImgGrpIndex);
return validateResult;
}
/**
*
* @param ans
* @param full_bg_index
* @param img_grp_index
* @return
*/
private int validateFailImage(int ans, int full_bg_index,
int img_grp_index) {
final int thread = 3;// 容差值
String full_bg_name = md5Encode(full_bg_index + "").substring(0, 9);
String bg_name = md5Encode(img_grp_index + "").substring(10, 19);
String answer_decode = "";
// 通過兩個字串奇數和偶數位拼接產生答案位
for (int i = 0; i < 9; i++) {
if (i % 2 == 0) {
answer_decode += full_bg_name.charAt(i);
} else if (i % 2 == 1) {
answer_decode += bg_name.charAt(i);
} else {
gtlog("exception");
}
}
String x_decode = answer_decode.substring(4, answer_decode.length());
int x_int = Integer.valueOf(x_decode, 16);// 16 to 10
int result = x_int % 200;
if (result < 40) {
result = 40;
}
if (Math.abs(ans - result) <= thread) {
return 1;
} else {
return 0;
}
}
/**
* 解碼隨機引數
*
* @param encodeStr
* @param challenge
* @return
*/
private int decodeResponse(String challenge, String string) {
if (string.length() > 100) {
return 0;
}
int[] shuzi = new int[] { 1, 2, 5, 10, 50 };
String chongfu = "";
HashMap<String, Integer> key = new HashMap<String, Integer>();
int count = 0;
for (int i = 0; i < challenge.length(); i++) {
String item = challenge.charAt(i) + "";
if (chongfu.contains(item) == true) {
continue;
} else {
int value = shuzi[count % 5];
chongfu += item;
count++;
key.put(item, value);
}
}
int res = 0;
for (int j = 0; j < string.length(); j++) {
res += key.get(string.charAt(j) + "");
}
res = res - decodeRandBase(challenge);
return res;
}
/**
* 輸入的兩位的隨機數字,解碼出偏移量
*
* @param randStr
* @return
*/
private int decodeRandBase(String challenge) {
String base = challenge.substring(32, 34);
ArrayList<Integer> tempArray = new ArrayList<Integer>();
for (int i = 0; i < base.length(); i++) {
char tempChar = base.charAt(i);
Integer tempAscii = (int) (tempChar);
Integer result = (tempAscii > 57) ? (tempAscii - 87)
: (tempAscii - 48);
tempArray.add(result);
}
int decodeRes = tempArray.get(0) * 36 + tempArray.get(1);
return decodeRes;
}
/**
* 輸出debug資訊,需要開啟debugCode
*
* @param message
*/
public void gtlog(String message) {
if (debugCode) {
System.out.println("gtlog: " + message);
}
}
protected boolean checkResultByPrivate(String challenge, String validate) {
String encodeStr = md5Encode(PRIVATEKEY + "geetest" + challenge);
return validate.equals(encodeStr);
}
/**
* 貌似不是Post方式,後面重構時修改名字
*
* @param host
* @param path
* @param data
* @param port
* @return
* @throws Exception
*/
protected String postValidate(String host, String path, String data,
int port) throws Exception {
String response = "error";
InetAddress addr = InetAddress.getByName(host);
Socket socket = new Socket(addr, port);
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream(), "UTF8"));
wr.write("POST " + path + " HTTP/1.0\r\n");
wr.write("Host: " + host + "\r\n");
wr.write("Content-Type: application/x-www-form-urlencoded\r\n");
wr.write("Content-Length: " + data.length() + "\r\n");
wr.write("\r\n"); // 以空行作為分割
// 傳送資料
wr.write(data);
wr.flush();
// 讀取返回資訊
BufferedReader rd = new BufferedReader(new InputStreamReader(
socket.getInputStream(), "UTF-8"));
String line;
while ((line = rd.readLine()) != null) {
response = line;
}
wr.close();
rd.close();
socket.close();
return response;
}
/**
* md5 加密
*
* @time 2014年7月10日 下午3:30:01
* @param plainText
* @return
*/
private String md5Encode(String plainText) {
String re_md5 = new String();
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(plainText.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
re_md5 = buf.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return re_md5;
}
}
/********************************/
新建一個controller 並使用ajax實時校驗
* 從geetest網站獲取的KEY和ID
*/
private static final String CAPTCHA_ID = “自己申請”;
private static final String CAPTCHA_KEY = “自己去官網免費申請”;
/**
* @Title: verifyCaptchaCode
* @Description: TODO(圖片驗證碼二次驗證返回結果)
* @param @param request
* @param @param response
* @param @throws IOException 引數
* @return void 返回型別
* @throws
*/
@RequestMapping(value = "/verifyCaptchaCode")
@ResponseBody
public void verifyCaptchaCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
GeetestLib gtSdk = new GeetestLib(CAPTCHA_ID, CAPTCHA_KEY);
String challenge = request.getParameter(GeetestLib.FN_GEETEST_CHALLENGE);
String validate = request.getParameter(GeetestLib.FN_GEETEST_VALIDATE);
String seccode = request.getParameter(GeetestLib.FN_GEETEST_SECCODE);
NotifyMsg msg = new NotifyMsg();
/****************************/
// 從session中獲取gt-server狀態
int gt_server_status_code = (Integer) request.getSession().getAttribute(gtSdk.gtServerStatusSessionKey);
// 從session中獲取userid
String userid = SessionUtil.getAttr(request, "USERID").toString();
int gtResult = 0;
if (gt_server_status_code == 1) {
// gt-server正常,向gt-server進行二次驗證
gtResult = gtSdk.enhencedValidateRequest(challenge, validate, seccode, userid);
log.info("gtResult:" + gtResult);
} else {
// gt-server非正常情況下,進行failback模式驗證
log.info("failback:use your own server captcha validate");
gtResult = gtSdk.failbackValidateRequest(challenge, validate, seccode);
log.info("gtResult:" + gtResult);
}
if (gtResult != 1) {
// 驗證成功
msg.init(1, "驗證失敗");
}
MethodUtil.toJsonMsg(response, msg.getKey(), msg.getMsg());
}
/**
* @Title: startValidate
* @Description: TODO(圖片驗證碼初始驗證返回輸出驗證碼)
* @param @param request
* @param @param response 引數
* @return void 返回型別
* @throws
*/
@RequestMapping(value = "/startCaptcha")
@ResponseBody
public void startValidate(HttpServletRequest request, HttpServletResponse response) {
GeetestLib gtSdk = new GeetestLib(CAPTCHA_ID, CAPTCHA_KEY);
String resStr = "{}";
// 自定義userid
String userid = "CaptchaCode";
// 進行驗證預處理
int gtServerStatus = gtSdk.preProcess(userid);
// 將伺服器狀態設定到session中
request.getSession().setAttribute(gtSdk.gtServerStatusSessionKey, gtServerStatus);
// 將userid設定到session中
SessionUtil.setAttr(request, "USERID", userid);
resStr = gtSdk.getResponseStr();
MethodUtil.toJsonPrint(response, resStr);
}
二、客戶端程式碼
注意,如果您的網站使用https,則只需要將引入極驗庫的地方換成https協議即可,不需要更改其它地方。例如SDK中的demo要使用https,則將以下程式碼:
更換成以下程式碼即可:
這裡的話我們新建一個login.jsp 具體程式碼省略
//新建一個顯示驗證碼DIV
//提交按鈕並將BIND事件繫結到按鈕中來
下面是JS指令碼放在最末尾
原文:https://blog.csdn.net/u010434697/article/details/51861205