欧美三级国产三级日韩三级_亚洲熟妇丰满大屁股熟妇_欧美亚洲成人一区二区三区_国产精品久久久久久模特

微信小程序登錄鑒權(quán)與獲取用戶信息 - 新聞資訊 - 云南小程序開發(fā)|云南軟件開發(fā)|云南網(wǎng)站建設(shè)-昆明葵宇信息科技有限公司

159-8711-8523

云南網(wǎng)建設(shè)/小程序開發(fā)/軟件開發(fā)

知識

不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運(yùn)營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實(shí)提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序?yàn)楹笃谏壧峁┍憬莸闹С郑?

您當(dāng)前位置>首頁 » 新聞資訊 » 小程序相關(guān) >

微信小程序登錄鑒權(quán)與獲取用戶信息

發(fā)表時間:2021-3-15

發(fā)布人:葵宇科技

瀏覽次數(shù):286

前言

在小程序中,與云開發(fā)相比,傳統(tǒng)的前后端開發(fā)在登錄鑒權(quán)的實(shí)現(xiàn)方面相對來說更加復(fù)雜,不僅需要前端和后端的交互,后端還需要與微信接口服務(wù)進(jìn)行交互,以完成整個鑒權(quán)流程:

整個流程簡單來說分為以下7步:

  1. 前端調(diào)用wx.login()獲取臨時登錄憑證code,并回傳到開發(fā)者服務(wù)器。
  2. 服務(wù)器調(diào)用auth.code2Session換取用戶唯一標(biāo)識OpenID和會話密鑰session_key。
  3. 服務(wù)器端根據(jù)OpenIDsession_key生成自定義登錄態(tài)(可以理解為是token),將token響應(yīng)給前端。
  4. 前端將token存入Storage中。
  5. 當(dāng)前端之后向后端發(fā)起請求時,就會帶上token
  6. 后臺通過token(或者其他類型密鑰),解密獲取OpenID,判斷是哪個用戶的行為,做出響應(yīng)的邏輯處理(比如操作數(shù)據(jù)庫等)。
  7. 后臺響應(yīng)數(shù)據(jù)給前端。

這個是小程序登錄的流程,但是小程序登錄和小程序獲取用戶信息并不是一回事。小程序登錄的APIwx.login,可以獲取用戶的openIDopenID是用戶的唯一標(biāo)識,是比較隱私的數(shù)據(jù),一般不會返回給前端。小程序獲取用戶信息的APIwx.getUserInfo,它可以獲取用戶的一些基本信息,比如nickName、avatarUrl等。兩者不要弄混。

其實(shí)微信登錄一開始并不是這樣的,以往微信小程序在用戶沒有任何操作的情況下就會直接彈出授權(quán)的登錄方式,如果用戶點(diǎn)擊拒絕授權(quán),則無法使用小程序。按照微信官方對這個功能更新的解釋是:

 

因此,微信對開發(fā)的建議是:

  1. 當(dāng)用戶打開小程序時訪問第一個頁面時,先通過 wx.login,獲取用戶 openID 。這時無需彈框授權(quán),開發(fā)者拿到 openID 可以建立自身的帳號 ID
  2. 在第一步中,拿到 openID 后,判斷是新用戶還是老用戶。如果是老用戶,可以直接登錄;如果是新用戶,可先在小程序首頁展示你的信息服務(wù),讓用戶對這個小程序有大概的了解,再引導(dǎo)用戶進(jìn)行下一步的操作。
  3. 當(dāng)需要獲取用戶頭像昵稱的時候,對用戶展示一個登錄頁面,這個頁面只有一個最重要的操作,引導(dǎo)用戶進(jìn)行登錄。

小程序登錄

在上一節(jié)中有提到,小程序登錄可以分為7個步驟,下面就詳細(xì)講一下7個步驟具體是如何實(shí)現(xiàn)。

step1: 前端調(diào)用wx.login()獲取臨時登錄憑證code

在項(xiàng)目中,我使用了Taro框架,所以調(diào)用的API對應(yīng)為Taro.login()

Taro.login({})
      .then((res) => {
        if (res.code) {
          // 將code發(fā)送到后臺,以獲取token
          getToken(res.code)
            .then((res: any) => {
              const { token, userExist } = res;

              // 將token存儲到Storage中
              Taro.setStorageSync('token', token);

              // 如果是老用戶,獲取用戶信息
              if(userExist) {
                const { userInfo } = res;
                Taro.setStorageSync('userInfo', userInfo);
              }
            })
            .catch((err) => {
              console.error(err);
            });
        } else {
          console.log('登錄失敗! ' + res.errMsg);
        }
      })
      .catch((err) => {
        console.error(err);
      });
復(fù)制代碼

step2: 服務(wù)端調(diào)用auth.code2Session換取openid和session_key

服務(wù)端調(diào)用外部接口需要使用egg.js中的一個apithis.ctx.curl,因?yàn)槭钱惒秸埱?,所以需要加?code>await:

