提交 40c3aa26 编写于 作者: DCloud_iOS_XHY's avatar DCloud_iOS_XHY

Merge branch 'master' of https://gitcode.net/dcloud/unidocs-zh

* 'master' of https://gitcode.net/dcloud/unidocs-zh:
  ad-rewarded-video:更新对微信小程序的支持条件
  Update uni-app.js
  docs: i18n
  Update app-privacy-android.md
  docs: update uni-id-pages.md
  docs: Vue2 component is
  Update uts-for-android.md
......@@ -55,11 +55,6 @@ export default {
"state": 1,
"prefix": "群5"
},
{
"number": "697264024",
"state": 1,
"prefix": "群6"
},
{
"number": "942061423",
"state": 1,
......@@ -125,6 +120,11 @@ export default {
"state": 1,
"prefix": "群19"
},
{
"number": "165796402",
"state": 1,
"prefix": "群20"
},
{
"number": "717019120",
"state": 1,
......@@ -201,10 +201,10 @@ export default {
"prefix": "群35"
},
{
"number": "165796402",
"number": "697264024",
"state": 0,
"prefix": "20"
"prefix": "6"
}
],
joinQQGroupHref: 'https://qm.qq.com/cgi-bin/qm/qr?k=H-U40nzIXq_F6NAWUOXBjvynZi2DsdOw&jump_from=webapi&authKey=oPGPBZDJhx60tm34ShKL4TNkH1zAfM1q+OXn6Tm9cN9nauPNUpfG8OMN0LJYdASG'
joinQQGroupHref: 'https://qm.qq.com/cgi-bin/qm/qr?k=chULlrdn_8TYl-hiUBjDIbs-Roah154g&jump_from=webapi&authKey=9hY3Mo3bVUSG/BWh8Sr0am+e9mQQO42vy818QYviVNG95hQr2ER/JEd1/0sP+dvd'
}
......@@ -428,9 +428,7 @@ export default {
#### 接入流程
1. 更新依赖库支持
- 如果项目使用了 [uni-id](/uniCloud/uni-id-summary.html) 需要更新到 3.3.29+
- 如果项目使用了 [uni-id-co](/uniCloud/uni-id-summary.html#save-user-token) 需要更新到 1.0.8+
1. 项目使用了 [uni-id-co](/uniCloud/uni-id-summary.html#save-user-token) 并更新到 1.0.8+
2. 使用 [uni-open-bridge](/uniCloud/uni-open-bridge.html) 托管三方开放平台数据
### Q&A
......@@ -459,7 +457,7 @@ A:
字段名称|说明|字段类型|备注|
:-|:-|:-|:-|
isValid|校验结果|Blean|判定结果,是否发放奖励|
isValid|校验结果|Blean|判定结果,是否发放奖励,具体发放奖励由用户自己的业务系统决定|
示例
```js
......@@ -468,11 +466,17 @@ exports.main = async (event, context) => {
console.log('event : ', event);
return {
"isValid": true
"isValid": true // 将结果返给广告商服务器,暂不支持在客户端获取此返回结果,可以先将结果报错到数据,由客户端发送请求到服务器查询确认
}
};
```
注意事项
1. 看一次广告收到2次回调结果,且 `trans_id` 相同,产生2次的可能原因有
> 1.1 在云函数里没有向广告商的请求返回 isValid: true
> 1.2 服务器响应过慢
#### 用户云函数详细说明
1. 如果业务使用了uniCloud,可以直接在云函数内部处理
......
......@@ -8,7 +8,7 @@
|App|H5 |微信小程序 |支付宝小程序 |百度小程序 |字节跳动小程序、飞书小程序 |QQ小程序 |快应用 |360小程序 |快手小程序|京东小程序|
|:-:|:-:|:-: |:-: |:-: |:-: |:-: |:-: |:-: |:-: |:-: |
|√ |√ |x |x |x |x |x |x |x |x |x |
|√ (Vue2 需传入 String 类型) |√ |x |x |x |x |x |x |x |x |x |
......
......@@ -779,6 +779,7 @@ uts中,需要区分全局方法、成员方法
|---|-------|---|
|继承类|:|extends|
|实现type接口|:|extends|
|实现接口|:|implements|
```kotlin
......
......@@ -67,7 +67,7 @@
"support": false,
"loadNativePlugins": false,
"visitorEntry": true,
"showAlways": true
"showAlways": false
},
"styles": {
"backgroundColor": "#00FF00",
......@@ -108,7 +108,7 @@
+ support 用户拒绝隐私协议后,是否直接进入游客模式,默认关闭
+ loadNativePlugins 游客模式下,是否加载原生插件
+ visitorEntry HBuilderX 3.6.7 版本后支持,默认false,当设置为true,隐私协议弹窗 会出现 游客模式 按钮
+ showAlways HBuilderX 3.6.10 版本后支持,默认true 标记用户拒绝协议后,下次启动是否继续弹出
+ showAlways HBuilderX 3.6.10 版本后支持,默认false 标记用户拒绝协议后,下次启动是否继续弹出,默认false,拒绝后不弹出
- second
配置二次确认提示框显示内容,message属性值不为空时弹出二次确认提示框
+ title 二次确认提示框上的标题
......
......@@ -32,7 +32,7 @@ let i18nConfig = {
// VUE2
// #ifndef VUE3
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import VueI18n from 'vue-i18n'// v8.x
Vue.use(VueI18n)
const i18n = new VueI18n(i18nConfig)
Vue.config.productionTip = false
......@@ -47,7 +47,7 @@ app.$mount()
// VUE3
// #ifdef VUE3
import { createSSRApp } from 'vue'
import { createI18n } from 'vue-i18n'
import { createI18n } from 'vue-i18n'// v9.x
const i18n = createI18n(i18nConfig)
export function createApp() {
const app = createSSRApp(App)
......
......@@ -162,7 +162,7 @@
| -- | -- | -- |-- |-- | -- |
|key | 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes [详情](https://v2.cn.vuejs.org/v2/api/#key) |√ | √ | √ | |
|ref | ref 被用来给元素或子组件注册引用信息 [详情](https://v2.cn.vuejs.org/v2/api/#ref) |√ | √ | √ |非 H5 平台只能获取 vue 组件实例不能获取到内置组件实例|
|is | 用于动态组件且基于 DOM 内模板的限制来工作 [详情](https://v2.cn.vuejs.org/v2/api/#is) |√ | √ | x | - |
|is | 用于动态组件且基于 DOM 内模板的限制来工作 [详情](https://v2.cn.vuejs.org/v2/api/#is) |√ | √ (需传入 String 类型) | x | - |
......
......@@ -382,10 +382,16 @@ const uniIdCo = uniCloud.importObject('uni-id-co')
|字段|说明|
|--|--|
|clientInfo|客户端信息; `uni.getSystemInfo`返回的字段|
|clientInfo|客户端信息; [uni.getSystemInfo](/api/system/info.md#getsysteminfo)返回的字段|
|uniIdToken|用户Token; 用户登录后必填|
|params|API接口参数字段|
如果是在 uni-app 之外的应用中调用 URL 化接口,请确保clientInfo中存在以下字段:
|字段|说明|
|--|--|
|uniPlatform|应用运行平台,与条件编译平台相同。[详见](/api/system/info.md#uniplatform)|
|appId|manifest 中应用appid,即DCloud appid。如没有请手动指定一个,需确保唯一性。|
假设已在uniCloud 控制台已设置URL化域名PATH,以PATH为`/http/uni-id-co`为例,演示登录示例:
```javascript
......@@ -1842,6 +1848,77 @@ await uniIdCo.setAuthorizedApp({
- 此接口为管理端接口
- 仅在用户token即将过期时返回新newToken
### 外部系统联登@external
#### 注册用户@external-register
外部用户注册,将自身系统的用户账号导入uniId,为其创建一个对应uniId的账号(unieid),使得该账号可以使用依赖uniId的系统及功能。
注册成功后,uni-id 返回 unieid 与 用户 token ,请务必在自身系统中维护好 unieid 与 token。
该接口使用URL方式调用时,需要携带鉴权签名值,查看[URL化请求鉴权签名计算](uni-id-pages.md#http-reqeust-auth)
**接口形式**
```js
await uniIdCo.externalRegister({
unieid,
nickname,
avatar,
gender
})
```
**参数说明**
|参数名 |类型 |必填 |说明 |
|-- |-- |-- |-- |
|unieid |string |是 |uni-id账号,必须保证唯一性。只允许使用数字、字母、“_”及“-”,但不能为纯数字。 |
|nickname |string|否 |用户昵称 |
|avatar |string|否 |用户头像 |
|gender |string|否 |用户性别;0 未知 1 男性 2 女性 |
**返回值**
|参数名 |类型 |说明 |
|-- |-- |-- |
|errCode |string|number |错误码 |
|errMsg |string |错误信息 |
|newToken |object |token信息 |
| |- token |string |token |
| |- tokenExpired|string |token过期时间 |
|unieid |string |用户id |
#### 用户登录@external-login
外部用户登录,用于获取用户token
该接口使用URL方式调用时,需要携带鉴权签名值,查看[URL化请求鉴权签名计算](uni-id-pages.md#http-reqeust-auth)
**接口形式**
```js
await uniIdCo.externalLogin({
unieid
})
```
**参数说明**
|参数名 |类型 |必填 |说明 |
|-- |-- |-- |-- |
|unieid |string |是 |uni-id账号,必须保证唯一性。只允许使用数字、字母、“_”及“-”,但不能为纯数字。 |
**返回值**
|参数名 |类型 |说明 |
|-- |-- |-- |
|errCode |string|number |错误码 |
|errMsg |string |错误信息 |
|newToken |object |token信息 |
| |- token |string |token |
| |- tokenExpired|string |token过期时间 |
|unieid |string |用户id |
### 其他功能@extra-function
### 覆盖或新增校验规则@custom-validator
......@@ -2054,3 +2131,327 @@ exports.main = async (event, context) => {
};
```
## URL化请求鉴权签名@http-reqeust-auth
uni-id 在URL化请求时,会对以下 API 进行调用鉴权验证,在调用 API 时,开发者需要使用请求鉴权密钥`requestAuthSecret`按照 uni-id 的约定方式对请求中的关键数据进行签名值计算,并将签名值添加到Header请求头的 `uni-id-signature` 参数中传给 uni-id 进行签名验证,uni-id 会对接收到数据进行签名值计算,并与接收到的请求签名值进行比对,如果签名值不一致,则视为无限签名,将拒绝本次请求。
需要进行签名的API列表
|API|
|---|
|externalRegister|
|externalLogin|
### 请求头公共参数
|参数名称|类型|是否必须|描述|
|---|---|---|---|
|uni-id-nonce|string|是|随机字符串|
|uni-id-timestamp|string|是|当前时间戳; 单位毫秒|
|uni-id-signature|string|是|请求鉴权签名; 签名算法见下|
### 鉴权签名算法
1. 将API请求参数(只包括请求body中的params参数,但除去array与object类型的参数),根据参数名称的ASCII码表的顺序排序。如:`foo:1, bar:2, foo_bar:3, foobar:4`排序后的顺序是 `bar:2, foo:1, foo_bar:3, foobar:4`
2. 将排序好的参数名和参数值按照 `key1=value1&key2=value2` 格式拼装在一起,根据上面的示例得到的结果为:`bar=2&foo=1&foo_bar=3&foobar=4`
3. 把拼装好的字符串采用utf-8编码,开发者使用请求鉴权密钥与随机串对时间戳与待签名字符串进行 HmacSHA256 加密处理,计算得出请求签名值,如:`HmacSHA256(timestamp + bar=2&foo=1&foo_bar=3&foobar=4, requestAuthSecret + nonce)`
4. 将加密得到的二进制结果使用十六进制表示,值必须为大写,如:`Hex.stringify(Utf8.parse("helloworld")) = "68656C6C6F776F726C64"`
### 签名值计算示例
#### Nodejs
```javascript
const crypto = require('crypto')
function getSignature (params, nonce, timestamp) {
const paramsStr = Object.keys(params)
.sort()
.filter(item => typeof params[item] !== "object")
.map(item => `${item}=${params[item]}`)
.join('&')
const signature = crypto.createHmac('sha256', `${requestAuthSecret}${nonce}`).update(`${timestamp}${paramsStr}`).digest('hex')
return signature.toUpperCase()
}
const requestAuthSecret = "testSecret"
const nonce = Math.random().toString(36).substring(2)
const timestamp = Date.now()
const params = {
foo: 1,
bar: 2,
foo_bar: 3,
foobar: 4
}
const signature = getSignature(params, nonce, timestamp)
console.log("nonce: ", nonce)
console.log("timestamp: ", timestamp)
console.log("signature: ", signature)
```
#### PHP
```php
<?php
class Sign {
private $requestAuthSecret;
public function __construct ($requestAuthSecret) {
$this->requestAuthSecret = $requestAuthSecret;
}
public function getSignature ($params, $nonce, $timestamp) {
$paramsStr = $this->getParamsString($params);
$signature = hash_hmac('sha256', utf8_encode((string)$timestamp . $paramsStr), utf8_encode($this->requestAuthSecret . $nonce));
return strtoupper($signature);
}
private function getParamsString ($params) {
ksort($params);
$paramsStr = [];
foreach($params as $key=>$value){
if (gettype($value) == "array" || gettype($value) == "object") {
continue;
}
array_push($paramsStr, $key . '=' . $value);
}
return join('&', $paramsStr);
}
}
$requestAuthSecret = "testSecret";
$nonce = sprintf("%d", rand());
$timestamp = time() * 1000;
$params = [
"foo" => 1,
"bar" => 2,
"foobar" => 4,
"foo_bar" => 3,
];
$sign = new Sign($requestAuthSecret);
$signature = $sign->getSignature($params, $nonce, $timestamp);
print_r("nonce: " . $nonce . PHP_EOL);
print_r("timestamp: " . $timestamp . PHP_EOL);
print_r("signature: " . $signature);
```
#### Python
```python
import hmac
import hashlib
import time
class Sign:
def __init__(self, requestAuthSecret):
self.requestAuthSecret = requestAuthSecret
def get_signature (self, params, nonce, timestamp):
params_str = self.get_params_string(params)
signature = hmac.new(bytes("%s%s" % (self.requestAuthSecret, nonce), 'utf-8'), bytes("%s%s" % (timestamp, params_str), 'utf-8'), digestmod = hashlib.sha256).hexdigest().upper()
return signature
def get_params_string(self, params):
params_str = []
for k in sorted(params):
if isinstance(params[k], (list, dict)):
continue
params_str.append("%s=%s" % (k, params[k]))
return "&".join(params_str)
if __name__ == "__main__":
requestAuthSecret = "testSecret"
nonce = "xxxxxxx"
timestamp = int(round(time.time() * 1000))
params = {
"foo": 1,
"bar": 2,
"foobar": 4,
"foo_bar": 3,
}
sign = Sign(requestAuthSecret)
signature = sign.get_signature(params, nonce, timestamp)
print(nonce, timestamp, signature)
```
#### Go
```golang
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"
"strconv"
"strings"
"time"
)
type Sign struct {
requestAuthSecret string
}
func (sign Sign) getSignature(params map[string]string, nonce string, timestamp int64) string {
paramsStr := sign.getParamsString(params)
mac := hmac.New(sha256.New, []byte(sign.requestAuthSecret+nonce))
mac.Write([]byte(strconv.Itoa(int(timestamp)) + paramsStr))
hexSignature := mac.Sum(nil)
signature := hex.EncodeToString(hexSignature)
return strings.ToUpper(signature)
}
func (sign Sign) getParamsString(params map[string]string) string {
keys := make([]string, 0, len(params))
for k := range params {
keys = append(keys, k)
}
sort.Strings(keys)
var paramsStr string = ""
i := 0
for _, k := range keys {
v := params[k]
if i != 0 {
paramsStr += "&"
}
paramsStr += k
paramsStr += "="
paramsStr += v
i++
}
return paramsStr
}
func main() {
requestAuthSecret := "testSecret"
nonce := "xxxxxxx"
timestamp := time.Now().UnixMilli()
params := map[string]string{
"foo": "1",
"bar": "2",
"foo_bar": "3",
"foobar": "4",
}
sign := Sign{
requestAuthSecret: requestAuthSecret,
}
signature := sign.getSignature(params, nonce, timestamp)
result := fmt.Sprintf("nonce: %s, timestamp: %d, signature: %s", nonce, timestamp, signature)
fmt.Println(result)
}
```
#### Java
```java
import java.util.HashMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.util.Map;
public class Sign {
static String requestAuthSecret = "testSecret";
public static void main(String[] args) {
String nonce = "xxxxxx";
long timestamp = System.currentTimeMillis();
Map<String, String> params = new HashMap<String, String>();
params.put("foo", "1");
params.put("bar", "2");
params.put("foo_bar", "4");
params.put("foobar", "3");
String signature = getSignature(params, nonce, timestamp);
System.out.println("nonce: " + nonce);
System.out.println("timestamp: " + timestamp);
System.out.println("signature: " + signature);
}
public static String getSignature (Map<String, String> params, String nonce, long timestamp) {
String paramsStr = getParamsString(params);
String algorithm = "HmacSHA256";
Mac hmacSha256;
String digestHexString = null;
try {
hmacSha256 = Mac.getInstance(algorithm);
String key = new StringBuilder().append(requestAuthSecret).append(nonce).toString();
String message = new StringBuilder().append(Long.toString(timestamp)).append(paramsStr).toString();
byte[] keyBytes = key.getBytes("utf-8");
byte[] messageBytes = message.getBytes("utf-8");
hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, algorithm));
byte[] digestBytes = hmacSha256.doFinal(messageBytes);
digestHexString = byteArrayToHexString(digestBytes);
} catch (Exception e) {
System.out.println("[ERROR] not supposed to happen: " + e.getMessage());
}
return digestHexString.toUpperCase();
}
private static String getParamsString (Map<String, String> params) {
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keys.length; i ++) {
String key = keys[i];
if (i != 0) {
sb.append("&");
}
sb.append(key).append("=").append(params.get(key));
}
return sb.toString();
}
private static String byteArrayToHexString(byte[] b) {
StringBuilder hs = new StringBuilder();
String stmp;
for (int n = 0; b!=null && n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0XFF);
if (stmp.length() == 1)
hs.append('0');
hs.append(stmp);
}
return hs.toString();
}
}
```
......@@ -201,6 +201,7 @@ uni-id的云端配置文件在`uniCloud/cloudfunctions/common/uni-config-center/
], // 数据库中password字段是加密存储的,这里的passwordSecret即为加密密码所用的加密算法,详见[密码安全]
"passwordStrength": "medium", // 密码强度,新增于 uni-id-pages 1.0.8版本,见下方说明
"tokenSecret": "", // 生成token所用的密钥,注意修改为自己的,使用一个较长的字符串即可
"requestAuthSecret": "", // URL化请求鉴权签名密钥
"tokenExpiresIn": 7200, // 全平台token过期时间,未指定过期时间的平台会使用此值
"tokenExpiresThreshold": 3600, // 新增于uni-id 1.1.7版本,checkToken时如果token有效期小于此值且在有效期内则自动获取新token,请注意将新token返回给前端保存(云对象会自动保存符合uniCloud响应体规范的响应内的新token),如果不配置此参数则不开启自动获取新token功能
"passwordErrorLimit": 6, // 密码错误最大重试次数
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册