近期,我在致力于打造自己的小程序產(chǎn)品時(shí),迎來了一項(xiàng)關(guān)鍵性的進(jìn)展——微信相關(guān)授權(quán)流程的完整實(shí)現(xiàn)。從用戶登錄到權(quán)限獲取,我們細(xì)致入微地梳理并實(shí)現(xiàn)了每一項(xiàng)授權(quán)機(jī)制,確保了用戶體驗(yàn)的流暢與安全。
微信小程序授權(quán)


授權(quán)流程:
- 用戶在小程序中點(diǎn)擊登錄按鈕,觸發(fā)
wx.login()
獲取 code
。 - 小程序?qū)?nbsp;
code
發(fā)送到后端服務(wù)器。 - 后端通過微信接口
jscode2session
使用 code
獲取 session_key
和 openid
。 - 后端返回
session_key
和 openid
給前端。 - 前端獲取
session_key
和 openid
,使用 wx.getUserProfile()
獲取用戶信息(如昵稱、頭像等)。 - 如果需要,可以將用戶信息(如昵稱、頭像等)發(fā)送到后端進(jìn)行存儲或處理。
wx.login({
success: function(res) {
if (res.code) {
// 將 code 發(fā)送到服務(wù)器
wx.request({
url: 'https://localhost:8080/api/login',
method: 'POST',
data: {
code: res.code
},
success: function(response) {
// 處理服務(wù)器返回的數(shù)據(jù)
console.log(response.data);
}
});
}
}
});
下面是Nest 偽代碼實(shí)現(xiàn)
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) {
}
}
微信網(wǎng)頁授權(quán)(OAuth 2.0)
網(wǎng)頁授權(quán)是通過微信官方提供的OAuth2.0認(rèn)證方式,使第三方網(wǎng)站或應(yīng)用能夠獲取用戶基本信息,實(shí)現(xiàn)用戶身份識別。


授權(quán)流程
- 發(fā)起授權(quán)
- 用戶點(diǎn)擊登錄/授權(quán)按鈕
- 生成授權(quán)鏈接
- 跳轉(zhuǎn)至微信授權(quán)頁面
- 用戶確認(rèn)
- 用戶選擇是否授權(quán)
- 確認(rèn)后獲取臨時(shí)授權(quán)碼
code
- 換取 Access Token
- 服務(wù)端使用
code
換取 access_token
- 獲取用戶的
openid
和 access_token
- 獲取用戶信息
- 使用
access_token
和 openid
- 調(diào)用微信接口獲取用戶詳細(xì)信息
- 系統(tǒng)內(nèi)部處理
- 創(chuàng)建或更新用戶信息
- 生成系統(tǒng)內(nèi)部登錄態(tài)
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`;
}
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('獲取用戶信息失敗');
}
}
}
微信開放平臺授權(quán)

特點(diǎn):
- 適用于第三方應(yīng)用
- 支持移動應(yīng)用、網(wǎng)站應(yīng)用等
- 需要開發(fā)者資質(zhì)認(rèn)證
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
interface OpenPlatformAuthResponse {
access_token: string;
expires_in: number;
refresh_token: string;
openid: string;
scope: string;
unionid: string;
}
@Injectable()
export class OpenPlatformAuthService {
constructor(private readonly httpService: HttpService) {}
async getAccessToken(code: string): Promise<OpenPlatformAuthResponse> {
const url = 'https://api.weixin.qq.com/sns/oauth2/access_token';
try {
const response = await firstValueFrom(
this.httpService.get(url, {
params: {
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('開放平臺授權(quán)失敗');
}
}
async refreshAccessToken(refreshToken: string) {
const url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token';
try {
const response = await firstValueFrom(
this.httpService.get(url, {
params: {
appid: process.env.OPEN_PLATFORM_APPID,
grant_type: 'refresh_token',
refresh_token: refreshToken
}
})
);
return response.data;
} catch (error) {
throw new Error('刷新 Token 失敗');
}
}
}
企業(yè)微信授權(quán)
企業(yè)微信授權(quán)是針對企業(yè)內(nèi)部應(yīng)用和員工的身份認(rèn)證機(jī)制,提供更嚴(yán)格和精細(xì)的權(quán)限控制。


特點(diǎn):
- 主要面向企業(yè)內(nèi)部應(yīng)用
- 更強(qiáng)的權(quán)限控制
- 安全性更高
授權(quán)流程
- 發(fā)起授權(quán)
- 員工訪問企業(yè)內(nèi)部應(yīng)用
- 觸發(fā)登錄機(jī)制(掃碼/輸入)
- 生成企業(yè)微信授權(quán)鏈接
- 身份驗(yàn)證
- 跳轉(zhuǎn)企業(yè)微信登錄頁
- 員工確認(rèn)身份
- 獲取臨時(shí)授權(quán)碼
- 換取用戶信息
- 服務(wù)端使用
code
換取用戶標(biāo)識 - 獲取
userid
- 調(diào)用接口獲取用戶詳細(xì)信息
- 系統(tǒng)內(nèi)部處理
- 驗(yàn)證員工身份
- 檢查權(quán)限狀態(tài)
- 生成系統(tǒng)內(nèi)部登錄態(tài)
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
interface EnterpriseWechatAuthResponse {
access_token: string;
expires_in: number;
user_ticket?: string;
user_info?: {
userid: string;
name: string;
department: number[];
};
}
@Injectable()
export class EnterpriseWechatAuthService {
constructor(private readonly httpService: HttpService) {}
async getUserInfo(code: string): Promise<EnterpriseWechatAuthResponse> {
const tokenUrl = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken';
const userInfoUrl = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo';
const tokenResponse = await firstValueFrom(
this.httpService.get(tokenUrl, {
params: {
corpid: process.env.ENTERPRISE_CORPID,
corpsecret: process.env.ENTERPRISE_CORPSECRET
}
})
);
const accessToken = tokenResponse.data.access_token;
const userInfoResponse = await firstValueFrom(
this.httpService.get(userInfoUrl, {
params: {
access_token: accessToken,
code: code
}
})
);
return userInfoResponse.data;
}
}
各個平臺授權(quán)小結(jié)
授權(quán)類型 | 個人是否可用 | 是否收費(fèi) | 主要適用場景 |
---|
網(wǎng)頁授權(quán) | 是 | 免費(fèi) | 網(wǎng)站、H5應(yīng)用 |
小程序授權(quán) | 是 | 免費(fèi) | 小程序登錄、開放平臺 |
公眾號授權(quán) | 部分可用 | 免費(fèi)+增值服務(wù) | 公眾號相關(guān)應(yīng)用(服務(wù)號、訂閱號) |
企業(yè)微信 | 否 | 有費(fèi)用 | 企業(yè)內(nèi)部協(xié)作 |
?轉(zhuǎn)自https://www.cnblogs.com/HaiJun-Aion/p/18609286
該文章在 2024/12/17 15:49:13 編輯過