feat(auth): 添加微信小程序登录认证功能

- 在app.js中添加token有效性检查机制
- 实现登录页面的完整表单验证和提交逻辑
- 创建request.js工具类封装wx.request请求
- 实现token.js工具类管理认证信息的存储和验证
- 添加自动跳转到登录页面的过期处理机制
- 支持Bearer token认证头信息的自动添加
master
Lmx 2 weeks ago
parent 714b044ebc
commit 2682b48d04

@ -1,4 +1,6 @@
// app.js
const { isAuthenticated } = require('./utils/token');
App({
onLaunch() {
// 展示本地存储能力
@ -12,8 +14,25 @@ App({
// 发送 res.code 到后台换取 openId, sessionKey, unionId
}
})
// 检查token有效性
this.checkTokenValidity();
},
globalData: {
userInfo: null
},
/**
* 检查token有效性
*/
checkTokenValidity() {
// 可以在这里添加token过期检查逻辑
// 例如如果使用JWT可以解析token并检查过期时间
if (isAuthenticated()) {
console.log('用户已认证');
} else {
console.log('用户未认证,需重新登录');
}
}
})

@ -1,13 +1,75 @@
// pages/login/login.js
const { post } = require('../../utils/request');
const { setToken, setUserInfo } = require('../../utils/token');
Page({
/**
* 页面的初始数据
*/
data: {
account:"",
password:""
},
// 账号输入监听(可选,用于实时校验)
handleAccountInput(e) {
this.setData({ account: e.detail });
},
// 密码输入监听
handlePasswordInput(e) {
this.setData({ password: e.detail });
},
// 重置表单
handleReset() {
this.setData({
account: '',
password: ''
});
wx.showToast({ title: '已重置', icon: 'none' });
},
// 登录提交
async handleLogin() {
const { account, password } = this.data;
// 基础校验
if (!account.trim()) {
wx.showToast({ title: '请输入账号', icon: 'none' });
return;
}
if (!password.trim()) {
wx.showToast({ title: '请输入密码', icon: 'none' });
return;
}
try {
const res = await post('/api/login', {
username: account, // 或 phone根据后端字段调整
password: password
}, {
showLoading: true,
loadingText: '登录中...'
});
// 假设成功返回 { token: 'xxx', userInfo: {...} }
setToken(res.token);
setUserInfo(res.userInfo);
wx.showToast({ title: '登录成功', icon: 'success' });
// 跳转首页
wx.switchTab({ url: '/pages/index/index' }); // 假设首页是 tab 页
} catch (error) {
// 错误已在 request 中统一提示,这里可不做处理
console.error('登录失败:', error);
if (error.statusCode === 401) {
wx.showToast({
title: `用户名或密码错误`,
icon: 'none'
});
}
}
},
/**
* 生命周期函数--监听页面加载
*/

@ -0,0 +1,118 @@
// utils/request.js
const { getAuthHeader, clearAuthData } = require('./token');
const BASE_URL = 'http://localhost:3000'; // 替换为你的后端接口域名
/**
* 封装 wx.request
* @param {Object} options - 请求配置
* @param {string} options.url - 接口路径不包含域名
* @param {string} [options.method='GET'] - 请求方法
* @param {Object} [options.data] - 请求参数
* @param {boolean} [options.showLoading=true] - 是否显示加载提示
* @param {string} [options.loadingText='加载中...'] - 加载提示文字
*/
const request = (options) => {
const {
url,
method = 'GET',
data = {},
showLoading = true,
loadingText = '加载中...'
} = options;
return new Promise((resolve, reject) => {
let loadingHide = () => {};
// 显示 loading
if (showLoading) {
wx.showLoading({
title: loadingText,
mask: true
});
loadingHide = wx.hideLoading;
}
wx.request({
url: BASE_URL + url,
method,
data,
header: getAuthHeader(),
success: (res) => {
const { statusCode, data: responseData } = res;
if (statusCode === 200) {
// 假设后端返回格式:{ code: 200, data: {}, msg: 'success' }
if (responseData.code === 200) {
resolve(responseData.data);
} else {
// 特殊处理401未授权错误
if (responseData.code === 401 || statusCode === 401) {
// token可能已过期清除本地认证信息
clearAuthData();
wx.showToast({
title: '登录已过期,请重新登录',
icon: 'none'
});
// 跳转到登录页
setTimeout(() => {
wx.redirectTo({
url: '/pages/login/login'
});
}, 1500);
reject(responseData);
} else {
// 其他业务错误(如参数错误等)
wx.showToast({
title: responseData.msg || '请求失败',
icon: 'none'
});
reject(responseData);
}
}
} else {
// HTTP 状态码非 200
if (statusCode === 401) {
// token可能已过期清除本地认证信息
clearAuthData();
wx.showToast({
title: '登录已过期,请重新登录',
icon: 'none'
});
// 跳转到登录页
setTimeout(() => {
wx.redirectTo({
url: '/pages/login/login'
});
}, 1500);
} else {
// wx.showToast({
// title: `请求错误 ${statusCode}`,
// icon: 'none'
// });
}
reject(res);
}
},
fail: (err) => {
wx.showToast({
title: '网络异常,请稍后重试',
icon: 'none'
});
reject(err);
},
complete: () => {
// 隐藏 loading
loadingHide();
}
});
});
};
module.exports = {
request,
get: (url, data, options = {}) => request({ ...options, url, method: 'GET', data }),
post: (url, data, options = {}) => request({ ...options, url, method: 'POST', data })
};

@ -0,0 +1,117 @@
// utils/token.js - Token管理工具
const TOKEN_KEY = 'token';
const USER_INFO_KEY = 'userInfo';
/**
* 保存token到本地存储
* @param {string} token - 认证令牌
*/
const setToken = (token) => {
try {
wx.setStorageSync(TOKEN_KEY, token);
} catch (error) {
console.error('保存token失败:', error);
}
};
/**
* 获取本地存储的token
* @returns {string} token值
*/
const getToken = () => {
try {
return wx.getStorageSync(TOKEN_KEY) || '';
} catch (error) {
console.error('获取token失败:', error);
return '';
}
};
/**
* 删除本地存储的token
*/
const removeToken = () => {
try {
wx.removeStorageSync(TOKEN_KEY);
} catch (error) {
console.error('删除token失败:', error);
}
};
/**
* 保存用户信息到本地存储
* @param {Object} userInfo - 用户信息对象
*/
const setUserInfo = (userInfo) => {
try {
wx.setStorageSync(USER_INFO_KEY, userInfo);
} catch (error) {
console.error('保存用户信息失败:', error);
}
};
/**
* 获取本地存储的用户信息
* @returns {Object} 用户信息对象
*/
const getUserInfo = () => {
try {
return wx.getStorageSync(USER_INFO_KEY) || null;
} catch (error) {
console.error('获取用户信息失败:', error);
return null;
}
};
/**
* 删除本地存储的用户信息
*/
const removeUserInfo = () => {
try {
wx.removeStorageSync(USER_INFO_KEY);
} catch (error) {
console.error('删除用户信息失败:', error);
}
};
/**
* 清除所有认证相关信息
*/
const clearAuthData = () => {
removeToken();
removeUserInfo();
};
/**
* 检查是否已登录是否有有效token
* @returns {boolean} 是否已登录
*/
const isAuthenticated = () => {
const token = getToken();
return !!token;
};
/**
* 获取完整的认证头信息
* @returns {Object} 包含认证信息的header对象
*/
const getAuthHeader = () => {
const token = getToken();
return {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : ''
};
};
module.exports = {
setToken,
getToken,
removeToken,
setUserInfo,
getUserInfo,
removeUserInfo,
clearAuthData,
isAuthenticated,
getAuthHeader
};
Loading…
Cancel
Save