您好,登錄后才能下訂單哦!
為了攔截大部分請求,秒殺案例前端引入了驗證碼。淘寶上很多人吐槽,等輸入完秒殺活動結束了,對,結束了...... 當然了,驗證碼的真正作用是,有效攔截刷單操作,讓羊毛黨空手而歸。
那么到底什么是驗證碼呢?驗證碼作為一種人機識別手段,其終極目的,就是區(qū)分正常人和機器的操作。我們常見的互聯(lián)網(wǎng)注冊、登錄、發(fā)帖、領優(yōu)惠券、投票等等應用場景,都有被機器刷造成各類損失的風險。
目前常見的驗證碼形式多為圖片驗證碼,即數(shù)字、字母、文字、圖片物體等形式的傳統(tǒng)字符驗證碼。這類驗證碼看似簡單易操作,但實際用戶體驗較差(參見12306網(wǎng)站),且隨著OCR技術和打碼平臺的利用,圖片比較容易被破解,被破解之后就形同虛設。
這里我們使用騰訊的智能人機安全驗證碼,告別傳統(tǒng)驗證碼的單點防御,十道安全柵欄打造立體全面的安全驗證,將黑產(chǎn)拒之門外。
下面我們來瞅瞅驗證碼輕松解決了那些場景安全問題:
申請地址:https://007.qq.com/product.html
在線體驗:https://007.qq.com/online.html
只要一個QQ就可以免費申請,對于一般的企業(yè)OA系統(tǒng)或者個人博客網(wǎng)站,驗證碼免費套餐足夠了已經(jīng),具備以下特點:
2000次/小時的安全防護,一般很少達到如此效果,當然了即時超出閾值,頂多也就是多個廣告而已。
快讀接入:https://007.qq.com/quick-start.html
接入與幫助提供了多種客戶端和服務端的接入案例,這里我們使用我們秒殺案例中最熟悉的Java語言來接入。
引入JS:
<script src="https://ssl.captcha.qq.com/TCaptcha.js"></script>
頁面元素:
<!--點擊此元素會自動激活驗證碼,不一定是button,其他標簽也可以-->
<!--id : 元素的id(必須)-->
<!--data-appid : AppID(必須)-->
<!--data-cbfn : 回調(diào)函數(shù)名(必須)-->
<!--data-biz-state : 業(yè)務自定義透傳參數(shù)(可選)-->
<button id="TencentCaptcha"
data-appid="*********"
data-cbfn="callback">驗證</button>
JS回調(diào):
<script type="text/javascript">
window.callback = function(res){
console.log(res)
// res(未通過驗證)= {ret: 1, ticket: null}
// res(驗證成功) = {ret: 0, ticket: "String", randstr: "String"}
if(res.ret === 0){
startSeckill(res)
}
}
//后臺驗證ticket,并進入秒殺隊列
function startSeckill(res){
$.ajax({
url : "startSeckill",
type : 'post',
data : {'ticket' : res.ticket,'randstr':res.randstr},
success : function(result) {
//驗證是否通過,提示用戶
}
});
}
</script>
@Api(tags = "秒殺商品")
@RestController
@RequestMapping("/seckillPage")
public class SeckillPageController {
@Autowired
private ActiveMQSender activeMQSender;
//自定義工具類
@Autowired
private HttpClient httpClient;
//這里自行配置參數(shù)
@Value("${qq.captcha.url}")
private String url;
@Value("${qq.captcha.aid}")
private String aid;
@Value("${qq.captcha.AppSecretKey}")
private String appSecretKey;
@RequestMapping("/startSeckill")
public Result startSeckill(String ticket,String randstr,HttpServletRequest request) {
HttpMethod method =HttpMethod.POST;
MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();
params.add("aid", aid);
params.add("AppSecretKey", appSecretKey);
params.add("Ticket", ticket);
params.add("Randstr", randstr);
params.add("UserIP", IPUtils.getIpAddr(request));
String msg = httpClient.client(url,method,params);
/**
* response: 1:驗證成功,0:驗證失敗,100:AppSecretKey參數(shù)校驗錯誤[required]
* evil_level:[0,100],惡意等級[optional]
* err_msg:驗證錯誤信息[optional]
*/
//{"response":"1","evil_level":"0","err_msg":"OK"}
JSONObject json = JSONObject.parseObject(msg);
String response = (String) json.get("response");
if("1".equals(response)){
//進入隊列、假數(shù)據(jù)而已
Destination destination = new ActiveMQQueue("seckill.queue");
activeMQSender.sendChannelMess(destination,1000+";"+1);
return Result.ok();
}else{
return Result.error("驗證失敗");
}
}
}
自定義請求工具類 HttpClient:
@Service
public class HttpClient {
public String client(String url, HttpMethod method, MultiValueMap<String, String> params){
RestTemplate client = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// 請勿輕易改變此提交方式,大部分的情況下,提交方式都是表單提交
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(params, headers);
// 執(zhí)行HTTP請求
ResponseEntity<String> response = client.exchange(url, HttpMethod.POST, requestEntity, String.class);
return response.getBody();
}
}
獲取IP地址工具類 IPUtils :
/**
* IP地址
*/
public class IPUtils {
private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
/**
* 獲取IP地址
* 使用Nginx等反向代理軟件, 則不能通過request.getRemoteAddr()獲取IP地址
* 如果使用了多級反向代理的話,X-Forwarded-For的值并不止一個,而是一串IP地址,X-Forwarded-For中第一個非unknown的有效IP字符串,則為真實IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
logger.error("IPUtils ERROR ", e);
}
// 使用代理,則獲取第一個IP地址
if (StringUtils.isEmpty(ip) && ip.length() > 15) {
if (ip.indexOf(",") > 0) {
ip = ip.substring(0, ip.indexOf(","));
}
}
return ip;
}
}
啟動項目訪問:http://localhost:8080/seckill/1000.shtml
在系統(tǒng)登錄的時候,我們需要先校驗用戶名以及密碼,然后調(diào)用驗證碼操作,這里就需要我們定制接入了。
<!-- 項目中使用了Vue -->
<div class="log_btn" @click="login" >登錄</div>
login: function () {
//這里校驗用戶名以及密碼
// 直接生成一個驗證碼對象
var captcha = new TencentCaptcha('2001344788', function(res) {
if(res.ret === 0){//回調(diào)成功
var data = {'username':username,'password':password,'ticket':res.ticket,'randstr':res.randstr}
$.ajax({
type: "POST",
url: "sys/loginCaptcha",
data: data,
dataType: "json",
success: function(result){
//校驗是否成功
}
});
}
});
captcha.show(); // 顯示驗證碼
},
騰訊后臺還提供了簡單實用的數(shù)據(jù)監(jiān)控,如下:
總體來說,系統(tǒng)接入人機驗證碼還是很方便的,并沒有技術難點,難點已經(jīng)被提供商封裝,我們只需要簡單的調(diào)用即可。
秒殺案例:https://gitee.com/52itstyle/spring-boot-seckill
演示案例(點擊生成按鈕):http://jichou.52itstyle.com
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。