登录接入
接口说明
为了保证游戏足够的安全性,我们在Token参数值提供了OTP自主验证。我们强烈建议游戏商必须做这一步动作。
平台账号Token,由SDK提供。
格式:"09608220dcf453a6fd2f10ddcc4182f3.1611134087.48234923",
按.分隔,第一个参数是Token,第二个参数是个计数值,第三个参数是验证码。
使用OTP算法,使用计数值,再加上密钥生成验证码(具体生成方式看下面)。再与第三个参数验证码比对。
一次性密码(OTP)介绍
流程图
游戏商实现部分为 5,6,7,8步骤
Token自主验证(游戏商服务器)
PHP 示例代码
token验证例子:
// secret由Eskyfun提供
$secret = '745a950f994b7da417cd438e5619ac7329400460';
//平台SDK提供token、userid参数
$token = '03c7c0ace395d80182db07ae2c30f034.1499671547.55935154';
$userid = '1000001';
//分割token
$tokenArray = explode('.',$token);
if(count($tokenArray) == 3){
//计数器
$times = $tokenArray[1];
//验证码
$code = $tokenArray[2];
//使用Token、用户ID、密钥,拼接用户唯一密钥值
$userSecret = $tokenArray[0].$userid.$secret;
//获取code
$otp = new Otp();
$oneCode = $otp->getCode($userSecret,$times);
//对比验证
if($oneCode == $code){
// 登录成功
}else{
// 登录失败
}
}else{
// 登录失败
}
PHP OTP类
1.通过hash_hmac加密方式,获取加密值。
2.再unpack解包加密值得到解包后的数据,再截取32位。
3.再mod取8位(需要则补位)得到验证码
class Otp
{
protected $_codeLength = 8;
protected $_timeSec = 30;
protected $_digest = 'SHA1';
public function getCode($secret, $timeSlice = null)
{
if ($timeSlice === null) {
$timeSlice = floor(time() / $this->_timeSec);
}
// hash加密
$hm = hash_hmac($this->_digest, (string)$timeSlice, $secret, true);
// 解包二进制数据,unpack N格式至少需要4个长度
$value = unpack('N', $hm);
$value = $value[1];
// 截取32位
$value = $value & 0x7FFFFFFF;
//位数值 eq:10的8次方
$modulo = pow(10, $this->_codeLength);
/** 返回值
* 如果截取32位的值,不够8位,向左填充0,返回值补充至8位
* 例如$value = 112345678 , $modulo=100000000,$value % $modulo = 12345678,返回12345678
* 例如$value = 123456 , $modulo=100000000,$value % $modulo = 123456,补充00,返回00123456
*/
return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
}
}
PYTHON 示例代码
token验证例子:
#secret由Eskyfun提供
secret = '745a950f994b7da417cd438e5619ac7329400460'
#平台SDK提供token、userId参数
token = '03c7c0ace395d80182db07ae2c30f034.1499671547.55935154'
userId = '1000001'
#分割token
tokenArray = token.split(".")
if len(tokenArray) == 3 :
#计数器
times = tokenArray[1]
#验证码
code = tokenArray[2]
#使用密钥和用户ID,拼接用户唯一密钥值
userSecret = tokenArray[0] + userId + secret
#获取code
oneCode = OPT.getCode(userSecret,times)
#对比验证
if oneCode == code:
#成功
print(1)
else :
#失败
print(0)
else:
#失败
print(0)
Python OTP类
1.通过hash_hmac加密方式,获取加密值。
2.再unpack解包加密值得到解包后的数据,再截取32位。
3.再mod取8位(需要则补位)得到验证码
class OPT:
codeLength = 8
timeSec = 30
digest = 'SHA1'
def getCode(secret,timeSlice = None):
if timeSlice == None:
timeSlice = math.floor(time.time() - OPT.timeSec)
#进行加密
hm = hmac.new(secret.encode('utf-8'),timeSlice.encode('utf-8'),OPT.digest).digest()
#进行二进制解包
packArray = struct.unpack('>L',hm[0:4])
num = packArray[0]
#补位
num = num & 0x7FFFFFFF;
modulo = pow(10, OPT.codeLength);
sNum = str(num % modulo)
sNum = sNum.zfill(OPT.codeLength)
return sNum
JAVA 示例代码
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
public class Token {
public static void main(String[] args) {
//secret由Eskyfun提供
String secret = "745a950f994b7da417cd438e5619ac7329400460";
//平台SDK提供token、userId参数
String token = "03c7c0ace395d80182db07ae2c30f034.1499671547.55935154";
String userId = "1000001";
//分割token
String[] tokenArray = token.split("\\.");
System.out.println("token中的code=" + tokenArray[2]);
// 使用密钥和用户ID,拼接用户唯一密钥值
String key = tokenArray[0] + userId + secret;
//计数器
String time = tokenArray[1];
// 获取code
OTP opt = new OTP();
String oneCode = opt.getCode(key, time);
if (oneCode.equals(tokenArray[2])){
System.out.println("成功");
} else {
System.out.println("失败");
}
}
public static class OTP{
public String getCode(String userSecret, String time){
//获取HmacSHA1
byte[] bytes;
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(userSecret.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKeySpec);
bytes = mac.doFinal(time.getBytes());
} catch (Exception e) {
return "";
}
//truncate 解包二进制数据
ByteBuffer data = ByteBuffer.wrap(bytes);
int num = data.getInt(0) & 0x7FFFFFFF;
int otp = num % 100000000;
StringBuilder result = new StringBuilder(Integer.toString(otp));
while (result.length() < 8) {
result.insert(0, "0");
}
return result.toString();
}
}
}