
# 接入鉴权
为保证SparkRTC的通信安全，当用户加入房间时，华为云SparkRTC服务需要对其进行接入鉴权。本章节主要介绍华为云SparkRTC接入鉴权的实现原理及鉴权签名的生成方法。
#### 鉴权原理
华为云SparkRTC系统使用数字签名作为接入鉴权方式，需要在SDK加入房间时设置"signature"和"ctime"。"signature"为标识签名，由租户使用华为云SparkRTC提供的"app_key"，"app_id"以及当前的"room_id"，"user_id"，"ctime"，按照华为SparkRTC的[签名生成样例]自行生成。具体参数说明请参见[表1]。
```
//认证用的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))
```
 表1参数说明 
| 参数      | 说明                                                                                                                                                                                                          |
|:---|:---|
| app_key | 华为云SparkRTC针对每个app生成的鉴权密钥，需要安全保存，谨防泄漏。 app_key的获取方法请参见[如何获取密钥？](https://support.huaweicloud.com/rtc_faq/rtc_06_0057.html)。                                                    |
| app_id  | 华为云SparkRTC生成的应用ID。 app_id请在[实时音视频控制台](https://console.huaweicloud.com/rtc/#/rtc/app-mgmt/app-list)的"应用管理"中获取。                                                                 |
| 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]所示。
图1鉴权原理   
![](https://support.huaweicloud.com/csdk-rtc/zh-cn_image_0295102350.png)
#### 签名生成方法
您可以参考如下方法生成对应的签名。
1. 将"app_id"、"room_id"，"user_id "和"ctime"拼接为一个字符串。
   ```
   long ctime = System.currentTimeMillis() / 1000 + 60 * 60;   //有效时间为1小时，单位是秒
   String content = app_id + "+" + room_id + "+" + user_id + "+" + ctime;
   ```
   
2. 使用"app_key"，通过HMAC-SHA256方式将字符串"content"进行加密，得到签名字符串。
   ```
   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"后，由服务器返回签名。详细代码示例如下所示：
```
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);
        }
    }
}
```
