網(wǎng)站首頁 編程語言 正文
方式一:小程序授權(quán)登錄
通過wx.login獲取 臨時登錄憑證code,向后端換取token。 可以做到無感登錄。
時序圖:
說明:
1、客戶端調(diào)用 wx.login() 獲取 臨時登錄憑證code,通過 wx.request() 發(fā)起網(wǎng)絡(luò)請求,將 code 傳給服務(wù)端
2、服務(wù)端使用 code + appid + appsecret 向微信換取 (調(diào)用 auth.code2Session 接口)用戶唯一標(biāo)識openid 和 會話密鑰session_key
3、服務(wù)端自定義 登錄狀態(tài)token(與openid、session_key關(guān)聯(lián))返回客戶端
4、客戶端將 登錄狀態(tài)token 存入 緩存storage(推薦使用 wx.setStorageSync(‘key’, ‘value’) 同步存儲)
5、客戶端wx.request() 發(fā)起請求時,攜帶 登錄狀態(tài)token (推薦使用 wx.getStorageSync(‘key’) 同步獲取)
6、服務(wù)端通過 登錄狀態(tài)token 查詢到對應(yīng) openid 和 session_key
7、驗證成功后,返回業(yè)務(wù)數(shù)據(jù)給客戶端
注意:
1、會話密鑰session_key 是對?戶數(shù)據(jù)進(jìn)?加密簽名的密鑰。為了應(yīng)??身的數(shù)據(jù)安全,開發(fā)者服務(wù)器不應(yīng)該把會話密鑰下發(fā)到?程序,也不應(yīng)該對外提供這個密鑰。
2、臨時登錄憑證code 只能使??次
code(以uni-app框架為例):
新建http.js,封裝登錄方法
// baseurl
let baseUrl = 'https://test.com.cn'
// 請求封裝
async function _request(url, method, data = {}) {
let res = await requestPromise(url, method, data)
if (res.code == 200) {
return Promise.resolve(res)
} else if (res.code == 401) { // 無感刷新
return await login(url, method, data)
} else {
return Promise.reject(res)
}
}
// 登錄
async function login(url, method, data) {
let openIdUrl = new String()
// #ifdef MP-WEIXIN
openIdUrl = '微信登錄接口地址'
//#endif
//#ifdef MP-ALIPAY
openIdUrl = '支付寶登錄接口地址'
//#endif
let res = await requestPromise(openIdUrl, 'POST', { code: await _getAppCode(), source: 'MP' })
if (res.code == 200) {
// 將token,userid存入緩存
uni.setStorageSync('token', res.data.token)
uni.setStorageSync('userId', res.data.userId)
// 再次發(fā)起請求
return await _request(url, method, data)
} else {
return Promise.reject(res)
}
}
// 發(fā)送request請求
function requestPromise(url, method, data = {}) {
return new Promise((resolve, reject) => {
uni.request({
header: {
'Content-Type': 'application/json;charset=UTF-8',
'X-Token': uni.getStorageSync('token') || new String(),
'X-UserId': uni.getStorageSync('userId') || new String()
},
url: `${baseUrl}${url}`,
method: method,
data: data,
success: result => {
resolve(result)
},
fail: error => {
reject(error)
},
})
})
}
// 獲取臨時登錄憑證code
function _getAppCode() {
return new Promise((resolve, reject) => {
// #ifdef MP-WEIXIN
uni.login({
provider: 'weixin',
success(res) {
resolve(res.code)
},
fail(err) {
reject(err)
}
})
// #endif
// #ifdef MP-ALIPAY
// 系統(tǒng)建議使用支付寶原生寫法
my.getAuthCode({
scopes: 'auth_base',
success(res) {
resolve(res.authCode)
},
fail(err) {
reject(err)
}
})
// #endif
})
}
module.exports = {
$get: function(url, data, onSuccess, onError) {
_request(url, 'GET', data).then(res => {
onSuccess && onSuccess(res)
}).catch(err => {
onError && onError(err)
})
},
$put: function(url, data, onSuccess, onError) {
_request(url, 'PUT', data).then(res => {
onSuccess && onSuccess(res)
}).catch(err => {
onError && onError(err)
})
},
$post: function(url, data, onSuccess, onError) {
_request(url, 'POST', data).then(res => {
onSuccess && onSuccess(res)
}).catch(err => {
onError && onError(err)
})
},
$delete: function(url, data, onSuccess, onError) {
_request(url, 'DELETE', data).then(res => {
onSuccess && onSuccess(res)
}).catch(err => {
onError && onError(err)
})
},
baseUrl: baseUrl
}
新建api.js,對接口進(jìn)行封裝
import https from '../utils/https.js'
export function test(params) {
return new Promise((resolve, reject) => {
https.$get("/api", params, res => {
return resolve(res)
}, err => {
return reject(err)
})
})
}
方式二:手機(jī)號授權(quán)登錄
過button按鈕的bindgetphonenumber事件,彈出手機(jī)號授權(quán),獲取到加密數(shù)據(jù)后,向后端換取token。
說明:
1、過button按鈕的bindgetphonenumber事件獲取手機(jī)號加密數(shù)據(jù),按鈕需要設(shè)置open-type=“getPhoneNumber”
2、調(diào)用 wx.login() 獲取 臨時登錄憑證code
3、將加密數(shù)據(jù)(encryptedData、iv、signature、rawData)和 臨時登錄憑證code傳給服務(wù)端
4、服務(wù)端使用 code + appid + appsecret 向微信換取 (調(diào)用 auth.code2Session 接口)用戶唯一標(biāo)識openid 和 會話密鑰session_key
5、服務(wù)端根據(jù)session_key,appid ,encryptedData,iv解密手機(jī)號
6、服務(wù)端自定義 登錄狀態(tài)token(與openid、session_key關(guān)聯(lián))返回客戶端
7、客戶端將 登錄狀態(tài)token 存入 緩存storage(推薦使用 wx.setStorageSync(‘key’, ‘value’) 同步存儲)
8、客戶端wx.request() 發(fā)起請求時,攜帶 登錄狀態(tài)token (推薦使用 wx.getStorageSync(‘key’) 同步獲取)
9、服務(wù)端通過 登錄狀態(tài)token 查詢到對應(yīng) openid 和 session_key
10、驗證成功后,返回業(yè)務(wù)數(shù)據(jù)給客戶端
注意:
在回調(diào)中調(diào)? wx.login 登錄,可能會刷新登錄態(tài)。此時服務(wù)器使? code 換取的sessionKey 不是加密時使?的 sessionKey,導(dǎo)致解密失敗。建議開發(fā)者提前進(jìn)? login;或者在回調(diào)中先使? checkSession 進(jìn)?登錄態(tài)檢查,避免 login刷新登錄態(tài)。
也就是說在觸發(fā)getPhoneNumber方法(用戶點(diǎn)擊button)之前,就需要獲取最新code。
code(以uni-app框架為例):
新建wxLogin.vue
<template>
<view class="wx-login">
<!-- #ifdef MP-WEIXIN -->
<u-button type="primary" text="微信用戶一鍵登錄" open-type="getPhoneNumber" :plain="true" @getphonenumber="getUserPhoneNumber"></u-button>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<button open-type="getPhoneNumber" :plain="true"@getphonenumber="getUserPhoneNumber" scope='userInfo'>支付寶用戶一鍵登錄</button>
<!-- #endif -->
</view>
</template>
<script>
import { mapActions } from 'vuex'
export default {
async created() {
this.code = await this.getAppCode()
},
data() {
return {
// 用戶憑證
code: new String()
}
},
methods: {
...mapActions(['Login']),
// 微信用戶手機(jī)號登錄
getUserPhoneNumber(event) {
if(event.detail.errMsg !== 'getPhoneNumber:ok') return
uni.showToast({
title: '登錄中',
icon: 'loading',
mask: true
})
event.detail.code = this.code
this.Login({ userInfo: event.detail }).then(async ({ code, msg }) => {
if (code == 200) { // 登錄成功,跳轉(zhuǎn)首頁
uni.reLaunch({ url: '/pages_home/home/index' })
} else {
this.code = await this.getAppCode()
uni.showToast({
icon: 'none',
title: msg,
duration: 2000
})
}
})
},
// 獲取code
getAppCode() {
return new Promise((resolve, reject) => {
// #ifdef MP-WEIXIN
uni.login({
provider: 'weixin',
success(res) {
resolve(res.code)
},
fail(err) {
reject(err)
}
})
// #endif
// #ifdef MP-ALIPAY
my.getAuthCode({
scopes: 'auth_base',
success(res) {
resolve(res.authCode)
},
fail(err) {
reject(err)
}
})
// #endif
})
},
}
}
</script>
<style lang="less" scoped>
.wx-login {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 0 32rpx 60rpx;
button {
font-size: 28rpx;
font-weight: 400;
}
}
</style>
新建store/index.js 封裝登錄邏輯
import Vue from "vue"
import Vuex from 'vuex'
import { loginByMobile } from '@/api/login.js'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
token: new String(),
userId: new String(),
userInfo: new Object(),
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_USER: (state, userInfo) => {
state.userInfo = userInfo
},
SET_USERID: (state, userId) => {
state.userId = userId
},
},
actions: {
// 登錄
Login({ commit }, { userInfo }) {
// 微信手機(jī)號登錄
userInfo.type = 'WX_MP'
delete userInfo.errMsg
return new Promise((resolve, reject) => {
loginByMobile(userInfo).then(response => {
if (response.code == 200) {
uni.setStorageSync('USER_ID', response.data.userId)
uni.setStorageSync('ACCESS_TOKEN', response.data.token)
uni.setStorageSync('userInfo', response.data)
commit('SET_USERID', response.data.userId)
commit('SET_TOKEN', response.data.token)
commit('SET_USER', response.data)
}
resolve(response)
}).catch(error => {
reject(error)
})
})
},
// 登出
Logout({ commit, state }) {
return new Promise((resolve) => {
commit('SET_USERID', new String())
commit('SET_TOKEN', new String())
commit('SET_USER', new Object())
uni.removeStorageSync('USER_ID')
uni.removeStorageSync('ACCESS_TOKEN')
uni.removeStorageSync('userInfo')
resolve('200')
})
},
},
})
export default store
main.js中引入vuex 并且掛載到vue實例上
//引入vuex 并且掛載到vue實例上
import store from "./store/index.js"
Vue.prototype.$store = store
request.js封裝
import env from './env.js'
// http
export const request = (url, method, data = {}) => {
return new Promise((resolve, reject) => {
uni.request({
header: {
'Content-Type': 'application/json;charset=UTF-8',
'X-Token': uni.getStorageSync('ACCESS_TOKEN'),
'X-UserId': uni.getStorageSync('USER_ID'),
'satoken': uni.getStorageSync('userInfo').satoken
},
url: `${env.root}${url}`,
method: method,
data: data,
success: res => {
if (res.data.code == 401 || res.data.message == '請先登錄') {
// token過期 重新登錄
uni.showToast({
icon: 'none',
title: '登錄超時,請重新登錄',
duration: 2000
})
uni.removeStorageSync('ACCESS_TOKEN')
uni.removeStorageSync('USER_ID')
uni.removeStorageSync('userInfo')
// 跳轉(zhuǎn)登錄
uni.redirectTo({ url: '/pages/login/index' })
reject(res.data)
} else { // 返回內(nèi)容
resolve(res.data)
}
},
error: err => {
console.error("請求失敗", err)
reject(err)
},
complete: () => {},
})
})
}
// upload
export const upload = (url, filePath, name, formData = {}) => {
return new Promise((resolve, reject) => {
uni.uploadFile({
header: {
'Content-Type': 'application/json;charset=UTF-8',
'X-Token': uni.getStorageSync('ACCESS_TOKEN'),
'X-UserId': uni.getStorageSync('USER_ID'),
'satoken': uni.getStorageSync('userInfo').satoken
},
url: `${env.root}${url}`,
filePath: filePath,
name: name,
formData: formData,
success: res => {
if (res.data.code == 401 || res.data.message == '請先登錄') {
// token過期 重新登錄
uni.showToast({
icon: 'none',
title: '登錄超時,請重新登錄',
duration: 2000
})
uni.removeStorageSync('ACCESS_TOKEN')
uni.removeStorageSync('USER_ID')
uni.removeStorageSync('userInfo')
// 跳轉(zhuǎn)登錄
uni.redirectTo({ url: '/pages/login/index' })
reject(res.data)
} else { // 返回內(nèi)容
resolve(JSON.parse(res.data))
}
},
error: err => {
console.error("請求失敗", err)
reject(err)
},
complete: () => {},
})
})
}
方式三:用戶信息授權(quán)登錄
通過button按鈕的click事件,調(diào)用 wx.getUserProfile() 彈出授權(quán)框,獲取到用戶加密數(shù)據(jù)后,向后端換取token。
說明:
1、通過 wx.getUserProfile() 獲取用戶信息,此方法需要通過button按鈕的click事件觸發(fā)
2、調(diào)用 wx.login() 獲取 臨時登錄憑證code
3、將加密數(shù)據(jù)(encryptedData、iv)和 臨時登錄憑證code傳給服務(wù)端
4、服務(wù)端使用 code + appid + appsecret 向微信換取 (調(diào)用 auth.code2Session 接口)用戶唯一標(biāo)識openid 和 會話密鑰session_key
5、服務(wù)端自定義 登錄狀態(tài)token(與openid、session_key關(guān)聯(lián))返回客戶端,同時返回用戶信息
6、客戶端將 登錄狀態(tài)token 存入 緩存storage(推薦使用 wx.setStorageSync(‘key’, ‘value’) 同步存儲)
7、客戶端wx.request() 發(fā)起請求時,攜帶 登錄狀態(tài)token (推薦使用 wx.getStorageSync(‘key’) 同步獲取)
8、服務(wù)端通過 登錄狀態(tài)token 查詢到對應(yīng) openid 和 session_key
9、驗證成功后,返回業(yè)務(wù)數(shù)據(jù)給客戶端
注意:
在回調(diào)中調(diào)? wx.login 登錄,可能會刷新登錄態(tài)。此時服務(wù)器使? code 換取的sessionKey 不是加密時使?的 sessionKey,導(dǎo)致解密失敗。建議開發(fā)者提前進(jìn)? login;或者在回調(diào)中先使? checkSession 進(jìn)?登錄態(tài)檢查,避免 login刷新登錄態(tài)。
code(以uni-app框架為例):
<template>
<view class="content">
<image src="logo.png"></image>
<view class="title">申請獲取以下權(quán)限</view>
<text class="msg">獲取你的公開信息(昵稱、頭像、地區(qū)等)</text>
<!-- #ifdef MP-WEIXIN -->
<button class="btn" @click="wxgetUserInfo">授權(quán)登錄</button>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<button class="btn" open-type="getAuthorize" @getAuthorize="alipaygetUserInfo" @error="onAuthError" scope='userInfo'>授權(quán)登錄</button>
<!-- #endif -->
</view>
</template>
<script>
import { loginByWx, loginByAlipay } from '@/api/user.js'
export default {
data() {
return {
code: new String()
}
},
async onLoad() {
this.code = await this.getAppCode()
},
methods: {
// 獲取微信用戶信息
async wxgetUserInfo() {
try {
// 微信登錄
// #ifdef MP-WEIXIN
let userData = await this._getwxUserData()
// 調(diào)用后臺接口登錄
let loginRes = await this.appLogin(userData)
// savecache
uni.setStorageSync('isLogin', true)
uni.setStorageSync('userInfo', {
headImg: loginRes.headImg,
userName: loginRes.userName
});
uni.setStorageSync('token', loginRes.token)
uni.setStorageSync('userId', loginRes.userId)
uni.navigateBack({
delta: 1
});
// #endif
} catch(err) {
this.onAuthError()
}
},
// 支付寶用戶登錄
async alipaygetUserInfo() {
try {
// 支付寶登錄
// #ifdef MP-ALIPAY
let userData = await this._getalipayUserData()
// 調(diào)用后臺接口登錄
let loginRes = await this.appLogin(userData)
loginRes.userName = userData.nickName
loginRes.headImg = userData.avatar
// savecache
uni.setStorageSync('isLogin', true)
uni.setStorageSync('userInfo', {
headImg: loginRes.headImg,
userName: loginRes.userName
})
uni.setStorageSync('token', loginRes.token)
uni.setStorageSync('userId', loginRes.userId)
uni.navigateBack({
delta: 1
})
// #endif
} catch(err) {
this.onAuthError()
}
},
// 授權(quán)失敗
onAuthError() {
uni.showToast({
title: '授權(quán)失敗,請確認(rèn)授權(quán)已開啟',
mask: true,
icon: 'none'
})
},
// 獲取支付寶用戶加密數(shù)據(jù)
_getalipayUserData() {
return new Promise((resolve, reject) => {
my.getOpenUserInfo({
success: res => {
let userInfo = JSON.parse(res.response).response
resolve(userInfo)
},
fail: err => {
reject(err)
}
})
})
},
// 獲取微信用戶加密數(shù)據(jù)
_getwxUserData() {
// 用戶信息接口調(diào)整,使用uni.getUserInfo() 獲取到的用戶信息是一個灰色的頭像和微信用戶
// 需要使用 uni.getUserProfile() 獲取用戶信息,此方法需要按鈕觸發(fā) @click=func
return new Promise((resolve, reject) => {
uni.getUserProfile({
desc: '完善用戶信息',
success: data => {
console.log("用戶信息:", data)
resolve(data)
},
fail: err => {
reject(err)
}
})
})
},
// 獲取 臨時登錄憑證code
getAppCode() {
return new Promise((resolve, reject) => {
// #ifdef MP-WEIXIN
uni.login({
provider: 'weixin',
success(res) {
resolve(res.code)
},
fail(err) {
reject(err)
}
})
// #endif
// #ifdef MP-ALIPAY
// 系統(tǒng)建議使用支付寶原生寫法
my.getAuthCode({
scopes: 'auth_base',
success(res) {
resolve(res.authCode)
},
fail(err) {
reject(err)
}
})
// #endif
})
},
// 用戶登錄
appLogin(detail) {
return new Promise(async(resolve, reject) => {
try {
// 微信登錄
// #ifdef MP-WEIXIN
let params = {
code: this.code,
source: 'MP',
encryptedData: detail.encryptedData,
iv: detail.iv
}
let wxloginRes = await loginByWx(params)
if(wxloginRes.code == 200) {
resolve(wxloginRes.data)
} else {
reject(wxloginRes)
}
// #endif
// #ifdef MP-ALIPAY
// 系統(tǒng)建議使用支付寶原生寫法
let alipayloginRes = await loginByAlipay({
code: this.code
});
if(alipayloginRes.code == 200) {
resolve(alipayloginRes.data)
} else {
reject(alipayloginRes)
}
// #endif
} catch(err) {
reject(err)
}
});
},
}
}
</script>
<style lang="less">
view, text, image, input {
box-sizing: border-box;
}
.content {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100vh;
padding: 160rpx 40rpx 0;
image {
width: 275rpx;
height: 104rpx;
}
.title {
width: 100%;
margin-top: 80rpx;
font-size: 30rpx;
font-weight: 600;
color: rgba(0, 0, 0, 0.85);
text-align: left;
}
.msg {
display: block;
width: 100%;
margin-top: 16rpx;
font-size: 28rpx;
color: rgba(0, 0, 0, 0.65);
text-align: left;
}
.btn {
position: absolute;
bottom: 160rpx;
width: 670rpx;
height: 96rpx;
background: #00B391;
border-radius: 8rpx;
font-size: 34rpx;
font-weight: 400;
color: #FFFFFF;
line-height: 96rpx;
}
}
</style>
原文鏈接:https://blog.csdn.net/weixin_45559449/article/details/129398318
相關(guān)推薦
- 2021-12-12 基于Go語言實現(xiàn)的簡易api網(wǎng)關(guān)的示例代碼_Golang
- 2023-02-12 react-router-domV6嵌套路由實現(xiàn)詳解_React
- 2022-05-20 python?特有語法推導(dǎo)式的基本使用_python
- 2022-04-18 Go中g(shù)routine通信與context控制實例詳解_Golang
- 2022-02-11 小程序如何把參數(shù)設(shè)置為全局變量
- 2022-11-23 python?Multiprocessing.Pool進(jìn)程池模塊詳解_python
- 2022-03-26 正則表達(dá)式詳析+常用示例_正則表達(dá)式
- 2022-09-14 Python詳解如何動態(tài)給對象增加屬性和方法_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支