// app/controller/home.js
// login接口
async login() {
    const { ctx } = this;
    const { code } = ctx.request.body;
    // 服務(wù)器根據(jù)客戶端傳來的code向微信接口服務(wù)獲取session_key和openid
    const res = await ctx.curl(
        `https://api.weixin.qq.com/sns/jscode2session?		
    appid=wx6936c18b38186cf3&secret=d11f77fb7d5a959b6ba46c30dbd4da95&js_code=${code}&grant_type=authorization_code`,
        {
        	dataType: 'json',
        }
    );
    const { openid } = res.data;  // 獲取到openid
}
復(fù)制代碼

step3: 根據(jù)openid生成自定義登錄態(tài)token,響應(yīng)給前端

因?yàn)?code>openid是用戶的唯一標(biāo)識,根據(jù)它生成token,響應(yīng)給前端后,前端每次發(fā)請求帶上token,后臺解密請求中的token,獲取到openid,便能識別這是哪個用戶的請求行為。

這里我們使用jwt來生成自定義登錄態(tài)token,使用jwt-simple庫來生成jwt

const jwt = require('jwt-simple');
const SECRET = 'zhuoran'; // 自定義

async login() {
    ...
    const { openid } = res.data;  // 獲取到openid
    
    // 根據(jù)用戶的openid生成token
    const token = jwt.encode(openid, SECRET);
    
    // 將token返回
    ctx.body = {
      token: token,
      ...
    };
    ...
}
復(fù)制代碼

step4: 前端將token存入storage

// 將token存儲到Storage中
Taro.setStorageSync('token', token);
復(fù)制代碼

并在每次請求時帶上token,將token放在請求頭的Authorization字段里面:

const option = {
    url: BASE_URL + url,
    data: data,
    method: method,
    header: {
        'content-type': contentType,
        Authorization: Taro.getStorageSync('token'),
    },
};
Taro.request(option);
復(fù)制代碼

這樣前端之后向后端發(fā)起請求,都會帶上token。

step5: 后臺解密token獲取openid

在后臺解密token獲取openid之后,便能知道這是哪個用戶的請求,執(zhí)行響應(yīng)的操作:

async request() {
    const { ctx } = this;

    // 從請求頭的authorization字段獲取token
    const token = ctx.get('authorization');

    // 對token進(jìn)行解密獲取其中的openid
    const openid = jwt.decode(token, SECRET);

    // 根據(jù)openid查找用戶信息
    const res = await ctx.model.User.findAll({
      where: {
        openid: openid,
      },
    });

    // 之后注意要將openid屬性去掉,私密屬性不傳回給客戶端
    ctx.body = res;
}
復(fù)制代碼

額外需要注意的點(diǎn)

保存用戶登錄態(tài),一直以來都有兩種解決方案:前端保存和后端保存。

  • 后端保存:在后端設(shè)定并存儲當(dāng)前token的過期時間,定期通知小程序前端重新登錄
  • 前端保存:因?yàn)閟ession_key存在時效性(因?yàn)橥ㄟ^session_key我們可以查看敏感信息,所以必定會有一定的時效性),而小程序前端可以通過wx.checkSession()來檢查session_key是否過期。我們可以自定義登錄態(tài),并考慮以session_key有效期作為自身登錄態(tài)有效期(也就是以session_key的到期時間作為自定義登錄態(tài)的到期時間,兩者實(shí)際上并沒有實(shí)質(zhì)聯(lián)系)。這個也是小程序文檔中推薦的方法。

因此,在項(xiàng)目中token是會過期的,一段時間沒有使用就會導(dǎo)致它過期。那怎么去檢驗(yàn)token是否過期,然后去更新它呢?

這里就需要用到前端攔截器和后端中間件。前端攔截器用來判斷token是否過期,后端中間件用于判斷請求是否攜帶token以及token是否有效。

前端攔截器

這里會使用到Taro攔截器APITaro.addInterceptor(callback)。攔截器允許我們在請求發(fā)出前或發(fā)出后做一些額外操作。

例如:

const interceptor = function (chain) {
  // 攔截請求發(fā)出前做一些額外操作
  const requestParams = chain.requestParams
  const { method, data, url } = requestParams
  
  console.log(`http ${method || 'GET'} --> ${url} data: `, data)

  return chain.proceed(requestParams)
    .then(res => {
      // 攔截請求發(fā)出后做一些額外操作
      console.log(`http <-- ${url} result:`, res)
      return res
    })
  }
Taro.addInterceptor(interceptor)
復(fù)制代碼

所以,我們可以將checkSession步驟寫在前端攔截器里,在請求每次發(fā)出之前判斷session_key是否有過期,如果過期了,則重新調(diào)用login方法,更新token,如果沒有過期,則正常發(fā)起請求:

