微信小程序-实现微信授权自动登录功能

显示效果

授权前界面
授权弹框界面

采用了wx.getUserProfile API申请用户信息,若基础库版本过高,例如3.7.7,则不会显示弹窗,默认为允许状态。

若基础库版本降低,例如2.25.4,则会显示弹窗,如下图所示

授权后界面

前端部分

基础封装

新建utils文件夹,在该文件夹下新建login.js文件,在该文件中编写以下内容

/**
 * 检测用户是否登陆
 */
export function checkLogin(){
  return new Promise(function(resolve,reject){
    if(wx.getStorageSync('userInfo') && wx.getStorageSync('token')){
      checkSession().then(()=>{
        resolve(true);
      }).catch(()=>{
        reject(false);
      })
    }else{
      reject(false);
    }
  })
}

/**
 * 调用wx.checkSession()检测当前登录状态是否有效
 */
function checkSession(){
  return new Promise(function(resolve,reject){
    wx.checkSession({
      success:function(){
        resolve(true);
      },
      fail:function(){
        reject(false);
      }
    })
  })
}

/**
 * 调用wx.login() 获取用户临时登录凭证code
 */
export function wxlogin(){
  return new Promise(function(resolve,reject){
    wx.login({
      success: (res) => {
        if(res.code){
          resolve(res);
        }else{
          reject(res);
        }
      },
      fail:function(error){
        reject(error);
      }
    })
  })
}

在api文件夹下新增auth.js文件,并编写以下内容

import {wxlogin}  from '../../utils/login'
const app = getApp();

export function loginByWeixinShouQuan(userInfo){
   return new Promise(function(resolve,reject){
     wxlogin().then((res)=>{
       app.wxRequest("/api/weixinopen/wxlogin", "POST", {
          code: res.code, //临时授权凭证
          userInfo: userInfo, //用户信息
        }).then(res => {
          if (res.code === 200) {
             //存储用户信息
              wx.setStorageSync('userInfo', res.data.userInfo);
              wx.setStorageSync('token', res.data.token);
              resolve(res);
          }else{
            reject(res);
          }
        }).catch((error) => {
          reject(error);
        });
     }).catch((error) => {
      reject(error);
    })
   })
}

注意:可以将userInfo对象作为参数发送给用户服务器后台,因为userInfo对象里面包含用户的昵称和头像等信息,但也可以改为将encryptData和iv作为参数发送给用户服务器后台,例如这样,将encryptedData和iv作为loginByWeixinShouQuan的参数,调用用户服务器的时候再传过去,用户服务器后台可以通过其它办法拿到用户的个人信息,例如昵称,头像等,这个稍后再说

import {wxlogin}  from '../../utils/login'
const app = getApp();

export function loginByWeixinShouQuan(encryptedData,iv){
   return new Promise(function(resolve,reject){
     wxlogin().then((res)=>{
       app.wxRequest("/api/weixinopen/wxlogin", "POST", {
          code: res.code, //临时授权凭证
          encryptedData: encryptedData,
          iv: iv
        }).then(res => {
          if (res.code === 200) {
             //存储用户信息
              wx.setStorageSync('userInfo', res.data.userInfo);
              wx.setStorageSync('token', res.data.token);
              resolve(res);
          }else{
            reject(res);
          }
        }).catch((error) => {
          reject(error);
        });
     }).catch((error) => {
      reject(error);
    })
   })
}
个人中心界面

mine.wxml

<view class='picTxt acea-row row-between-wrapper' bindtap="goLogin">
  <view class='pictrue'>
    <image src='{{userInfo.avatarUrl}}'></image>
  </view>
  <view class='text'>
    <view class='acea-row row-middle'>
      <view class='name line1'>{{userInfo.nickName || '请授权'}}</view>
    </view>
    <view class='id'>用户编号:{{userInfo.userId || ''}}</view>
  </view>
</view>

mine.Js

const app = getApp();
Page({
data: {
     userInfo:{
      nickName:'点击登录',
      avatarUrl:'/resource/image/userhead/avatar.png',
      userId:''
     },
},

  onShow() {
    let userInfo = wx.getStorageSync('userInfo');
    if(app.globalData.hasLogin || userInfo){
      this.setData({
        userInfo:userInfo,
      });
    }
},

goLogin(){
  checkLogin().then(()=>{
   }).catch((error) => {
     console.log('未登录');
     wx.navigateTo({
       url: "/pages/auth/login/login"
     });
   })
}
})
登录界面

如下图所示,可以选择微信授权的方式自动登录,也可以选择输入账号密码的方式手动登录

login.wxml

<view class="container">
  <view class="login-box">
    <button type="primary" class="wx-login-btn" bindtap="getUserProfile">微信授权登录</button>
    <button type="primary" class="account-login-btn" bindtap="accountLogin">账号登录</button>
  </view>
</view>

login.js

import {
  loginByWeixinShouQuan
} from '../../../api/miniapp/auth'

import {showErrorToast} from '../../../utils/util'
import { checkLogin } from '../../../utils/login'

