统一鉴权说明
虎牙小程序API以及开放API都通过JWT方式进行接入验签操作。
1.JWT的结构:
JWT通过以下三部分组成:
Header
Payload
Signature
通过英文点号"."拼接成 Header.Payload.Signature
样例如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1NTYxODgzOTEsImV4cCI6MTU1NjE4ODk5MSwiYXBwSWQiOiJ4eHh4eHh4eHh4eHgifQ.DFdsMFu_VzPkUUS1eu_Kwyzvc6vSQ-x_HI3wEut72cU
2.各字段说明
3.1 Header
Header部分是一个JSON字符串,描述 JWT 的元数据:
{
"alg": "HS256",
"typ": "JWT"
}
- 1.alg属性表示签名的算法,字符串类型,默认是HS256(HMACSHA256);
- 2.typ属性表示token的类型,字符串类型,统一为JWT;
- 3.将Header对应的Json字符串进行base64UrlEncode(header)(可在jwt.io验证),转换后的字符串为:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
3.2 Payload
Payload部分也是一个JSON字符串,用来存放实际需要传递的数据。 以下是虎牙开放API约定的必填字段:
{
"iat":1556188391,
"exp":1556188991,
"appId":"xxxxxxxxxxxx"
}
- 1.iat属性表示当前时间的unix时间戳(秒),整型;
- 2.exp属性表示过期时间的unix时间戳(秒),整型,通常设置10分钟有效,即exp=iat+600,注意不少于当前时间且不超过当前时间60分钟;
- 3.appId属性为开发者申请的开发者账号(点此查看),字符串类型;
- 4.将Payload对应的Json字符串进行base64UrlEncode(payload)(可在jwt.io验证),转换后的字符串为:
eyJpYXQiOjE1NTYxODgzOTEsImV4cCI6MTU1NjE4ODk5MSwiYXBwSWQiOiJ4eHh4eHh4eHh4eHgifQ
- 注意,JWT的数据传递默认是不加密的,请不要把秘密信息放在这个部分。
3.3 Signature
Signature部分是对前两部分(Header和Payload)的签名,防止数据篡改。
- 1.首先需要指定一个密钥appSecret,这个密钥对应的是开发者申请的appId的secret(点此查看)。
- 2.使用HMACSHA256算法计算出签名,完整的签名生成如下(可在jwt.io验证)(例子中的secret为123456):
HMACSHA256(
base64UrlEncode(Header) + "." +
base64UrlEncode(Payload),
secret) //!注意:secret不需要base64UrlEncode
- 3.将计算出的签名进行base64UrlEncode(可在jwt.io验证),转换后的字符串为:
DFdsMFu_VzPkUUS1eu_Kwyzvc6vSQ-x_HI3wEut72cU
3.4 JWT串(token)
最后把Header、Payload、Signature三部分拼成字符串,每个部分之间用"."(点)分隔,上面例子对应的JWT串(sToken)是:
拼成:Header.Payload.Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1NTYxODgzOTEsImV4cCI6MTU1NjE4ODk5MSwiYXBwSWQiOiJ4eHh4eHh4eHh4eHgifQ.DFdsMFu_VzPkUUS1eu_Kwyzvc6vSQ-x_HI3wEut72cU
3.JWT官方网站上有各种语言的类库。
4.JWT加解密DEMO
本示例用JAVA语言写了一个小程序token的加解密的demo。具体的可在代码仓库中查看详细代码。
代码示例
package hyext.token.examples;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
/**
* 小程序Token加解密demo
*/
public class ExtToken {
/**
* 签名生成
* @param appId 小程序开发者ID(成为开发者后,https://ext.huya.com可查)
* @param secret 小程序开发者密钥(成为开发者后,https://ext.huya.com可查)
* @param extUuid 小程序ID(成为开发者后,https://ext.huya.com可查)
* @param profileId 主播unionId
* @param second 签名有效时间(秒)
* @return
*/
public static String genToken(String appId, String secret, String extUuid, String profileId, int second) {
//获取时间戳(毫秒)
long currentTimeMillis = System.currentTimeMillis();
long expireTimeMillis = System.currentTimeMillis() + second * 60 * 1000; //超时时间
Date iat = new Date(currentTimeMillis);
Date exp = new Date(expireTimeMillis);
try {
Map<String, Object> header = new HashMap<String, Object>();
header.put("alg", "HS256");
header.put("typ", "JWT");
//生成JWT凭证
Algorithm algorithm = Algorithm.HMAC256(secret); //开发者密钥
String sToken = JWT.create()
.withHeader(header) //JWT声明
.withIssuedAt(iat) //jwt凭证生成时间
.withExpiresAt(exp) //jwt凭证超时时间
.withClaim("appId", appId) //开发者ID
.withClaim("extUuid", extUuid) //小程序ID
.withClaim("creator", "DEV") //创建者(token生成方:SYS平台,DEV开发者)
.withClaim("profileId", profileId) //开发者ID
.sign(algorithm);
return sToken;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 签名解密
* @param token 小程序签名串
* @param sceret 小程序开发者密钥(成为开发者后,https://ext.huya.com可查)
* @return
*/
public static Map<String, Claim> parseToken(String token, String sceret) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(sceret)).build();
jwt = verifier.verify(token);
return jwt.getClaims();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
String appId = "testAppId";
String extUuid = "testExtUuid";
String sceret = "testSecret";
String profileId = "testProfileId";
String token = genToken(appId, sceret, extUuid, profileId,86400); // 生成签名
System.out.println("token:" + token);
Map<String, Claim> claims = parseToken(token, sceret); // 签名解密
System.out.println("claims appId :" + claims.get("appId").asString());
System.out.println("claims extUuid :" + claims.get("extUuid").asString());
System.out.println("claims profileId :" + claims.get("profileId").asString());
System.out.println("claims creator :" + claims.get("creator").asString());
}
}