一 產品介紹
騰訊云官方介紹鏈接
騰訊云新一代行為驗證碼(Captcha),基于十道安全防護策略,為網頁、App、小程序開發者打造立體、全面的人機驗證。在保護注冊登錄、活動秒殺、點贊發帖、數據保護等各大場景下業務安全的同時,提供更精細化的用戶體驗。
驗證碼服務可以幫助您解決以下業務安全問題:
- 登錄注冊:有效防止撞庫攻擊、阻止注冊機批量注冊小號。
- 活動秒殺:有效攔截刷單操作,防止自動機刷取福利券。
- 點贊發帖:有效解決廣告屠版、惡意灌水、刷票問題。
- 數據保護:有效防止自動機、爬蟲盜取網頁內容和數據。
操作簡單,部分示例:
二 大致流程
這個流程圖可以點擊鏈接看到??驗證碼 快速入門_騰訊云
大致流程就是:
- 前端請求appid
- 拿到appid后使用騰訊云提供的方法,可以加載出來各種類型的驗證碼(驗證碼的類型,看appid申請的是什么樣的)
- 用戶完成認證后,會得到返回一系列參數,如randstr,ticket
- 前端拿著randstr,ticket請求后端接口,驗證這個驗證碼是否OK,后端返回true或者false
三 騰訊云申請流程
1. 在騰訊云官網搜索驗證碼
2. 領取7天免費的,沒用過驗證碼功能的第一次進去會看到領取的地方
3. 前往控制臺
4. 記錄下來生成的密鑰
5 記錄下來自己的賬號密鑰
四 后端代碼編寫
1. 總的demo結構
2. pom
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 騰訊云 驗證碼 --><dependency><groupId>com.tencentcloudapi</groupId><artifactId>tencentcloud-sdk-java</artifactId><version>3.1.1297</version></dependency><!-- Lombok for reducing boilerplate code --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- Validation --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
3. Yaml
server:port: 8080spring:application:name: tcaptcha-demo# 日志配置logging:level:org.example: DEBUGcom.tencentcloudapi: INFOpattern:console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"# 騰訊云驗證碼配置
tencent:captcha:# 騰訊云API密鑰IDsecretId: xxxxxxxxxxxx# 騰訊云API密鑰secretKey: xxxxxxxxxx# 驗證碼應用IDcaptchaAppId: 123456789# 驗證碼應用密鑰appSecretKey: xxxxxxxx# API端點endpoint: xxxxxxxxxxxx# API服務名稱service: captcha# API操作名稱action: DescribeCaptchaResult# 驗證碼類型captchaType: 9# 是否需要獲取驗證碼時間needGetCaptchaTime: 1# API版本version: "2025-07-10"
4. 配置類 config包下
4.1?
package org.example.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 驗證碼配置類** @author wangmh**/
@Configuration
@EnableConfigurationProperties(CaptchaProperties.class)
public class CaptchaConfig {/*** 配置ObjectMapper* * @return ObjectMapper實例*/@Beanpublic ObjectMapper objectMapper() {ObjectMapper objectMapper = new ObjectMapper();objectMapper.registerModule(new JavaTimeModule());objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);return objectMapper;}
}
package org.example.config;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*** 騰訊云驗證碼配置屬性** @author wangmh**/
@Component
@ConfigurationProperties(prefix = "tencent.captcha")
public class CaptchaProperties {/*** 騰訊云API密鑰ID*/private String secretId;/*** 騰訊云API密鑰*/private String secretKey;/*** 驗證碼應用ID*/private Integer captchaAppId;/*** 驗證碼類型*/private Integer captchaType;/*** 是否需要獲取驗證碼時間*/private Integer needGetCaptchaTime;/*** 應用密鑰*/private String appSecretKey;/*** API版本*/private String version;/*** API端點*/private String endpoint;/*** API服務名稱*/private String service;/*** API操作名稱*/private String action;// Getters and Setterspublic String getSecretId() {return secretId;}public void setSecretId(String secretId) {this.secretId = secretId;}public String getSecretKey() {return secretKey;}public void setSecretKey(String secretKey) {this.secretKey = secretKey;}public Integer getCaptchaAppId() {return captchaAppId;}public void setCaptchaAppId(Integer captchaAppId) {this.captchaAppId = captchaAppId;}public Integer getCaptchaType() {return captchaType;}public void setCaptchaType(Integer captchaType) {this.captchaType = captchaType;}public Integer getNeedGetCaptchaTime() {return needGetCaptchaTime;}public void setNeedGetCaptchaTime(Integer needGetCaptchaTime) {this.needGetCaptchaTime = needGetCaptchaTime;}public String getAppSecretKey() {return appSecretKey;}public void setAppSecretKey(String appSecretKey) {this.appSecretKey = appSecretKey;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}public String getEndpoint() {return endpoint;}public void setEndpoint(String endpoint) {this.endpoint = endpoint;}public String getService() {return service;}public void setService(String service) {this.service = service;}public String getAction() {return action;}public void setAction(String action) {this.action = action;}
}
5 實體類? dto包下
package org.example.dto;import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;/*** 驗證碼驗證請求DTO* * @author wangmh**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CaptchaVerifyRequest {/*** 用戶IP地址*/@NotBlank(message = "用戶IP地址不能為空")@JsonProperty("userIp")private String userIp;/*** 隨機字符串*/@NotBlank(message = "隨機字符串不能為空")@JsonProperty("randstr")private String randstr;/*** 驗證票據*/@NotBlank(message = "驗證票據不能為空")@JsonProperty("ticket")private String ticket;
}
package org.example.dto;import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;/*** 驗證碼驗證響應DTO** @author wangmh**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CaptchaVerifyResponse {/*** 請求ID*/@JsonProperty("RequestId")private String RequestId;/*** 驗證碼驗證結果* 1: 驗證通過*/@JsonProperty("CaptchaCode")private Integer CaptchaCode;/*** 驗證碼驗證消息*/@JsonProperty("CaptchaMsg")private String CaptchaMsg;/*** 前端獲取驗證碼時間*/@JsonProperty("GetCaptchaTime")private Integer GetCaptchaTime;/*** 提交驗證碼時間*/@JsonProperty("SubmitCaptchaTime")private Integer SubmitCaptchaTime;/*** 無感驗證模式下,該參數返回驗證結果*/@JsonProperty("EvilLevel")private Integer EvilLevel;/*** 攔截類型*/@JsonProperty("EvilBitmap")private Integer EvilBitmap;/*** 是否驗證通過*/public boolean isSuccess() {return CaptchaCode != null && CaptchaCode == 1;}}
6 Service
package org.example.service;import com.fasterxml.jackson.databind.ObjectMapper;
import com.tencentcloudapi.common.CommonClient;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.config.CaptchaProperties;
import org.example.dto.CaptchaVerifyRequest;
import org.example.dto.CaptchaVerifyResponse;
import org.springframework.stereotype.Service;import jakarta.annotation.PostConstruct;
import org.springframework.util.ObjectUtils;import java.util.HashMap;
import java.util.Map;/*** 騰訊云驗證碼服務** @author wangmh**/
@Slf4j
@Service
@RequiredArgsConstructor
public class CaptchaService {private final CaptchaProperties captchaProperties;private final ObjectMapper objectMapper;private CommonClient commonClient;/*** 初始化騰訊云客戶端*/@PostConstructpublic void init() {try {Credential credential = new Credential(captchaProperties.getSecretId(), captchaProperties.getSecretKey());HttpProfile httpProfile = new HttpProfile();httpProfile.setEndpoint(captchaProperties.getEndpoint());ClientProfile clientProfile = new ClientProfile();clientProfile.setHttpProfile(httpProfile);this.commonClient = new CommonClient(captchaProperties.getService(),captchaProperties.getVersion(),credential,"",clientProfile);log.info("騰訊云驗證碼客戶端初始化成功");} catch (Exception e) {log.error("騰訊云驗證碼客戶端初始化失敗", e);throw new RuntimeException("騰訊云驗證碼客戶端初始化失敗", e);}}/*** 驗證驗證碼* * @param request 驗證請求* @return 驗證響應*/public CaptchaVerifyResponse verifyCaptcha(CaptchaVerifyRequest request) throws TencentCloudSDKException {try {Map<String, Object> params = buildRequestParams(request);String paramsJson = objectMapper.writeValueAsString(params);log.info("驗證碼請求參數: {}", paramsJson);String response = commonClient.call(captchaProperties.getAction(), paramsJson);log.info("騰訊云API響應: {}", response);Map<String, Object> responseMap = objectMapper.readValue(response, Map.class);CaptchaVerifyResponse captchaResponse = buildCaptchaResponse(responseMap);return captchaResponse;} catch (TencentCloudSDKException e) {log.error("騰訊云API調用失敗", e);throw e;} catch (Exception e) {log.error("驗證碼驗證過程中發生異常", e);throw new RuntimeException("驗證碼驗證失敗", e);}}/*** 構建請求參數* * @param request 驗證請求* @return 請求參數Map*/private Map<String, Object> buildRequestParams(CaptchaVerifyRequest request) {Map<String, Object> params = new HashMap<>();// 從配置中獲取的參數params.put("CaptchaAppId", captchaProperties.getCaptchaAppId());params.put("CaptchaType", captchaProperties.getCaptchaType());params.put("NeedGetCaptchaTime", captchaProperties.getNeedGetCaptchaTime());params.put("AppSecretKey", captchaProperties.getAppSecretKey());// 從請求中獲取的參數params.put("UserIp", request.getUserIp());params.put("Randstr", request.getRandstr());params.put("Ticket", request.getTicket());return params;}/*** 簡單驗證方法 - 只返回是否驗證通過* * @param userIp 用戶IP* @param randstr 隨機字符串* @param ticket 驗證票據* @return 是否驗證通過*/public boolean isCaptchaValid(String userIp, String randstr, String ticket) {try {CaptchaVerifyRequest request = CaptchaVerifyRequest.builder().userIp(userIp).randstr(randstr).ticket(ticket).build();CaptchaVerifyResponse response = verifyCaptcha(request);return response.isSuccess();} catch (Exception e) {log.error("驗證碼驗證失敗", e);return false;}}/*** 構建驗證碼響應對象** @param responseMap 響應Map* @return 驗證碼響應對象*/private CaptchaVerifyResponse buildCaptchaResponse(Map<String, Object> responseMap) {CaptchaVerifyResponse response = new CaptchaVerifyResponse();try {if (responseMap.containsKey("Response")) {Map<String, Object> responseData = (Map<String, Object>) responseMap.get("Response");populateResponse(response, responseData);}} catch (Exception e) {log.error("解析響應數據失敗", e);}return response;}/*** 填充響應數據** @param response 響應對象* @param data 數據Map*/private void populateResponse(CaptchaVerifyResponse response, Map<String, Object> data) {if (data.containsKey("RequestId")) {response.setRequestId((String) data.get("RequestId"));}if (data.containsKey("CaptchaCode")) {Object v = data.get("CaptchaCode");if (!ObjectUtils.isEmpty(v))response.setCaptchaCode(v instanceof Integer ? (Integer) v : Integer.parseInt(v.toString()));}if (data.containsKey("CaptchaMsg")) {response.setCaptchaMsg((String) data.get("CaptchaMsg"));}if (data.containsKey("GetCaptchaTime")) {Object v = data.get("GetCaptchaTime");if (!ObjectUtils.isEmpty(v))response.setGetCaptchaTime(v instanceof Integer ? (Integer) v : Integer.parseInt(v.toString()));}if (data.containsKey("SubmitCaptchaTime")) {Object v = data.get("SubmitCaptchaTime");if (!ObjectUtils.isEmpty(v))response.setSubmitCaptchaTime(v instanceof Integer ? (Integer) v : Integer.parseInt(v.toString()));}if (data.containsKey("EvilLevel")) {Object v = data.get("EvilLevel");if (!ObjectUtils.isEmpty(v))response.setEvilLevel(v instanceof Integer ? (Integer) v : Integer.parseInt(v.toString()));}if (data.containsKey("EvilBitmap")) {Object v = data.get("EvilBitmap");if (!ObjectUtils.isEmpty(v))response.setEvilBitmap(v instanceof Integer ? (Integer) v : Integer.parseInt(v.toString()));}}}
6. Controller
package org.example.controller;import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.config.CaptchaProperties;
import org.example.dto.CaptchaVerifyRequest;
import org.example.dto.CaptchaVerifyResponse;
import org.example.service.CaptchaService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;import jakarta.validation.Valid;/*** 驗證碼控制器** @author wangmh**/
@Slf4j
@RestController
@RequestMapping("/api/captcha")
@RequiredArgsConstructor
public class CaptchaController {private final CaptchaService captchaService;private final CaptchaProperties captchaProperties;/*** 驗證驗證碼* * @param request 驗證請求* @return 驗證結果*/@PostMapping("/verify")public ResponseEntity<CaptchaVerifyResponse> verifyCaptcha(@Valid @RequestBody CaptchaVerifyRequest request) {try {log.info("收到簡單驗證碼驗證請求: userIp={}, randstr={}", request.getUserIp(), request.getRandstr());CaptchaVerifyResponse response = captchaService.verifyCaptcha(request);return ResponseEntity.ok(response);} catch (Exception e) {log.error("驗證碼驗證失敗", e);return ResponseEntity.internalServerError().build();}}/*** 簡單驗證接口* * @param userIp 用戶IP* @param randstr 隨機字符串* @param ticket 驗證票據* @return 是否驗證通過*/@GetMapping("/verify")public ResponseEntity<Boolean> verifyCaptchaSimple(@RequestParam String userIp,@RequestParam String randstr,@RequestParam String ticket) {try {log.info("收到簡單驗證碼驗證請求: userIp={}, randstr={}", userIp, randstr);boolean isValid = captchaService.isCaptchaValid(userIp, randstr, ticket);log.info("收到簡單驗證碼驗證請求: userIp={}, randstr={} 驗證結果:{}", userIp, randstr, isValid);return ResponseEntity.ok(isValid);} catch (Exception e) {log.error("驗證碼驗證失敗", e);return ResponseEntity.internalServerError().build();}}/*** 獲取驗證碼應用ID* @return captchaAppId*/@GetMapping("/appid")public ResponseEntity<Integer> getCaptchaAppId() {return ResponseEntity.ok(captchaProperties.getCaptchaAppId());}}
7. 請求測試
五 前端代碼
里面的js 下載地址:https://turing.captcha.qcloud.com/TJCaptcha.js
css 可以刪掉
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Web 前端接入示例</title><link rel="stylesheet" href="./css/style.css"><!-- 驗證碼程序依賴(必須)。請勿修改以下程序依賴,如通過其他手段規避加載,會導致驗證碼無法正常更新,對抗能力無法保證,甚至引起誤攔截。 --><script src="./js/TJCaptcha.js"></script>
</head><body><button id="CaptchaId" type="button">驗證</button>
</body><script>// 定義回調函數function callback(res) {// 第一個參數傳入回調結果,結果如下:// ret Int 驗證結果,0:驗證成功。2:用戶主動關閉驗證碼。// ticket String 驗證成功的票據,當且僅當 ret = 0 時 ticket 有值。// CaptchaAppId String 驗證碼應用ID。// bizState Any 自定義透傳參數。// randstr String 本次驗證的隨機串,后續票據校驗時需傳遞該參數。// verifyDuration Int 驗證碼校驗接口耗時(ms)。// actionDuration Int 操作校驗成功耗時(用戶動作+校驗完成)(ms)。// sid String 鏈路sid。console.log('callback:', res);// res(用戶主動關閉驗證碼)= {ret: 2, ticket: null}// res(驗證成功) = {ret: 0, ticket: "String", randstr: "String"}// res(請求驗證碼發生錯誤,驗證碼自動返回trerror_前綴的容災票據) = {ret: 0, ticket: "String", randstr: "String", errorCode: Number, errorMessage: "String"}// 此處代碼僅為驗證結果的展示示例,真實業務接入,建議基于ticket和errorCode情況做不同的業務處理if (res.ret === 0) {// 復制結果至剪切板var str = '【randstr】->【' + res.randstr + '】 【ticket】->【' + res.ticket + '】';var ipt = document.createElement('input');ipt.value = str;document.body.appendChild(ipt);ipt.select();document.execCommand("Copy");document.body.removeChild(ipt);alert('1. 返回結果(randstr、ticket)已復制到剪切板,ctrl+v 查看。2. 打開瀏覽器控制臺,查看完整返回結果。');}}// 定義驗證碼js加載錯誤處理函數function loadErrorCallback() {var appid = '您的CaptchaAppId';// 生成容災票據或自行做其它處理var ticket = 'trerror_1001_' + appid + '_' + Math.floor(new Date().getTime() / 1000);callback({ret: 0,randstr: '@' + Math.random().toString(36).substr(2),ticket: ticket,errorCode: 1001,errorMessage: 'jsload_error'});}// 定義驗證碼觸發事件window.onload = function () {document.getElementById('CaptchaId').onclick = function () {try {// 生成一個驗證碼對象// CaptchaAppId:登錄驗證碼控制臺,從【驗證管理】頁面進行查看。如果未創建過驗證,請先新建驗證。注意:不可使用客戶端類型為小程序的CaptchaAppId,會導致數據統計錯誤。//callback:定義的回調函數var captcha = new TencentCaptcha('198595300', callback, {userLanguage: 'zh-cn',showFn: (ret) => {const {duration, // 驗證碼渲染完成的耗時(ms)sid, // 鏈路sid} = ret;},});// 調用方法,顯示驗證碼captcha.show();} catch (error) {// 加載異常,調用驗證碼js加載錯誤處理函數loadErrorCallback();}}}
</script></html>
六 官方文檔
驗證碼產品頁面:T-sec-騰訊安全天御-行為式驗證碼 Captcha-騰訊云
產品敘述:驗證碼 產品概述_騰訊云
web端接入:驗證碼 Web 客戶端接入_騰訊云
接收票據校驗:驗證碼 接入票據校驗(Web 及 App)_騰訊云
核驗驗證碼票據結果:驗證碼 核查驗證碼票據結果(Web及APP)_騰訊云
jar包引入:云產品SDK中心_云產品SDK文檔-騰訊云