接入鉴权
为保证SparkRTC的通信安全,当用户加入房间时,华为云SparkRTC服务需要对其进行接入鉴权。本章节主要介绍华为云SparkRTC接入鉴权的实现原理及鉴权签名的生成方法。
鉴权原理
//认证用的app_key和app_id硬编码至代码中或以明文形式存储会有极大风险。建议密文形式配置存储在文件或者环境变量中,使用时解密,以确保安全。本例以app_key和app_id存放至环境变量为例,运行前请先在本地环境中设置完成环境变量APP_KEY和APP_ID。 app_key = System.getenv("APP_KEY"); app_id = System.getenv("APP_ID"); signature = hmacSha256(app_key,(app_id + room_id + user_id + ctime))
参数 |
说明 |
---|---|
app_key |
华为云SparkRTC针对每个app生成的鉴权密钥,需要安全保存,谨防泄漏。 app_key的获取方法请参见如何获取密钥?。 |
app_id |
华为云SparkRTC生成的应用ID。 app_id请在实时音视频控制台的“应用管理”中获取。 |
room_id |
租户自行创建的房间ID。 |
user_id |
租户接入华为云SparkRTC系统的用户ID。 |
ctime |
签名鉴权的过期时间。是系统当前UTC时间(unix时间戳)加上鉴权过期时间(推荐2小时,最长需要小于12小时)。单位为秒。
说明:
ctime为创建时间+过期时间,例如,当前时间为9点,鉴权过期时间为30分钟,则ctime为9点30分。即超过9点30分后,signature签名将失效。 |
建议租户构建自己的应用签名分发服务器,以防止“app_key”下沉到终端APP的过程中造成不必要的泄漏,鉴权原理如图1所示。
签名生成方法
您可以参考如下方法生成对应的签名。
- 将“app_id”、“room_id”,“user_id ”和“ctime”拼接为一个字符串。
1 2
long ctime = System.currentTimeMillis() / 1000 + 60 * 60; //有效时间为1小时,单位是秒 String content = app_id + "+" + room_id + "+" + user_id + "+" + ctime;
- 使用“app_key”,通过HMAC-SHA256方式将字符串“content”进行加密,得到签名字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
String signatureStr = hmacSha(appKey, content, "HmacSHA256"); static String hmacSha(String KEY, String VALUE, String SHA_TYPE) { try { SecretKeySpec signingKey = new SecretKeySpec(KEY.getBytes("UTF-8"), SHA_TYPE); Mac mac = Mac.getInstance(SHA_TYPE); mac.init(signingKey); byte[] rawHmac = mac.doFinal(VALUE.getBytes("UTF-8")); byte[] hexArray = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; byte[] hexChars = new byte[rawHmac.length * 2]; for (int j = 0; j < rawHmac.length; j++) { int v = rawHmac[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } catch (Exception ex) { throw new RuntimeException(ex); } }
签名生成样例
为防止“app_key”密钥泄漏,建议您配置自己的应用签名分发服务器,向服务器传入“app_id”、“room_id”,“user_id”和“ctime”后,由服务器返回签名。详细代码示例如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package com.xxx.xxx.utils; import androidx.annotation.NonNull; import com.alibaba.fastjson.JSON; import com.huawei.rtcdemo.Constants; import java.io.IOException; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import okhttp3.Response; public class SignatureUtil { private static final String TAG = "SignatureUtil"; public interface onSignatureSuccess { void onSuccess(String signature); } public static void getSignature(String appid, String roomid, String userid, long ctime, String signatureKey, onSignatureSuccess callback) { if (Constants.RTC_SIGNATURE_USE_LOCAL) { getSignatureLocal(appid, roomid, userid, ctime, signatureKey, callback); } else { getSignatureRemote(appid, roomid, userid, ctime, callback); } } private static void getSignatureLocal(String appid, String roomid, String userid, long ctime, String signatureKey, @NonNull onSignatureSuccess callback) { String content = appid + "+" + roomid + "+" + userid + "+" + ctime; // here "+" is real char in content. String signature = SignatureUtil.hmacSha256(signatureKey, content); callback.onSuccess(signature); } private static void getSignatureRemote(String appid, String roomid, String userid, long ctime, @NonNull onSignatureSuccess callback) { new Thread(new Runnable() { @Override public void run() { HttpUtil httpUtil = new HttpUtil(); // Constants.RTC_SIGNATURE_URL:带用户自己应用签名的分发服务器地址 String url = Constants.RTC_SIGNATURE_URL + "?appid=" + appid + "&roomid=" + roomid + "&userid=" + userid + "&ctime=" + ctime; Response response = httpUtil.sendGetMethodWithHead(url, "X-AUTH-TOKEN", Constants.RTC_AUTH_TOKEN); if (response == null) { return; } if (response.isSuccessful()) { try { String json = response.body().string(); String signature = JSON.parseObject(json).getString("signature"); callback.onSuccess(signature); } catch (IOException e) { LogUtil.e(TAG, "getSignature failed:" + e.getMessage()); } } else { LogUtil.e(TAG, "getSignature failed!"); } } }).start(); } public static String hmacSha256(String KEY, String VALUE) { return hmacSha(KEY, VALUE, "HmacSHA256"); } private static String hmacSha(String KEY, String VALUE, String SHA_TYPE) { try { SecretKeySpec signingKey = new SecretKeySpec(KEY.getBytes("UTF-8"), SHA_TYPE); Mac mac = Mac.getInstance(SHA_TYPE); mac.init(signingKey); byte[] rawHmac = mac.doFinal(VALUE.getBytes("UTF-8")); byte[] hexArray = { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' }; byte[] hexChars = new byte[rawHmac.length * 2]; for (int j = 0; j < rawHmac.length; j++) { int v = rawHmac[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } catch (Exception ex) { throw new RuntimeException(ex); } } } |