# 网页 API 权限warning
该文档已停止维护，相关内容参考[步骤二（可选）：鉴权调用 JSAPI](https://open.feishu.cn/document/uYjL24iN/uEzM4YjLxMDO24SMzgjN)。

如果你的网页（Web）应用需要调用 [JSAPI](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/)，为了数据安全，飞书需要先验证网页应用有权限访问的 JSAPI 范围，此过程称为鉴权。

除了 [requestAuthCode](https://open.feishu.cn/document/uYjL24iN/uUzMuUzMuUzM/20220308)、[closeWindow](https://open.feishu.cn/document/uYjL24iN/uYTOuYTOuYTO/closewindow) API ，其它所有调用 JSAPI 的页面均需要先鉴权再调用。

## 鉴权流程
网页应用的鉴权是从应用页面发起的，如果前端页面调用需要鉴权的 JSAPI 方法，则在页面加载时需要向服务端发起请求，获取鉴权参数（appId、timestamp、nonceStr、signature）。通过这些参数，前端页面调用 config 接口进行鉴权，鉴权成功后即可调用 JSAPI。
* 接入方前端调用 JSAPI 前的鉴权过程需要接入方服务端配合。建议你合理安排前端和服务端开发人员共同参与。
* 接入方前端只能调用 JSAPI，如果你需要调用服务端 API，请在接入方服务端调用，参考 [如何调用服务端API](https://open.feishu.cn/document/ukTMukTMukTM/uITNz4iM1MjLyUzM)。

![图片](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/0c0d6bbe872c4c89b6eb633f9ec9089d~tplv-goo7wpa0wc-image.image?height=811&lazyload=true&maxWidth=800&width=1445)

## 鉴权示例代码

我们提供了可供本地调试的 [客户端网页应用鉴权示例Demo](https://sf3-cn.feishucdn.com/obj/open-platform-opendoc/8e80b6864660587b68d2b235d18f7ba5_YVXf93orWM.zip)，该 Demo 基于 Python3 环境和 Flask 框架编写。

## **项目结构**
项目目录的核心代码文件如下：
```XML
｜── README.zh.md     ----- 说明文档
｜── public
｜   ｜── svg     ----- 前端图形文件
｜   ｜── index.css     ----- 前端展示样式
｜   ｜── index.js     ----- 前端交互代码
｜── templates
｜   ｜── index.html     ----- 前端用户信息展示页面
｜── auth.py     ----- 服务端获取jsapi_ticket等
｜── server.py     ----- 服务端核心业务代码
｜── requirements.txt     ----- 环境配置文件
└── .env     ----- 全局默认配置文件，主要存储App ID和App Secret等
```

* **public 和 templates**：前端模块，主要功能为调用 JSAPI 获取用户信息和展示用户信息。
* **其他**：服务端模块，使用 [Flask](https://dormousehole.readthedocs.io/en/latest/) 构建，主要功能为：
   * 使用 App ID 和 App Secret 获取 tenant_access_token。
   * 使用 tenant_access_token 获取 jsapi_ticket。
   * 使用 jsapi_ticket、随机字符串、当前时间戳、当前鉴权的网页 URL 生成签名 signature。
详细代码的使用说明，请参考[网页应用开发指南](https://open.feishu.cn/document/uYjL24iN/uEzM4YjLxMDO24SMzgjN)。

## 网页应用鉴权策略

为了兼顾安全性和便捷性，网页应用的鉴权策略经过了多个版本的迭代。每个版本的鉴权策略如下：

### **容器级别的鉴权策略（v4.0 >飞书版本）**
网页容器简单来说就是网页所在的环境，从客户端视角来看：
* Android 端：一个 Activity 对应一个容器。
* iOS 端：一个 ViewController 对应一个容器。
鉴权信息作用域为网页应用容器，即容器内只需要鉴权一次，便可在应用容器的生命周期内有效。飞书 V4.0 之前使用的是该策略。

### **URL 级别的鉴权策略（v5.1 >飞书版本 >=  v4.0）**
鉴权信息作用域为 URL，URL 改变后需要重新鉴权。飞书 V4.0 至 V5.0 使用的是该策略。

### **染色级别的鉴权策略（飞书版本 >= v5.1 ）**
在 URL 的基础上，增加了染色机制，即只要在当前页面的回退页面栈中出现过当前页面的父路径，且该父路径鉴权通过包含应用鉴权信息，则该页面就可以被染上同样的应用鉴权信息。飞书 V5.1 之后使用的是该策略。

## **鉴权示例**
### **示例一**
假设打开 pathA 后获得了应用鉴权信息 a1，接着打开 pathB 的鉴权情况如下。
* pathA 指 http://open.feishu.cn/home
* pathB 指 http://open.feishu.cn/detail

| **鉴权策略** | **打开 pathA** | **打开 pathB** |
| --- | --- | --- |
| 容器级别 | 获得鉴权信息 a1 | 仍然获得鉴权信息 a1。 <br> 在同一个容器生命周期内，因此仍然可以获得鉴权信息。 <br>  |
| URL 级别 | 获得鉴权信息 a1 | 不能获得鉴权，需要重新对页面鉴权。 <br> 因为页面 URL 发生了变化，因此需要重新鉴权。 <br>  |
| 染色级别 | 获得鉴权信息 a1 | 不能获得鉴权，需要重新对页面鉴权。 <br> 因为 pathB 的回退页面栈中不包含 pathB 的父路径，不满足染色条件，因此需要重新鉴权。 <br>  |

### **示例二**
假设打开 pathA 后获得了应用鉴权信息 a1，接着打开 pathC 的鉴权情况如下：
* pathA 指 http://open.feishu.cn/home
* pathC 指 http://open.feishu.cn/home/route

| **鉴权策略** | **打开 pathA** | **打开 pathC** |
| --- | --- | --- |
| 容器级别 | 获得鉴权信息 a1 | 仍然获得鉴权信息 a1。 <br> 在同一个容器生命周期内，因此仍然可以获得鉴权信息。 <br>  |
| URL 级别 | 获得鉴权信息 a1 | 不能获得鉴权，需要重新对页面鉴权。 <br>因为页面 URL 发生了变化，因此需要重新鉴权。 <br>  |
| 染色级别 | 获得鉴权信息 a1 | 仍然获得鉴权信息 a1。 <br> 因为 pathC 的回退页面栈中包含 pathC 的父路径 pathA，因此仍然可以获得 pathA 的鉴权信息。 <br>  |warning
所有鉴权策略均需**确认容器是否唯一**，即是否在网页应用内是否又开启了新容器。若开启了新容器，则需重新调用 config 接口进行鉴权。

开启新的网页容器代表在客户端上新打开了一个网页容器来承载网页展现，即网页所在的环境与之前不一致。常见的开启新容器的场景有：

* 使用 [openSchema](https://open.feishu.cn/document/uAjLw4CM/uYjL24iN/block/api/navigator/openschema) 方式打开新链接 ：openSchema 是一个 JSAPI，用于在新窗口打开网页、文档、小程序等。
* 使用 [AppLink](https://open.feishu.cn/document/uYjL24iN/ucjN1UjL3YTN14yN2UTN) 打开新链接：AppLink 是一个 URL 协议，用于打开飞书或者其中的一个功能。
* 前端通过 `window.open(xxx , '_self')` 打开新页面时，iOS 端按照浏览器标准实现新容器的开启，Android 端则是在旧的网页容器中渲染页面，不会开启新容器。`window.open` 是 JavaScript 函数，用于打开一个新窗口或改变原窗口，`_self`表示在原窗口打开。

