# JSAPI 鉴权流程

为了防止恶意网页通过 JSAPI 随意获取用户私密信息，对用户财产造成损失，需要确保 JSAPI 的调用方是可信任的。因此在 JSAPI 被调用前，请仔细阅读下文中接入必读及接入流程。

## 接入必读

- 目前协作提供 JSAPI 中 requestAccess、getDeviceInfo、getAppInfo、getNetworkType 无需鉴权即可使用；其他的 JSAPI 都需要鉴权方可正常调用；
- 鉴权是针对 H5 页面实现的，如果当前页面用到了 JSAPI ，此页面就需要执行鉴权流程。如果项目属于私有化环境，请按新文档指引接入，文档链接格式为： `域名/developer/` 

## 流程时序图

![](https://cloudcdn.qwps.cn/open/_img/aad6400f22.png)

## Step1 获取 jsapi_token

### 请求说明

| **请求地址** | https://openapi.wps.cn/kopen/woa/api/v1/developer/app/sdk/auth/jsapi_token            |
| :----------- | :------------------------------------------------------------------------------------ |
| **请求方法** | GET                                                                                   |
| **签名方式** | [wps-3](/app-integration-dev/wps365/server/api-description/signature-description-wps-3) |

### 请求地址示例

```http
[GET]https://openapi.wps.cn/kopen/woa/api/v1/developer/app/sdk/auth/jsapi_token
```

### 响应体示例

```json
// 成功
{
    "result": 0,
    "jsapi_token": "xxxxxx",
    "expires_in": 7200 // 过期时间(秒)
}
// 失败
{
    "result": 10801xxx,
    "msg": "error msg"
}
```

## Step2 获取 jsapi_ticket

### 请求说明

| **请求地址** | https://openapi.wps.cn/kopen/woa/api/v1/developer/app/sdk/auth/jsapi_ticket           |
| :----------- | :------------------------------------------------------------------------------------ |
| **请求方法** | GET                                                                                   |
| **签名方式** | [wps-3](/app-integration-dev/wps365/server/api-description/signature-description-wps-3) |

### 查询参数（Query）

| **名称**    | **类型** | **是否必填** | **说明**                 |
| :---------- | :------- | :----------- | :----------------------- |
| jsapi_token | string   | 是           | 上一步获取的 jsapi_token |

### 请求地址示例

```http
[GET]https://openapi.wps.cn/kopen/woa/api/v1/developer/app/sdk/auth/jsapi_ticket?jsapi_token=xxxx
```

### 响应体示例

```json
// 成功
{
    "result": 0,
    "jsapi_ticket": "xxxxxx",
    "expires_in": 7200 // 过期时间(秒)
}
// 失败
{
    "result": 10801xxx,
    "msg": "error msg"
}
```

## Step3 计算签名

参与签名的字段包括 noncestr（随机字符串），有效的 jsapi_ticket，timestamp（时间戳）， url（**当前网页的 URL，需包含# ？& 等部分，即一个完整的网页 url 数据。若前端传给服务端做了编码处理，服务端需要进行解码，以保证 url 正确**）。

```json
noncestr=Y7a8KkqX041bsSwT   // 业务方随机字符串
jsapi_ticket=617bf955832a4d4d80d9d8d85917a427  // ticket
timestamp=1510045655000    // 时间戳
url=https://m.haiwainet.cn/ttc/3541093/2018/0509/content_31312407_1.html?a=b&c=d // 当前网页url
```

根据 jsapi_ticket、nonceStr、timestamp、url 的顺序，使用 URL 键值对的格式（即 key1=value1&key2=value2...）拼接成字符串 verifyStr：**(顺序和字段名不能改变)**

```json
jsapi_ticket=617bf955832a4d4d80d9d8d85917a427&noncestr=Y7a8KkqX041bsSwT&timestamp=1510045655000&url=https://m.haiwainet.cn/ttc/3541093/2018/0509/content_31312407_1.html?a=b&c=d
```

对 verifyStr 进行 sha1 签名，得到 signature：

```json
63fba76a53eb4862872741ead44731f53465d563
```

示例代码：

```go
func main() {

    noncestr := "Y7a8KkqX041bsSwT"
    jsapi_ticket := "617bf955832a4d4d80d9d8d85917a427"
    timestamp := "1510045655000"
    url := "https://m.haiwainet.cn/ttc/3541093/2018/0509/content_31312407_1.html?a=b&c=d"

    verifyStr := "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url

    hash := sha1.New()
    hash.Write([]byte(verifyStr))

    toString := hex.EncodeToString(hash.Sum(nil))

    fmt.Println(toString)
}
```

```java
import java.security.MessageDigest;

public class main {
    public static String shaEncode(String inStr) throws Exception {
        MessageDigest sha = null;
        try {
            sha = MessageDigest.getInstance("SHA");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }

        byte[] byteArray = inStr.getBytes("UTF-8");
        byte[] md5Bytes = sha.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }

    public static void main(String[] args) throws Exception {
        String noncestr = "Y7a8KkqX041bsSwT";
        String jsapi_ticket = "617bf955832a4d4d80d9d8d85917a427";
        String timestamp = "1510045655000";
        String url = "https://m.haiwainet.cn/ttc/3541093/2018/0509/content_31312407_1.html?a=b&c=d";

        System.out.println(shaEncode("jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url));
    }
}

```

## Step4 配置 H5 可信域名

前往 [开发者后台](https://open-xz.wps.cn/admin/app)

1. 在开发者管理中，点击「安全设置」。
   ![](https://cloudcdn.qwps.cn/open/_img/361419cf2d.png)
2. 在「H5 可信域名」中添加需要调用 config 接口的页面所在域名。例如：完整 URL 是 http://www.xiezuo.com?type=xxxx， 则配置 http://www.xiezuo.com。
   ![](https://cloudcdn.qwps.cn/open/_img/e1fb18fa2b.png)
   <br/>

## Step5 调用获取 config 接口鉴权

完成以上步骤后，应用方服务端将数据传递给应用方前端。包括以下参数：

| 参数      | 参数说明                   |
| :-------- | :------------------------- |
| appID     | 鉴权的应用 ID              |
| timeStamp | 生成签名时用到的时间戳     |
| nonceStr  | 生成签名时用到的随机字符串 |
| signature | 生成的签名                 |

业务方前端拿到数据后，在所有需要使用 JSAPI 的页面必须先注入配置信息，否则将无法调用。针对单页面应用，鉴权需要对父页面实现。

```js
window.ksoxz_sdk.config({
  params: {
    appId: "", // 必填，应用ID
    timeStamp: 0, // 必填，生成签名的时间戳，毫秒级
    nonceStr: "", // 必填，生成签名的随机串
    signature: "", // 必填，签名
  },
  onSuccess: function () {
    // 成功回调
  },
  onError: function () {
    // 失败回调
  },
});
```
