以下是完整的易语言验证客户端实现,推荐使用精易模块进行开发。
.版本 2
.支持库 spec
.程序集 验证模块
.程序集变量 APP_KEY, 文本型
.程序集变量 APP_SECRET, 文本型
.程序集变量 TRANSFER_KEY, 文本型
.程序集变量 API_URL, 文本型
.程序集变量 当前Token, 文本型
.程序集变量 执行令牌, 文本型
.程序集变量 当前用户名, 文本型
.程序集变量 到期时间, 文本型
.程序集变量 剩余点数, 整数型
.程序集变量 心跳间隔, 整数型
.程序集变量 最后错误码, 整数型
.程序集变量 最后错误信息, 文本型
.程序集变量 机器码, 文本型
' ==================== 初始化配置 ====================
.子程序 初始化配置, 逻辑型, 公开, 初始化SDK配置
.参数 应用密钥, 文本型
.参数 签名密钥, 文本型
.参数 传输密钥, 文本型
.参数 接口地址, 文本型
APP_KEY = 应用密钥
APP_SECRET = 签名密钥
TRANSFER_KEY = 传输密钥
API_URL = 接口地址
心跳间隔 = 30
返回 (真)
' ==================== 核心请求函数 ====================
.子程序 发送加密请求, 文本型, , 发送加密请求并返回解密后的JSON
.参数 参数文本, 文本型, , 已排序的参数字符串(不含sign)
.局部变量 签名, 文本型
.局部变量 完整参数, 文本型
.局部变量 加密数据, 字节集
.局部变量 响应, 文本型
.局部变量 解密数据, 字节集
' 1. 计算HMAC-SHA256签名
签名 = 取文本小写 (编码_HMAC_SHA256 (到字节集 (参数文本), 到字节集 (APP_SECRET), 真))
完整参数 = 参数文本 + "&sign=" + 签名
' 2. RC4加密
加密数据 = 编码_RC4加密 (到字节集 (完整参数), 到字节集 (TRANSFER_KEY))
' 3. 发送请求
响应 = 网页_访问_对象 (API_URL + "?app_key=" + APP_KEY + "&data=" + 取文本小写 (编码_字节集到十六进制 (加密数据)))
' 4. 判断响应类型并解密
.如果 (取文本左边 (响应, 1) = "{")
' 明文响应
返回 (响应)
.否则
' 密文响应,RC4解密
解密数据 = 编码_RC4加密 (编码_十六进制到字节集 (响应), 到字节集 (TRANSFER_KEY))
返回 (到文本 (解密数据))
.如果结束
.子程序 构建参数, 文本型, , 构建排序后的参数字符串
.参数 参数数组, 文本型, 数组, 格式: {"key1=value1", "key2=value2", ...}
.局部变量 i, 整数型
.局部变量 结果, 文本型
' 数组排序(按ASCII升序)
数组_排序 (参数数组, , 真)
' 拼接
结果 = ""
.计次循环首 (取数组成员数 (参数数组), i)
.如果 (结果 ≠ "")
结果 = 结果 + "&"
.如果结束
结果 = 结果 + 参数数组 [i]
.计次循环尾 ()
返回 (结果)
.子程序 取时间戳, 文本型, , 获取Unix时间戳
返回 (到文本 (取现行时间戳 ()))
.子程序 取随机字符串, 文本型, , 生成随机字符串
.参数 长度, 整数型, 可空, 默认16
.局部变量 字符集, 文本型
.局部变量 结果, 文本型
.局部变量 i, 整数型
.如果 (是否为空 (长度) 或 长度 ≤ 0)
长度 = 16
.如果结束
字符集 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
结果 = ""
.计次循环首 (长度, i)
结果 = 结果 + 取文本中间 (字符集, 取随机数 (1, 取文本长度 (字符集)), 1)
.计次循环尾 ()
返回 (结果)
.子程序 解析JSON字符串, 文本型, , 从JSON中提取字符串值
.参数 JSON文本, 文本型
.参数 键名, 文本型
.局部变量 位置, 整数型
.局部变量 开始, 整数型
.局部变量 结束, 整数型
位置 = 寻找文本 (JSON文本, #引号 + 键名 + #引号 + ":", , 假)
.如果 (位置 ≤ 0)
返回 ("")
.如果结束
开始 = 寻找文本 (JSON文本, #引号, 位置 + 取文本长度 (键名) + 3, 假)
.如果 (开始 ≤ 0)
返回 ("")
.如果结束
结束 = 寻找文本 (JSON文本, #引号, 开始 + 1, 假)
.如果 (结束 ≤ 0)
返回 ("")
.如果结束
返回 (取文本中间 (JSON文本, 开始 + 1, 结束 - 开始 - 1))
.子程序 解析JSON整数, 整数型, , 从JSON中提取整数值
.参数 JSON文本, 文本型
.参数 键名, 文本型
.局部变量 位置, 整数型
.局部变量 数值文本, 文本型
.局部变量 i, 整数型
.局部变量 字符, 文本型
位置 = 寻找文本 (JSON文本, #引号 + 键名 + #引号 + ":", , 假)
.如果 (位置 ≤ 0)
返回 (0)
.如果结束
位置 = 位置 + 取文本长度 (键名) + 3
数值文本 = ""
.计次循环首 (20, i)
字符 = 取文本中间 (JSON文本, 位置 + i - 1, 1)
.如果 (字符 ≥ "0" 且 字符 ≤ "9" 或 字符 = "-")
数值文本 = 数值文本 + 字符
.否则
.如果 (数值文本 ≠ "")
跳出循环 ()
.如果结束
.如果结束
.计次循环尾 ()
.如果 (数值文本 = "")
返回 (0)
.如果结束
返回 (到整数 (数值文本))
' ==================== API接口 ====================
.子程序 API_初始化, 逻辑型, 公开, 初始化验证
.参数 输入机器码, 文本型
.参数 版本号, 文本型, 可空
.局部变量 参数, 文本型, , "0"
.局部变量 响应, 文本型
机器码 = 输入机器码
重定义数组 (参数, 假, 0)
加入成员 (参数, "action=init")
加入成员 (参数, "machine_code=" + 机器码)
加入成员 (参数, "nonce=" + 取随机字符串 ())
加入成员 (参数, "timestamp=" + 取时间戳 ())
.如果 (是否为空 (版本号) = 假 且 版本号 ≠ "")
加入成员 (参数, "version=" + 版本号)
.如果结束
响应 = 发送加密请求 (构建参数 (参数))
最后错误码 = 解析JSON整数 (响应, "code")
最后错误信息 = 解析JSON字符串 (响应, "msg")
.如果 (最后错误码 ≠ 1)
返回 (假)
.如果结束
心跳间隔 = 解析JSON整数 (响应, "heartbeat_interval")
.如果 (心跳间隔 ≤ 0)
心跳间隔 = 30
.如果结束
返回 (真)
.子程序 API_登录, 逻辑型, 公开, 登录验证(统一接口)
.参数 用户名, 文本型, , 账号登录传用户名,卡密登录传卡密
.参数 密码, 文本型, , 账号登录传密码,卡密登录传"0"
.局部变量 参数, 文本型, , "0"
.局部变量 响应, 文本型
重定义数组 (参数, 假, 0)
加入成员 (参数, "action=login")
加入成员 (参数, "machine_code=" + 机器码)
加入成员 (参数, "nonce=" + 取随机字符串 ())
加入成员 (参数, "password=" + 密码)
加入成员 (参数, "timestamp=" + 取时间戳 ())
加入成员 (参数, "username=" + 用户名)
响应 = 发送加密请求 (构建参数 (参数))
最后错误码 = 解析JSON整数 (响应, "code")
最后错误信息 = 解析JSON字符串 (响应, "msg")
.如果 (最后错误码 ≠ 2)
返回 (假)
.如果结束
当前Token = 解析JSON字符串 (响应, "token")
执行令牌 = 解析JSON字符串 (响应, "exec_token")
当前用户名 = 解析JSON字符串 (响应, "username")
到期时间 = 解析JSON字符串 (响应, "expire_time")
剩余点数 = 解析JSON整数 (响应, "points")
返回 (真)
.子程序 API_心跳, 逻辑型, 公开, 发送心跳
.局部变量 参数, 文本型, , "0"
.局部变量 响应, 文本型
重定义数组 (参数, 假, 0)
加入成员 (参数, "action=heartbeat")
加入成员 (参数, "machine_code=" + 机器码)
加入成员 (参数, "timestamp=" + 取时间戳 ())
加入成员 (参数, "token=" + 当前Token)
响应 = 发送加密请求 (构建参数 (参数))
最后错误码 = 解析JSON整数 (响应, "code")
最后错误信息 = 解析JSON字符串 (响应, "msg")
.如果 (最后错误码 ≠ 3)
返回 (假)
.如果结束
到期时间 = 解析JSON字符串 (响应, "expire_time")
剩余点数 = 解析JSON整数 (响应, "points")
执行令牌 = 解析JSON字符串 (响应, "exec_token")
返回 (真)
.子程序 API_登出, 逻辑型, 公开, 退出登录
.局部变量 参数, 文本型, , "0"
.局部变量 响应, 文本型
重定义数组 (参数, 假, 0)
加入成员 (参数, "action=logout")
加入成员 (参数, "machine_code=" + 机器码)
加入成员 (参数, "timestamp=" + 取时间戳 ())
加入成员 (参数, "token=" + 当前Token)
响应 = 发送加密请求 (构建参数 (参数))
最后错误码 = 解析JSON整数 (响应, "code")
返回 (最后错误码 = 4)
.子程序 API_扣点, 逻辑型, 公开, 扣除点数
.参数 点数, 整数型
.参数 请求ID, 文本型, 可空, 用于幂等性,可传UUID
.局部变量 参数, 文本型, , "0"
.局部变量 响应, 文本型
重定义数组 (参数, 假, 0)
加入成员 (参数, "action=deduct")
加入成员 (参数, "machine_code=" + 机器码)
加入成员 (参数, "points=" + 到文本 (点数))
.如果 (是否为空 (请求ID) 或 请求ID = "")
加入成员 (参数, "request_id=" + 取随机字符串 (32))
.否则
加入成员 (参数, "request_id=" + 请求ID)
.如果结束
加入成员 (参数, "timestamp=" + 取时间戳 ())
加入成员 (参数, "token=" + 当前Token)
响应 = 发送加密请求 (构建参数 (参数))
最后错误码 = 解析JSON整数 (响应, "code")
最后错误信息 = 解析JSON字符串 (响应, "msg")
.如果 (最后错误码 ≠ 8)
返回 (假)
.如果结束
剩余点数 = 解析JSON整数 (响应, "points")
返回 (真)
.子程序 API_充值, 逻辑型, 公开, 使用卡密充值
.参数 卡密, 文本型
.参数 用户名, 文本型, 可空, 未登录时使用
.局部变量 参数, 文本型, , "0"
.局部变量 响应, 文本型
重定义数组 (参数, 假, 0)
加入成员 (参数, "action=recharge")
加入成员 (参数, "card_no=" + 卡密)
加入成员 (参数, "timestamp=" + 取时间戳 ())
.如果 (当前Token ≠ "")
加入成员 (参数, "token=" + 当前Token)
.否则
.如果 (是否为空 (用户名) = 假 且 用户名 ≠ "")
加入成员 (参数, "username=" + 用户名)
.如果结束
.如果结束
响应 = 发送加密请求 (构建参数 (参数))
最后错误码 = 解析JSON整数 (响应, "code")
最后错误信息 = 解析JSON字符串 (响应, "msg")
.如果 (最后错误码 ≠ 5)
返回 (假)
.如果结束
到期时间 = 解析JSON字符串 (响应, "expire_time")
剩余点数 = 解析JSON整数 (响应, "points")
返回 (真)
.子程序 API_验证, 逻辑型, 公开, 验证Token是否有效
.局部变量 参数, 文本型, , "0"
.局部变量 响应, 文本型
重定义数组 (参数, 假, 0)
加入成员 (参数, "action=verify")
加入成员 (参数, "machine_code=" + 机器码)
加入成员 (参数, "timestamp=" + 取时间戳 ())
加入成员 (参数, "token=" + 当前Token)
响应 = 发送加密请求 (构建参数 (参数))
最后错误码 = 解析JSON整数 (响应, "code")
返回 (最后错误码 = 9)
' ==================== 属性获取 ====================
.子程序 取Token, 文本型, 公开
返回 (当前Token)
.子程序 取用户名, 文本型, 公开
返回 (当前用户名)
.子程序 取到期时间, 文本型, 公开
返回 (到期时间)
.子程序 取剩余点数, 整数型, 公开
返回 (剩余点数)
.子程序 取心跳间隔, 整数型, 公开
返回 (心跳间隔)
.子程序 取错误码, 整数型, 公开
返回 (最后错误码)
.子程序 取错误信息, 文本型, 公开
返回 (最后错误信息)
.版本 2
.支持库 spec
.程序集 窗口程序集_启动窗口
.程序集变量 心跳时钟, 时钟
.子程序 __启动窗口_创建完毕
' 初始化配置
初始化配置 ("your_app_key", "your_app_secret", "your_transfer_key", "https://your-server.com/api/v1/client/enc")
.子程序 _按钮_初始化_被单击
.如果 (API_初始化 (取机器码 (), "1.0.0"))
信息框 ("初始化成功!心跳间隔: " + 到文本 (取心跳间隔 ()) + "秒", 0, "提示")
.否则
信息框 ("初始化失败: " + 取错误信息 () + " (code: " + 到文本 (取错误码 ()) + ")", 0, "错误")
.如果结束
.子程序 _按钮_登录_被单击
.局部变量 卡密, 文本型
卡密 = 编辑框_卡密.内容
.如果 (卡密 = "")
信息框 ("请输入卡密", 0, "提示")
返回 ()
.如果结束
.如果 (API_登录 (卡密, "0"))
信息框 ("登录成功!" + #换行符 + "用户名: " + 取用户名 () + #换行符 + "到期时间: " + 取到期时间 () + #换行符 + "剩余点数: " + 到文本 (取剩余点数 ()), 0, "提示")
' 启动心跳
时钟_心跳.时钟周期 = 取心跳间隔 () × 1000
更新显示 ()
.否则
信息框 ("登录失败: " + 取错误信息 () + " (code: " + 到文本 (取错误码 ()) + ")", 0, "错误")
.如果结束
.子程序 _时钟_心跳_周期事件
.如果 (API_心跳 () = 假)
时钟_心跳.时钟周期 = 0
信息框 ("心跳失败,已掉线: " + 取错误信息 (), 0, "错误")
返回 ()
.如果结束
更新显示 ()
.子程序 _按钮_扣点_被单击
.如果 (API_扣点 (1, ))
信息框 ("扣点成功,剩余: " + 到文本 (取剩余点数 ()), 0, "提示")
更新显示 ()
.否则
信息框 ("扣点失败: " + 取错误信息 (), 0, "错误")
.如果结束
.子程序 _按钮_退出_被单击
时钟_心跳.时钟周期 = 0
API_登出 ()
信息框 ("已退出登录", 0, "提示")
结束 ()
.子程序 更新显示
标签_用户名.标题 = "用户名: " + 取用户名 ()
标签_到期时间.标题 = "到期时间: " + 取到期时间 ()
标签_点数.标题 = "剩余点数: " + 到文本 (取剩余点数 ())
.子程序 取机器码, 文本型, , 获取机器码(示例)
' 实际项目中应该获取真实的硬件信息
' 可以使用精易模块的 系统_取硬件特征码() 函数
返回 (系统_取硬件特征码 ())
| 函数名 | 说明 |
|---|---|
| 编码_RC4加密(字节集, 密钥字节集) | RC4加密/解密(对称算法) |
| 编码_HMAC_SHA256(数据字节集, 密钥字节集, 是否返回十六进制) | HMAC-SHA256签名 |
| 编码_字节集到十六进制(字节集) | 字节集转HEX字符串 |
| 编码_十六进制到字节集(十六进制文本) | HEX字符串转字节集 |
| 网页_访问_对象(URL) | HTTP GET请求 |
| 取现行时间戳() | 获取Unix时间戳 |
| 系统_取硬件特征码() | 获取机器码 |
签名前参数必须按ASCII字母顺序排列,例如:
action=login&machine_code=xxx&nonce=xxx&password=xxx×tamp=xxx&username=xxx
a开头的action在前,m开头的machine_code其次,n开头的nonce再次,以此类推。
数组_排序(参数, , 真) 的第三个参数设为"真"来启用ASCII排序。