验证方式平台架构
需求概述
1、当前验证方式已插件化,系统支持图片验证码插件(默认)、腾讯云-天御图形验证插件和阿里云滑动验证。
2、以插件方式进行开发,以方便后期兼容更多的验证方式。
3、系统默认开启的是图片验证码方式进行验证。可在管理端-设置管理-验证方式设置中开启其它的验证方式。
数据库设计
表名:es_validator_platform
字段名 | 类型与长度 | 备注 |
---|---|---|
id | bigint(20) | 主键ID |
name | varchar(100) | 验证方式名称 |
open | int(1) | 是否开启 0:否,1:是 |
config | longtext | 配置json信息 |
plugin_id | varchar(100) | 插件唯一标识 |
图片验证码插件
插件唯一标识:captchaValidatorPlugin
系统默认开启的就是图片验证码插件,严格来说,图片验证码不属于插件化开发出来的,只是为了保持统一才将图片验证码作为了插件的其中一种。
图片验证码使用规范可参考:图片验证码规范
阿里云滑动验证插件
插件唯一标识:aliyunAfsPlugin
Javashop系统在后台代码中集成了阿里云滑动验证,但是在前端不会显示此插件信息,也就是默认是无法使用的,只有在有二开需求的情况下可以将相关代码开放。
这是由于在Javashop系统对腾讯一派的应用较深(例如微信登录、QQ登录、微信小程序应用等等),因此在阿里云和腾讯云两种方式中,我们默认开放腾讯云-天御图形验证,隐藏了阿里云滑动验证相关功能
流程图
注意:以下以登录操作举例,此验证方式可通用(登录、注册、绑定手机号等)。
验证流程说明:
客户端发送请求,需要将验证的参数上传,但是API在接收参数时,不接收需要验证的参数,而是在调用验证接口时用HTTPRequest的方式来获取参数。以登录操作举例,API只需要接收用户名和密码参数即可,其它需要验证的参数要在相对应的验证接口中用HTTPRequest的方式来获取,而不需要API在调用验证接口的时候直接传参。
代码展示
public void onValidate(Map param) {
HttpServletRequest req = ThreadContextHolder.getHttpRequest();
/** 应用标示。它和scene字段一起决定了滑动验证的业务场景与后端对应使用的策略模型 */
String appKey = param.get("appKey").toString();
/** 场景标示。它和appkey字段一起决定了滑动验证的业务场景与后端对应使用的策略模型 */
String scene = param.get("scene").toString();
/** 地区标示 */
String regionId = param.get("regionId").toString();
/** 访问秘钥ID */
String accessKeyId = param.get("accessKeyId").toString();
/** 访问秘钥 */
String accessKeySecret = param.get("accessKeySecret").toString();
AuthenticateSigRequest request = new AuthenticateSigRequest();
String cSessionid = req.getParameter("c_sessionid");
if(StringUtil.isEmpty(cSessionid)){
throw new ServiceException(SystemErrorCode.E930.code(), "验证方式已更换,请刷新界面重试");
}
request.setSessionId(cSessionid);
request.setSig(req.getParameter("sig"));
request.setToken(req.getParameter("nc_token"));
request.setScene(scene);
request.setAppKey(appKey);
request.setRemoteIp(getIpAddr(req));
try {
IAcsClient client = getClientProfile(regionId, accessKeyId, accessKeySecret);
//response的code枚举:100验签通过,900验签失败
AuthenticateSigResponse response = client.getAcsResponse(request);
if (response.getCode() != 100) {
throw new ServiceException(SystemErrorCode.E930.code(), "滑动验证参数校验失败");
}
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
//输出日志,但是继续返回前端相同的错误信息,不直接抛出 Exception 是为了减少logger输出
logger.error("滑动验证参数校验失败", e);
throw new ServiceException(SystemErrorCode.E930.code(), "滑动验证参数校验失败");
}
}
腾讯云-天御图形验证插件
SDK接入
Javashop系统对接了腾讯提供的SDK,以便快速集成对接
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.1.633</version>
</dependency>
代码展示
需要根据客户端类型来判断调用哪种接口
接口分为两种:Web 及 App校验接口和小程序校验接口
具体代码如下:
public void onValidate(Map param) {
HttpServletRequest request = ThreadContextHolder.getHttpRequest();
String secretId = param.get("secretId")+"";
String secretKey = param.get("secretKey")+"";
Long captchaType = 9l;
String ticket = request.getParameter("ticket");
String userIp = IPUtil.getIpAdrress();
String randstr = request.getParameter("randstr");
//从header读取客户端类型
String clientType = request.getHeader("clientType");
try{
// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
// 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
Credential cred = new Credential(secretId, secretKey);
// 实例化一个http选项,可选的,没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("captcha.tencentcloudapi.com");
// 实例化一个client选项,可选的,没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象,clientProfile是可选的
CaptchaClient client = new CaptchaClient(cred, "", clientProfile);
// 核查验证码票据结果(小程序插件)
// 官方文档:https://cloud.tencent.com/document/product/1110/48499
if (clientType.equals(ClientTypeEnum.MINI.name())) {
Long captchaAppId = Long.parseLong(param.get("miniCaptchaAppId")+"");
String appSecretKey = param.get("miniAppSecretKey")+"";
this.miniValidate(captchaType,ticket,userIp,captchaAppId,appSecretKey,client);
} else {
// 核查验证码票据结果(Web及APP)
// 官方文档:https://cloud.tencent.com/document/product/1110/36926
Long captchaAppId = Long.parseLong(param.get("captchaAppId")+"");
String appSecretKey = param.get("appSecretKey")+"";
this.pcAndH5Validate(captchaType,ticket,userIp,randstr,captchaAppId,appSecretKey,client);
}
} catch (TencentCloudSDKException e) {
this.logger.error(e.getMessage(), e);
throw new ServiceException("500", "验证码校验失败");
}
}
API说明
1、前端在开发验证功能时,需要调用API来获取当前系统开启了哪种验证方式,并获取相应的参数。
2、API说明:
API地址 | https://{base-api-domain}/validator |
---|---|
请求方式 | GET |
调用示例:
GET: https://base-api.xx.com/validator
返回参数:
返回参数为ValidatorPlatformDTO对象,其中包含的具体信息如下:
参数名 | 意义 | 类型 |
---|---|---|
validator_type | 验证类型 IMAGE:图片验证码,ALIYUN:阿里云滑动验证,TENCENT:阿里云滑动验证 | String |
aliyun_afs | 阿里云滑动验证参数对象 | AliyunAfsVO |
tencent | 腾讯云天御滑动验证参数 | TencentVO |
AliyunAfsVO对象说明:
名称 | 意义 | 类型 |
---|---|---|
app_key | 应用程序秘钥 | String |
scene | 场景标识 | String |
返回参数示例:
{
"validator_type": "ALIYUN",
"aliyun_afs": {
"app_key": "FFFF0N00000000008828",
"scene": "nc_other"
}
}
TencentVO对象说明:
名称 | 意义 | 类型 |
---|---|---|
captcha_app_id | PC/H5/APP图形验证码 CaptchaAppId | String |
mini_captcha_app_id | 小程序图形验证码 CaptchaAppId | String |
返回参数示例:
{
"validator_type": "TENCENT",
"tencent": {
"captcha_app_id": "19000001",
"mini_captcha_app_id": "19000002"
}
}