---
title: 用户体系
summary: 介绍涂鸦小程序的用户体系和登录流程，包括获取登录凭证、服务端对接及完整的代码示例。
questions:
  - 小程序如何获取App的用户身份标识？
  - 小程序登录的流程是怎样的？
  - 如何使用ty.login()获取临时登录凭证？
  - 服务器端需要调用哪个API来获取access_token？
  - 使用小程序登录前需要做什么配置？
  - 如何将小程序与云项目进行关联？
  - 小程序的登录状态如何管理？
  - Ray开发和原生开发的登录代码有什么区别？
---

### 用户体系
在涂鸦系的 App 内打开小程序，默认使用 App 的用户体系。 如果开发者需要在小程序业务获取用户信息，并在用户维度处理自己的业务，
那么就需要使用小程序登录能力来对接。

### 小程序登录

涂鸦官方提供了登录能力，开发者可以通过相关 API 获取 App 的用户身份标识，快速的建立小程序内的用户体系。

### 登录流程

<Image
  src="/images/guide/login/login-zh.png"
  style={{
    borderRadius: '10px',
    padding: 24,
    boxShadow: '0 0 5px rgba(0,0,0,0.3)',
  }}
/>

#### 说明

1. 需要调用 `ty.login()` 获取 `临时登录凭证 code`，并将 code 传到开发者服务器
2. 开发者服务器调用涂鸦云开发能力 API `/1.0/token`，传入 code，获取 access_token 及 uid，然后开发者根据 aceess_token 及 uid 自定义登录状态，并返回给前端，然后进行后续的交互。

> **<font style={{color: 'red'}}>注意：在开发小程序前，请先确保已经将小程序与云项目进行关联，关联方法请参考下面的服务器开发。</font>**

### 服务器开发

开发者服务器端要调用涂鸦云开发能力 API，需要创建云项目，并需要将云项目应用与小程序进行关联。

1. 使用 涂鸦开发者平台账号登录到 [涂鸦开发者平台](https://iot.tuya.com/)，选择云开发菜单，点击创建云项目

<Image
  src="/images/guide/login/create-cloud.png"
  style={{
    borderRadius: '10px',
    padding: 24,
    boxShadow: '0 0 5px rgba(0,0,0,0.3)',
  }}
/>

2. 创建成功后，即在云项目的详细页面中查询获取 Client ID 及 Client secret，用于请求 API 签名

<Image
  src="/images/guide/login/cloud-detail.png"
  style={{
    borderRadius: '10px',
    padding: 24,
    boxShadow: '0 0 5px rgba(0,0,0,0.3)',
  }}
/>

3. 到小程序开发者平台，进入小程序`开发设置`菜单，点击云项目模块的 `添加` 按钮，然后选择对应的云项目关联。

<Image
  src="/images/guide/login/cloud-setting.png"
  style={{
    borderRadius: '10px',
    padding: 24,
    boxShadow: '0 0 5px rgba(0,0,0,0.3)',
  }}
/>

<Image
  src="/images/guide/login/cloud-setting1.png"
  style={{
    borderRadius: '10px',
    padding: 24,
    boxShadow: '0 0 5px rgba(0,0,0,0.3)',
  }}
/>

#### 相关文档

1. 云开发开发文档，可参考[这里](https://developer.tuya.com/cn/docs/iot/introduction-to-tuya-iot-platform?id=Ka6vijvqb3uhn)文档。
2. API 签名机制，可参考[这里](https://developer.tuya.com/cn/docs/iot/singnature?id=Ka43a5mtx1gsc)
3. ty.login API定义，可参考[这里](/cn/miniapp/develop/miniapp/api/login/login)

### 代码示例

#### Ray 开发示例

```js | pure
import {
  login,
  getUserInfo,
  request,
  setStorageSync,
  showModal,
} from '@ray-js/ray';

const { code } = await login({});

const { nickName, avatarUrl } = await new Promise((resolve, reject) =>
  getUserInfo({ success: resolve, fail: reject })
);

request({
  url: 'https://www.xxx.com/login',
  method: 'POST',
  header: {
    'Content-Type': 'application/json',
  },
  data: JSON.stringify({
    code,
    nickName,
    avatarUrl,
  }),
  success: ({ data }: any) => {
    setStorageSync({ key: 'session', data });
  },
  fail: () => {
    showModal({
      title: '提示',
      content: '登录失败',
      showCancel: false,
    });
  },
});
```

#### 原生小程序开发示例

```js | pure
ty.login({
  success: ({ code }) => {
    ty.getUserInfo({
      success: ({ nickName, avatarUrl }) => {
        ty.request({
          url: 'https://www.xxx.com/login',
          method: 'POST',
          header: {
            'Content-Type': 'application/json',
          },
          data: JSON.stringify({
            code,
            nickName,
            avatarUrl,
          }),
          success: ({ data }: any) => {
            ty.setStorageSync({
              key: 'session',
              data,
            });
          },
          fail: (err) => {
            console.log('err', err);
            ty.showModal({
              title: '提示',
              content: '登录失败',
              showCancel: false,
            });
          },
        });
      },
      fail: (err) => {
        console.log('getUserInfo err', err);
      },
    });
  },
});
```

#### 服务器端代码（这里使用 Nodejs Koa 实现）

```js | pure
import Koa from 'koa';
import KoaRouter from 'koa-router';
import crypto from 'crypto';
import axios from 'axios';
import bodyParser from 'koa-bodyparser';

const app = new Koa();
const router = new KoaRouter();
const accessKey = '云开发 Client ID';
const secretKey = '云开发 Client Secret';

router.post('/login', async (ctx) => {
  const { code, nickName, avatarUrl } = ctx.request.body;

  const path = `/v1.0/token?code=${code}&grant_type=2`;
  const contentHash = crypto.createHash('sha256').update('').digest('hex');
  const t = +new Date();
  const nonce = '';
  const stringToSign = ['GET', contentHash, '', path].join('\n');
  const signStr = [accessKey, t, nonce, stringToSign].join('');
  const sign = crypto
    .createHmac('sha256', secretKey)
    .update(signStr, 'utf8')
    .digest('hex')
    .toUpperCase();

  const { data } = await axios({
    // 不同的地区需要使用不同的域名地址，这里为中国区地址
    url: `https://openapi.tuyacn.com/v1.0/token?code=${code}&grant_type=2`,
    method: 'GET',
    headers: {
      t,
      sign,
      client_id: accessKey,
      sign_method: 'HMAC-SHA256',
      Dev_lang: 'Nodejs',
      'Signature-Headers': '',
    },
  });

  let responseData;
  if (data.success) {
    responseData = {
      success: true,
      result: {
        sessionId: '1234567890', // 服务端自己生成一个唯一id，用于管理小程序登录状态
      },
    };
  } else {
    responseData = {
      success: false,
      errMsg: data.msg,
      errCode: data.code,
    };
  }

  ctx.body = responseData;
});

app.use(bodyParser()).use(router.allowedMethods()).use(router.routes());

app.listen(3000, () => {
  console.log('Server start on http://localhost:3000');
});
```