// login方法
const login = () => {
  Taro.login({})
    .then((res) => {
      if (res.code) {
        console.log('code為' + res.code);

        // 將code和userInfo發(fā)送到后臺,以獲取token
        getToken(res.code)
          .then((token) => {
            console.log('獲取token');
            console.log(token);

            // 將token存儲到Storage中
            Taro.setStorageSync('token', token);
          })
          .catch((err) => {
            console.error(err);
          });
      } else {
        console.log('登錄失敗! ' + res.errMsg);
      }
    })
    .catch((err) => {
      console.error(err);
    });
};

// 自定義攔截器
const customInterceptor = (chain) => {
  const requestParams = chain.requestParams;

  // 獲取token
  const loginFlag = Taro.getStorageSync('token');

  // 檢查session是否過期
  Taro.checkSession({})
    .then((res) => {
      console.log(res);
      console.log('session沒過期,不用重新登錄');
      console.log('token為' + loginFlag);
    })
    .catch((err) => {
      console.log(err);
      console.log('session已過期,要重新登錄');

      // 重新登錄
      login();
    });

  return chain.proceed(requestParams).then((res) => {
    // 只要請求成功,不管返回什么狀態(tài)碼,都走這個回調(diào)
    switch (res.statusCode) {
      case HTTP_STATUS.NOT_FOUND:
        return Promise.reject('請求資源不存在');
      case HTTP_STATUS.BAD_GATEWAY:
        return Promise.reject('服務(wù)端出現(xiàn)了問題');
      case HTTP_STATUS.FORBIDDEN: {
        Taro.setStorageSync('Authorization', '');
        // pageToLogin();
        // TODO 根據(jù)自身業(yè)務(wù)修改
        return Promise.reject('沒有權(quán)限訪問');
      }
      case HTTP_STATUS.AUTHENTICATE: {
        Taro.setStorageSync('Authorization', '');
        // pageToLogin();
        return Promise.reject('需要鑒權(quán)');
      }
      case HTTP_STATUS.SUCCESS:
        return res.data;
    }
  });
};

// Taro 提供了兩個內(nèi)置攔截器
// logInterceptor - 用于打印請求的相關(guān)信息
// timeoutInterceptor - 在請求超時時拋出錯誤。
const interceptors = [
  customInterceptor,
  Taro.interceptors.logInterceptor,
];

export default interceptors;
復(fù)制代碼

后端中間件

正如之前所述,后端中間件的作用是用戶判斷請求是否攜帶token以及token是否有效的。我們在egg Node.js后端項(xiàng)目中定義校驗(yàn)token的中間件:

// app/middleware/auth.js
let jwt = require('jwt-simple');
const SECRET = 'zhuoran';

module.exports = (options) => {
  return async function auth(ctx, next) {
    const token = ctx.get('authorization');

    if (token) {
      console.log('請求帶有token');

      try {
        const openid = jwt.decode(token, SECRET);
        await next();
      } catch (err) {
        ctx.body = {
          code: 401,
          msg: 'token有誤',
        };
      }
    } else {
      console.log('請求沒有帶token');
      ctx.body = {
        code: 401,
        msg: '您沒有登錄',
      };
    }
  };
};
復(fù)制代碼

因?yàn)樾〕绦蛑?,有些功能不?qiáng)制要求用戶登錄之后才能使用,所以有些請求操作不需要后臺校驗(yàn)是否有token,那么這個auth.js中間件就不能夠全局配置,而是放在需要校驗(yàn)token的路由下:

module.exports = (app) => {
  const { router, controller, middleware } = app;
  const auth = middleware.auth();

  router.get('/', controller.home.index);
  router.get('/request', auth, controller.home.request); // 將middleware放在中間
  router.post('/login', controller.home.login);
  router.post('/userInfo', controller.home.userInfo);
};
復(fù)制代碼

登錄鑒權(quán)及獲得用戶信息流程梳理

登錄:

獲取用戶信息:

總結(jié)

相對于云開發(fā)來說,傳統(tǒng)的前后端開發(fā)在登錄鑒權(quán)方面復(fù)雜許多,涉及多端交互,以上總結(jié)也有許多要改進(jìn)的地方??偠灾?,將邏輯理順之后,寫代碼就比較簡單了。

在登錄鑒權(quán)過程中比較重要的點(diǎn):

  1. 知道什么是用戶的唯一標(biāo)識,并根據(jù)它生成token
  2. 對token進(jìn)行校驗(yàn),判斷其是否過期,是否準(zhǔn)確
  3. 通過畫流程圖等方式理清各個過程的邏輯

參考

使用Nodejs實(shí)現(xiàn)jwt原理

手把手教會你小程序登錄鑒權(quán)

小程序登錄那些事

小程序:授權(quán)、登錄、session_key、unionId

微信小程序wx.getUserInfo授權(quán)獲取用戶信息(頭像、昵稱)

前后端分離項(xiàng)目,token過期,重新登錄和刷新token的問題

egg中間件匹配路由

Taro微信小程序登錄

Button組件onGetUserInfo未執(zhí)行


相關(guān)案例查看更多