first commit
This commit is contained in:
262
AIEC-server/utils/token/token-manager.js
Normal file
262
AIEC-server/utils/token/token-manager.js
Normal file
@ -0,0 +1,262 @@
|
||||
/**
|
||||
* Token管理工具类
|
||||
*/
|
||||
class TokenManager {
|
||||
|
||||
static TOKEN_KEY = 'yundage_token';
|
||||
static USER_KEY = 'yundage_user';
|
||||
static REFRESH_TOKEN_KEY = 'yundage_refresh_token';
|
||||
|
||||
/**
|
||||
* 保存Token到本地存储
|
||||
* @param {string} token - JWT Token
|
||||
* @param {boolean} remember - 是否记住登录状态
|
||||
*/
|
||||
static saveToken(token, remember = false) {
|
||||
try {
|
||||
const storage = remember ? localStorage : sessionStorage;
|
||||
storage.setItem(this.TOKEN_KEY, token);
|
||||
|
||||
// 解析token获取过期时间
|
||||
const payload = this.parseToken(token);
|
||||
if (payload && payload.exp) {
|
||||
storage.setItem(this.TOKEN_KEY + '_exp', payload.exp.toString());
|
||||
}
|
||||
|
||||
console.log('Token saved successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to save token:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token
|
||||
* @returns {string|null} Token或null
|
||||
*/
|
||||
static getToken() {
|
||||
try {
|
||||
// 优先从sessionStorage获取
|
||||
let token = sessionStorage.getItem(this.TOKEN_KEY);
|
||||
|
||||
// 如果sessionStorage没有,从localStorage获取
|
||||
if (!token) {
|
||||
token = localStorage.getItem(this.TOKEN_KEY);
|
||||
}
|
||||
|
||||
// 检查token是否过期
|
||||
if (token && this.isTokenExpired(token)) {
|
||||
this.removeToken();
|
||||
return null;
|
||||
}
|
||||
|
||||
return token;
|
||||
} catch (error) {
|
||||
console.error('Failed to get token:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除Token
|
||||
*/
|
||||
static removeToken() {
|
||||
try {
|
||||
sessionStorage.removeItem(this.TOKEN_KEY);
|
||||
sessionStorage.removeItem(this.TOKEN_KEY + '_exp');
|
||||
localStorage.removeItem(this.TOKEN_KEY);
|
||||
localStorage.removeItem(this.TOKEN_KEY + '_exp');
|
||||
|
||||
// 同时清除用户信息
|
||||
this.removeUser();
|
||||
|
||||
console.log('Token removed successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to remove token:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Token是否有效
|
||||
* @returns {boolean} 是否有效
|
||||
*/
|
||||
static isTokenValid() {
|
||||
const token = this.getToken();
|
||||
return token !== null && !this.isTokenExpired(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Token是否过期
|
||||
* @param {string} token - JWT Token
|
||||
* @returns {boolean} 是否过期
|
||||
*/
|
||||
static isTokenExpired(token) {
|
||||
try {
|
||||
const payload = this.parseToken(token);
|
||||
if (!payload || !payload.exp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// JWT的exp是秒级时间戳,JavaScript的Date.now()是毫秒级
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
return payload.exp < currentTime;
|
||||
} catch (error) {
|
||||
console.error('Failed to check token expiration:', error);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JWT Token
|
||||
* @param {string} token - JWT Token
|
||||
* @returns {object|null} 解析后的payload
|
||||
*/
|
||||
static parseToken(token) {
|
||||
try {
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) {
|
||||
throw new Error('Invalid token format');
|
||||
}
|
||||
|
||||
const payload = parts[1];
|
||||
const decoded = atob(payload.replace(/_/g, '/').replace(/-/g, '+'));
|
||||
return JSON.parse(decoded);
|
||||
} catch (error) {
|
||||
console.error('Failed to parse token:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存用户信息
|
||||
* @param {object} user - 用户信息
|
||||
* @param {boolean} remember - 是否记住登录状态
|
||||
*/
|
||||
static saveUser(user, remember = false) {
|
||||
try {
|
||||
const storage = remember ? localStorage : sessionStorage;
|
||||
storage.setItem(this.USER_KEY, JSON.stringify(user));
|
||||
console.log('User info saved successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to save user info:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @returns {object|null} 用户信息或null
|
||||
*/
|
||||
static getUser() {
|
||||
try {
|
||||
// 优先从sessionStorage获取
|
||||
let userStr = sessionStorage.getItem(this.USER_KEY);
|
||||
|
||||
// 如果sessionStorage没有,从localStorage获取
|
||||
if (!userStr) {
|
||||
userStr = localStorage.getItem(this.USER_KEY);
|
||||
}
|
||||
|
||||
return userStr ? JSON.parse(userStr) : null;
|
||||
} catch (error) {
|
||||
console.error('Failed to get user info:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除用户信息
|
||||
*/
|
||||
static removeUser() {
|
||||
try {
|
||||
sessionStorage.removeItem(this.USER_KEY);
|
||||
localStorage.removeItem(this.USER_KEY);
|
||||
console.log('User info removed successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to remove user info:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存刷新Token
|
||||
* @param {string} refreshToken - 刷新Token
|
||||
*/
|
||||
static saveRefreshToken(refreshToken) {
|
||||
try {
|
||||
localStorage.setItem(this.REFRESH_TOKEN_KEY, refreshToken);
|
||||
} catch (error) {
|
||||
console.error('Failed to save refresh token:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取刷新Token
|
||||
* @returns {string|null} 刷新Token或null
|
||||
*/
|
||||
static getRefreshToken() {
|
||||
try {
|
||||
return localStorage.getItem(this.REFRESH_TOKEN_KEY);
|
||||
} catch (error) {
|
||||
console.error('Failed to get refresh token:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除刷新Token
|
||||
*/
|
||||
static removeRefreshToken() {
|
||||
try {
|
||||
localStorage.removeItem(this.REFRESH_TOKEN_KEY);
|
||||
} catch (error) {
|
||||
console.error('Failed to remove refresh token:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有认证信息
|
||||
*/
|
||||
static clearAll() {
|
||||
this.removeToken();
|
||||
this.removeUser();
|
||||
this.removeRefreshToken();
|
||||
console.log('All auth data cleared');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token头部信息用于HTTP请求
|
||||
* @returns {object|null} Authorization头部或null
|
||||
*/
|
||||
static getAuthHeader() {
|
||||
const token = this.getToken();
|
||||
return token ? { 'Authorization': `Bearer ${token}` } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户是否已登录
|
||||
* @returns {boolean} 是否已登录
|
||||
*/
|
||||
static isLoggedIn() {
|
||||
return this.isTokenValid() && this.getUser() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Token剩余有效时间(秒)
|
||||
* @returns {number} 剩余秒数,-1表示已过期或无效
|
||||
*/
|
||||
static getTokenRemainingTime() {
|
||||
const token = this.getToken();
|
||||
if (!token) return -1;
|
||||
|
||||
const payload = this.parseToken(token);
|
||||
if (!payload || !payload.exp) return -1;
|
||||
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
const remainingTime = payload.exp - currentTime;
|
||||
|
||||
return remainingTime > 0 ? remainingTime : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// 导出供其他模块使用
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = TokenManager;
|
||||
}
|
||||
2
AIEC-server/utils/token/token-manager.jsZone.Identifier
Normal file
2
AIEC-server/utils/token/token-manager.jsZone.Identifier
Normal file
@ -0,0 +1,2 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
258
AIEC-server/utils/validation/form-validator.js
Normal file
258
AIEC-server/utils/validation/form-validator.js
Normal file
@ -0,0 +1,258 @@
|
||||
/**
|
||||
* 表单验证工具类
|
||||
*/
|
||||
class FormValidator {
|
||||
|
||||
/**
|
||||
* 验证手机号格式
|
||||
* @param {string} phone - 手机号
|
||||
* @returns {boolean} 是否有效
|
||||
*/
|
||||
static validatePhone(phone) {
|
||||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||||
return phoneRegex.test(phone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证邮箱格式
|
||||
* @param {string} email - 邮箱地址
|
||||
* @returns {boolean} 是否有效
|
||||
*/
|
||||
static validateEmail(email) {
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证账号格式(手机号或邮箱)
|
||||
* @param {string} account - 账号
|
||||
* @returns {object} 验证结果 {valid, type, message}
|
||||
*/
|
||||
static validateAccount(account) {
|
||||
if (!account || account.trim() === '') {
|
||||
return {
|
||||
valid: false,
|
||||
type: null,
|
||||
message: '请输入手机号或邮箱'
|
||||
};
|
||||
}
|
||||
|
||||
account = account.trim();
|
||||
|
||||
// 判断是否为手机号
|
||||
if (/^\d+$/.test(account)) {
|
||||
if (this.validatePhone(account)) {
|
||||
return {
|
||||
valid: true,
|
||||
type: 'phone',
|
||||
message: ''
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
valid: false,
|
||||
type: 'phone',
|
||||
message: '手机号格式不正确'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否为邮箱
|
||||
if (account.includes('@')) {
|
||||
if (this.validateEmail(account)) {
|
||||
return {
|
||||
valid: true,
|
||||
type: 'email',
|
||||
message: ''
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
valid: false,
|
||||
type: 'email',
|
||||
message: '邮箱格式不正确'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
type: null,
|
||||
message: '请输入正确的手机号或邮箱格式'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证验证码格式
|
||||
* @param {string} code - 验证码
|
||||
* @returns {object} 验证结果 {valid, message}
|
||||
*/
|
||||
static validateVerifyCode(code) {
|
||||
if (!code || code.trim() === '') {
|
||||
return {
|
||||
valid: false,
|
||||
message: '请输入验证码'
|
||||
};
|
||||
}
|
||||
|
||||
code = code.trim();
|
||||
|
||||
if (!/^\d{6}$/.test(code)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '验证码必须为6位数字'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
message: ''
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户名格式
|
||||
* @param {string} username - 用户名
|
||||
* @returns {object} 验证结果 {valid, message}
|
||||
*/
|
||||
static validateUsername(username) {
|
||||
if (!username || username.trim() === '') {
|
||||
return {
|
||||
valid: false,
|
||||
message: '请输入用户名'
|
||||
};
|
||||
}
|
||||
|
||||
username = username.trim();
|
||||
|
||||
if (username.length < 2 || username.length > 20) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '用户名长度应在2-20个字符之间'
|
||||
};
|
||||
}
|
||||
|
||||
// 允许中文、英文、数字、下划线
|
||||
if (!/^[\u4e00-\u9fa5a-zA-Z0-9_]+$/.test(username)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: '用户名只能包含中文、英文、数字和下划线'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
message: ''
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误信息
|
||||
* @param {string} fieldId - 字段ID
|
||||
* @param {string} message - 错误信息
|
||||
*/
|
||||
static showError(fieldId, message) {
|
||||
const field = document.getElementById(fieldId);
|
||||
const errorElement = document.getElementById(fieldId + 'Error');
|
||||
|
||||
if (field) {
|
||||
field.classList.add('error');
|
||||
}
|
||||
|
||||
if (errorElement) {
|
||||
errorElement.textContent = message;
|
||||
errorElement.classList.remove('hidden');
|
||||
errorElement.classList.add('show');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除错误信息
|
||||
* @param {string} fieldId - 字段ID
|
||||
*/
|
||||
static clearError(fieldId) {
|
||||
const field = document.getElementById(fieldId);
|
||||
const errorElement = document.getElementById(fieldId + 'Error');
|
||||
|
||||
if (field) {
|
||||
field.classList.remove('error');
|
||||
}
|
||||
|
||||
if (errorElement) {
|
||||
errorElement.classList.add('hidden');
|
||||
errorElement.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示成功信息
|
||||
* @param {string} fieldId - 字段ID
|
||||
* @param {string} message - 成功信息
|
||||
*/
|
||||
static showSuccess(fieldId, message = '格式正确') {
|
||||
const field = document.getElementById(fieldId);
|
||||
|
||||
if (field) {
|
||||
field.classList.remove('error');
|
||||
|
||||
// 移除可能存在的成功提示
|
||||
const existingFeedback = field.parentNode.querySelector('.valid-feedback');
|
||||
if (existingFeedback) {
|
||||
existingFeedback.remove();
|
||||
}
|
||||
|
||||
// 添加新的成功提示
|
||||
const feedback = document.createElement('div');
|
||||
feedback.className = 'valid-feedback';
|
||||
feedback.textContent = message;
|
||||
field.parentNode.appendChild(feedback);
|
||||
|
||||
// 3秒后移除成功提示
|
||||
setTimeout(() => {
|
||||
if (feedback.parentNode) {
|
||||
feedback.remove();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实时验证字段
|
||||
* @param {string} fieldId - 字段ID
|
||||
* @param {Function} validator - 验证函数
|
||||
*/
|
||||
static setupRealTimeValidation(fieldId, validator) {
|
||||
const field = document.getElementById(fieldId);
|
||||
|
||||
if (!field) return;
|
||||
|
||||
// 输入时验证
|
||||
field.addEventListener('input', () => {
|
||||
const result = validator(field.value);
|
||||
|
||||
if (result.valid) {
|
||||
this.clearError(fieldId);
|
||||
} else if (field.value.trim() !== '') {
|
||||
// 只有在有输入内容时才显示错误
|
||||
this.showError(fieldId, result.message);
|
||||
}
|
||||
});
|
||||
|
||||
// 失去焦点时验证
|
||||
field.addEventListener('blur', () => {
|
||||
const result = validator(field.value);
|
||||
|
||||
if (result.valid) {
|
||||
this.clearError(fieldId);
|
||||
if (result.type) {
|
||||
this.showSuccess(fieldId, `${result.type === 'phone' ? '手机号' : '邮箱'}格式正确`);
|
||||
}
|
||||
} else {
|
||||
this.showError(fieldId, result.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 导出供其他模块使用
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = FormValidator;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
[ZoneTransfer]
|
||||
ZoneId=3
|
||||
Reference in New Issue
Block a user