IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    微信授权全链路打通指南

    程序员海军发表于 2024-12-15 22:37:43
    love 0

    近期,我在致力于打造自己的小程序产品时,迎来了一项关键性的进展——微信相关授权流程的完整实现。从用户登录到权限获取,我们细致入微地梳理并实现了每一项授权机制,确保了用户体验的流畅与安全。

    微信小程序授权

    授权流程:

    1. 用户在小程序中点击登录按钮,触发 wx.login() 获取 code。
    2. 小程序将 code 发送到后端服务器。
    3. 后端通过微信接口 jscode2session 使用 code 获取 session_key 和 openid。
    4. 后端返回 session_key 和 openid 给前端。
    5. 前端获取 session_key 和 openid,使用 wx.getUserProfile() 获取用户信息(如昵称、头像等)。
    6. 如果需要,可以将用户信息(如昵称、头像等)发送到后端进行存储或处理。
    wx.login({
      success: function(res) {
        if (res.code) {
          // 将 code 发送到服务器
          wx.request({
            url: 'https://localhost:8080/api/login',
            method: 'POST',
            data: {
              code: res.code
            },
            success: function(response) {
              // 处理服务器返回的数据
              console.log(response.data);
            }
          });
        }
      }
    });

    下面是Nest 伪代码实现

    import { Injectable } from '@nestjs/common';
    import { HttpService } from '@nestjs/axios';
    import { firstValueFrom } from 'rxjs';
    
    interface MiniProgramLoginResponse {
      openid: string;
      session_key: string;
      unionid?: string;
      errcode?: number;
      errmsg?: string;
    }
    
    @Injectable()
      export class MiniProgramAuthService {
        constructor(private readonly httpService: HttpService) {}
    
        async login(code: string): Promise<MiniProgramLoginResponse> {
          const url = 'https://api.weixin.qq.com/sns/jscode2session';
    
          try {
            const response = await firstValueFrom(
              this.httpService.get(url, {
                params: {
                  appid: process.env.MINIPROGRAM_APPID,
                  secret: process.env.MINIPROGRAM_APPSECRET,
                  js_code: code,
                  grant_type: 'authorization_code'
                }
              })
            );
    
            return response.data;
          } catch (error) {
            throw new Error('小程序登录失败');
          }
        }
    
        // 解密用户敏感信息
        async decryptUserInfo(sessionKey: string, encryptedData: string, iv: string) {
          // 实现微信小程序用户信息解密逻辑
          // 通常需要使用第三方加密库如 crypto-js
        }
      }

    微信网页授权(OAuth 2.0)

    网页授权是通过微信官方提供的OAuth2.0认证方式,使第三方网站或应用能够获取用户基本信息,实现用户身份识别。

    画板

    授权流程

    1. 发起授权

      • 用户点击登录/授权按钮
      • 生成授权链接
      • 跳转至微信授权页面
    2. 用户确认

      • 用户选择是否授权
      • 确认后获取临时授权码 code
    3. 换取 Access Token

      • 服务端使用 code 换取 access_token
      • 获取用户的 openid 和 access_token
    4. 获取用户信息

      • 使用 access_token 和 openid
      • 调用微信接口获取用户详细信息
    5. 系统内部处理

      • 创建或更新用户信息
      • 生成系统内部登录态
    import { Injectable } from '@nestjs/common';
    import { HttpService } from '@nestjs/axios';
    import { firstValueFrom } from 'rxjs';
    
    // 用户授权信息接口定义
    interface WechatUserInfo {
      openid: string;      // 用户唯一标识
      nickname: string;    // 用户昵称
      sex: number;         // 用户性别
      province: string;    // 省份
      city: string;        // 城市
      country: string;     // 国家
      headimgurl: string;  // 头像地址
      privilege: string[]; // 用户特权信息
      unionid?: string;    // 开放平台唯一标识
    }
    
    @Injectable()
    export class WebAuthService {
      constructor(private readonly httpService: HttpService) {}
    
      // 生成授权链接
      generateAuthUrl(redirectUri: string, scope: 'snsapi_base' | 'snsapi_userinfo' = 'snsapi_userinfo') {
        const baseUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize';
        const params = new URLSearchParams({
          appid: process.env.WECHAT_APPID,
          redirect_uri: redirectUri,
          response_type: 'code',
          scope: scope,
          state: 'STATE#wechat_redirect'  // 自定义参数,用于回传
        });
        
        return `${baseUrl}?${params}#wechat_redirect`;
      }
    
      // 获取 Access Token
      async getAccessToken(code: string) {
        const url = 'https://api.weixin.qq.com/sns/oauth2/access_token';
        
        try {
          const response = await firstValueFrom(
            this.httpService.get(url, {
              params: {
                appid: process.env.WECHAT_APPID,
                secret: process.env.WECHAT_APPSECRET,
                code: code,
                grant_type: 'authorization_code'
              }
            })
          );
    
          return response.data;
        } catch (error) {
          throw new Error('获取 Access Token 失败');
        }
      }
    
      // 获取用户信息
      async getUserInfo(accessToken: string, openid: string): Promise<WechatUserInfo> {
        const url = 'https://api.weixin.qq.com/sns/userinfo';
        
        try {
          const response = await firstValueFrom(
            this.httpService.get(url, {
              params: {
                access_token: accessToken,
                openid: openid,
                lang: 'zh_CN'
              }
            })
          );
    
          return response.data;
        } catch (error) {
          throw new Error('获取用户信息失败');
        }
      }
    }

    微信开放平台授权

    特点:

    • 适用于第三方应用
    • 支持移动应用、网站应用等
    • 需要开发者资质认证
    import { Injectable } from '@nestjs/common';
    import { HttpService } from '@nestjs/axios';
    import { firstValueFrom } from 'rxjs';
    
    // 定义开放平台授权响应接口
    interface OpenPlatformAuthResponse {
      access_token: string;    // 接口调用凭证
      expires_in: number;      // access_token 过期时间
      refresh_token: string;   // 刷新 token
      openid: string;          // 授权用户唯一标识
      scope: string;           // 用户授权的作用域
      unionid: string;         // 开放平台唯一标识
    }
    
    @Injectable()
    export class OpenPlatformAuthService {
      constructor(private readonly httpService: HttpService) {}
    
      /**
       * 获取 access_token
       * @param code 授权码
       * @returns 授权响应信息
       */
      async getAccessToken(code: string): Promise<OpenPlatformAuthResponse> {
        // 微信获取 access_token 的接口地址
        const url = 'https://api.weixin.qq.com/sns/oauth2/access_token';
        
        try {
          // 使用授权码换取 access_token
          const response = await firstValueFrom(
            this.httpService.get(url, {
              params: {
                // 从环境变量读取开放平台 AppID
                appid: process.env.OPEN_PLATFORM_APPID,
                // 从环境变量读取开放平台密钥
                secret: process.env.OPEN_PLATFORM_APPSECRET,
                // 授权码
                code: code,
                // 授权类型,固定值
                grant_type: 'authorization_code'
              }
            })
          );
    
          return response.data;
        } catch (error) {
          // 捕获并抛出授权失败的错误
          throw new Error('开放平台授权失败');
        }
      }
    
      /**
       * 刷新 access_token
       * @param refreshToken 刷新 token
       * @returns 新的授权信息
       */
      async refreshAccessToken(refreshToken: string) {
        // 微信刷新 access_token 的接口地址
        const url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token';
        
        try {
          // 使用 refresh_token 换取新的 access_token
          const response = await firstValueFrom(
            this.httpService.get(url, {
              params: {
                // 开放平台 AppID
                appid: process.env.OPEN_PLATFORM_APPID,
                // 授权类型,固定值
                grant_type: 'refresh_token',
                // 用于刷新的 token
                refresh_token: refreshToken
              }
            })
          );
    
          return response.data;
        } catch (error) {
          // 捕获并抛出刷新 Token 失败的错误
          throw new Error('刷新 Token 失败');
        }
      }
    }

    企业微信授权

    企业微信授权是针对企业内部应用和员工的身份认证机制,提供更严格和精细的权限控制。

    画板

    特点:

    • 主要面向企业内部应用
    • 更强的权限控制
    • 安全性更高

    授权流程

    1. 发起授权

      • 员工访问企业内部应用
      • 触发登录机制(扫码/输入)
      • 生成企业微信授权链接
    2. 身份验证

      • 跳转企业微信登录页
      • 员工确认身份
      • 获取临时授权码
    3. 换取用户信息

      • 服务端使用 code 换取用户标识
      • 获取 userid
      • 调用接口获取用户详细信息
    4. 系统内部处理

      • 验证员工身份
      • 检查权限状态
      • 生成系统内部登录态
    import { Injectable } from '@nestjs/common';
    import { HttpService } from '@nestjs/axios';
    import { firstValueFrom } from 'rxjs';
    
    // 定义企业微信授权响应接口
    interface EnterpriseWechatAuthResponse {
      access_token: string;    // 企业接口调用凭证
      expires_in: number;      // access_token 过期时间
      user_ticket?: string;    // 用户票据(可选)
      user_info?: {
        userid: string;        // 企业成员 ID
        name: string;          // 成员名称
        department: number[];  // 部门 ID 列表
      };
    }
    
    @Injectable()
    export class EnterpriseWechatAuthService {
      constructor(private readonly httpService: HttpService) {}
    
      /**
       * 获取企业微信用户信息
       * @param code 临时授权码
       * @returns 用户信息和 access_token
       */
      async getUserInfo(code: string): Promise<EnterpriseWechatAuthResponse> {
        // 获取企业 access_token 的接口地址
        const tokenUrl = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken';
        // 获取用户信息的接口地址
        const userInfoUrl = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo';
    
        // 第一步:获取企业 access_token
        // 需要使用企业 ID 和应用的秘钥
        const tokenResponse = await firstValueFrom(
          this.httpService.get(tokenUrl, {
            params: {
              // 从环境变量读取企业 ID
              corpid: process.env.ENTERPRISE_CORPID,
              // 从环境变量读取企业应用秘钥
              corpsecret: process.env.ENTERPRISE_CORPSECRET
            }
          })
        );
    
        // 从响应中提取 access_token
        const accessToken = tokenResponse.data.access_token;
    
        // 第二步:使用 access_token 和临时授权码获取用户信息
        const userInfoResponse = await firstValueFrom(
          this.httpService.get(userInfoUrl, {
            params: {
              // 企业 access_token
              access_token: accessToken,
              // 临时授权码
              code: code
            }
          })
        );
    
        return userInfoResponse.data;
      }
    }

    各个平台授权小结

    授权类型个人是否可用是否收费主要适用场景
    网页授权是免费网站、H5应用
    小程序授权是免费小程序登录、开放平台
    公众号授权部分可用免费+增值服务公众号相关应用(服务号、订阅号)
    企业微信否有费用企业内部协作


沪ICP备19023445号-2号
友情链接