getUserProfile(e){
    wx.showLoading({
      title: "正在登录...",
      mask: true
    })

    wx.getUserProfile({
      desc: '用于完善用户信息',
      success:(res)=>{
        console.log('wx.getUserProfile返回结果:' + JSON.stringify(res));
        if (res.errMsg === 'getUserProfile:ok'){
           checkLogin().catch(() => {
            loginByWeixinShouQuan(res.userInfo).then(res => {
              app.globalData.hasLogin = true;
              wx.navigateBack({
                delta: 1
              })
            }).catch((err) => {
              app.globalData.hasLogin = false;
              showErrorToast('微信登录失败');
              wx.hideLoading();
            });
          })
        }else{
          app.globalData.hasLogin = false;
          showErrorToast('微信登录失败');
          wx.hideLoading();
        }
      },
      fail: (res) => {
        app.globalData.hasLogin = false;
        showErrorToast('微信登录失败');
        wx.hideLoading();
      }
    })
  },

解析:

  1. 通过wx.getUserProfile可以拿到用户信息,用户信息目录结构如下图所示,可以看到userInfo对象里面包含用户的昵称和头像等信息,用户可以将昵称和头像作为参数发送给用户服务器后台,也可以不发,改为将encryptData和iv作为参数发送给用户服务器后台,如果是传encryptData和ivencryptData和iv,则调用时传对应的参数,例如
loginByWeixinShouQuan(res.encryptedData,res.iv)

  1. showErrorToast是封装的一个函数,函数内容如下
function showErrorToast(msg) {
  wx.showToast({
    title: msg,
    image: '/resource/images/toast/icon_error.png'
  })
}
  1. wx.navigateBack({delta: 1})作用:关闭当前页面,返回上一级页面。

后端部分

  1. 调用微信接口获取用户登录状态信息
string text = "?appid=" + weixinOpenSettings.AppId + "&secret=" + weixinOpenSettings.AppSecret + "&js_code=" + request.code + "&grant_type=authorization_code";
string requestUri = "https://api.weixin.qq.com/sns/jscode2session" + text;
using HttpClient httpClient = new HttpClient();
byte[] wxresult = httpClient.GetByteArrayAsync(requestUri).Result;
var oiask = JsonConvert.DeserializeObject<OpenIdAndSessionKey>(Encoding.UTF8.GetString(wxresult));

OpenIdAndSessionKey.cs

 public class OpenIdAndSessionKey
 {
     public string openid { get; set; }

     public string session_key { get; set; }

     public string errcode { get; set; }

     public string errmsg { get; set; }
 }

解析:<font style="color:rgba(0, 0, 0, 0.85);">https://api.weixin.qq.com/sns/jscode2session</font> 是微信用于获取用户登录状态信息的接口,通过此接口可以拿到openid和session_key信息,需要传入的参数如下

  1. appid:微信小程序的appid
  2. secret:微信小程序appid对应的密钥
  3. js_code:wx.login接口返回的临时登录凭证
  4. grant_type:固定值authorization_code

注意:若返回errmsg变量值为“invalid code, rid: xxxx”,说明有问题,解决办法参考以下文章

【微信小程序】错误码 40029:invalid code 详解

  1. 通过传入参数encryptedData、session_key、iv参数调用解密方法获取用户信息(如果接口参数传的是用户信息,则可以直接通过接口参数获取)
if (userFound == null)
{
    var user = JsonConvert.DeserializeObject<WeixinAppUserInfo>(Cryptography.AES_decrypt(encryptedData, oiask.session_key, iv)) ?? new WeixinAppUserInfo();

    parameters.UserClaim.Nickname = user.nickName;
    parameters.UserClaim.HeadImgUrl = user.avatarUrl;
    parameters.UserClaim.Sex = user.gender;
    parameters.UserClaim.Province = user.province;
    parameters.UserClaim.City = user.city;
}

WeixinAppUserInfo.cs

 public class WeixinAppUserInfo
 {
     public class Watermark
     {
         public string appid { get; set; }

         public string timestamp { get; set; }
     }

     public string openId { get; set; }

     public string nickName { get; set; }

     public string gender { get; set; }

     public string city { get; set; }

     public string province { get; set; }

     public string country { get; set; }

     public string avatarUrl { get; set; }

     public string unionId { get; set; }

     public Watermark watermark { get; set; }
 }

Cryptography.cs

 public class Cryptography
 {
     public static string AES_decrypt(string encryptedData, string Session_key, string IV)
     {
         try
         {
             byte[] array = Convert.FromBase64String(encryptedData);
             byte[] bytes = new RijndaelManaged
             {
                 Key = Convert.FromBase64String(Session_key),
                 IV = Convert.FromBase64String(IV),
                 Mode = CipherMode.CBC,
                 Padding = PaddingMode.PKCS7
             }.CreateDecryptor().TransformFinalBlock(array, 0, array.Length);
             return Encoding.Default.GetString(bytes);
         }
         catch (Exception)
         {
             return "";
         }
     }
 }

后端返回信息

jwtToken是jwt的token。

    WeixinOpenResponseDto data = new WeixinOpenResponseDto()
    {
        token = jwtToken,
        openId = oiask.openid,
        userInfo = new UserInfo()
        {
            userId = userFound.Id.ToString(),
            avatarUrl = avatarUrl,
            nickName = nickName
        }
    };

将授权信息存入授权表中。

posted @ 2025-02-19 15:55  相遇就是有缘  阅读(2591)  评论(0)    收藏  举报