Files
ylt_diy/lib/api/auth_api.py

222 lines
7.2 KiB
Python
Raw Normal View History

"""
认证相关 API
登录退出修改密码
"""
import hashlib
import secrets
from datetime import datetime, timedelta
from flask import Blueprint, request, jsonify, g
from lib.db import execute_query, execute_update
from lib.logger import log_info, log_error, log_warning
auth_bp = Blueprint('auth', __name__, url_prefix='/api/auth')
def md5_password(password):
"""MD5加密密码"""
return hashlib.md5(password.encode('utf-8')).hexdigest()
def generate_token():
"""生成登录令牌"""
return secrets.token_hex(32)
def get_user_by_token(token):
"""根据token获取用户信息"""
if not token:
return None
try:
user = execute_query(
"SELECT id, username, real_name, token_expire FROM t_daily_report_admin WHERE token = %s",
(token,)
)
if user and user[0]:
user_info = user[0]
if user_info.get('token_expire') and user_info['token_expire'] > datetime.now():
return user_info
except Exception as e:
log_warning(f"[认证] 获取用户信息失败: {e}", 'auth')
return None
def get_current_user():
"""获取当前登录用户从token"""
token = request.headers.get('Authorization', '')
if token.startswith('Bearer '):
token = token[7:]
if not token:
return None
try:
user = execute_query(
"SELECT id, username, real_name, token_expire FROM t_daily_report_admin WHERE token = %s",
(token,)
)
if user and user[0]:
user_info = user[0]
if user_info.get('token_expire') and user_info['token_expire'] > datetime.now():
return user_info
except Exception as e:
log_warning(f"[认证] 获取用户信息失败: {e}", 'auth')
return None
def login_required(f):
"""登录验证装饰器"""
from functools import wraps
@wraps(f)
def decorated_function(*args, **kwargs):
user = get_current_user()
if not user:
return jsonify({'success': False, 'message': '未登录或登录已过期', 'code': 401}), 401
g.current_user = user
return f(*args, **kwargs)
return decorated_function
@auth_bp.route('/login', methods=['POST'])
def login():
"""登录"""
try:
data = request.get_json()
username = data.get('username', '').strip()
password = data.get('password', '').strip()
if not username or not password:
return jsonify({'success': False, 'message': '用户名和密码不能为空'})
# 查询用户
users = execute_query(
"SELECT id, username, password, real_name FROM t_daily_report_admin WHERE username = %s",
(username,)
)
if not users or not users[0]:
log_warning(f"[登录] 用户不存在: {username}", 'auth')
return jsonify({'success': False, 'message': '用户名或密码错误'})
user = users[0]
# 验证密码
password_md5 = md5_password(password)
if user['password'] != password_md5:
log_warning(f"[登录] 密码错误: {username}", 'auth')
return jsonify({'success': False, 'message': '用户名或密码错误'})
# 生成token有效期7天
token = generate_token()
token_expire = datetime.now() + timedelta(days=7)
# 获取客户端IP
client_ip = request.remote_addr
# 更新用户token和登录信息
execute_update(
"""UPDATE t_daily_report_admin
SET token = %s, token_expire = %s, last_login_time = NOW(), last_login_ip = %s
WHERE id = %s""",
(token, token_expire, client_ip, user['id'])
)
log_info(f"[登录] 登录成功: {username}", 'auth')
return jsonify({
'success': True,
'message': '登录成功',
'data': {
'token': token,
'username': user['username'],
'real_name': user.get('real_name', ''),
'expires_in': 7 * 24 * 3600
}
})
except Exception as e:
log_error(f"[登录] 异常: {e}", 'auth', exc_info=True)
return jsonify({'success': False, 'message': '登录失败: ' + str(e)})
@auth_bp.route('/logout', methods=['POST'])
@login_required
def logout():
"""退出登录"""
try:
user = g.current_user
# 清除token
execute_update(
"UPDATE t_daily_report_admin SET token = NULL, token_expire = NULL WHERE id = %s",
(user['id'],)
)
log_info(f"[退出] 用户退出登录: {user['username']}", 'auth')
return jsonify({'success': True, 'message': '退出成功'})
except Exception as e:
log_error(f"[退出] 异常: {e}", 'auth', exc_info=True)
return jsonify({'success': False, 'message': '退出失败: ' + str(e)})
@auth_bp.route('/info', methods=['GET'])
@login_required
def get_user_info():
"""获取当前用户信息"""
try:
user = g.current_user
return jsonify({
'success': True,
'data': {
'id': user['id'],
'username': user['username'],
'real_name': user.get('real_name', '')
}
})
except Exception as e:
return jsonify({'success': False, 'message': str(e)})
@auth_bp.route('/change-password', methods=['POST'])
@login_required
def change_password():
"""修改密码"""
try:
data = request.get_json()
old_password = data.get('old_password', '').strip()
new_password = data.get('new_password', '').strip()
if not old_password or not new_password:
return jsonify({'success': False, 'message': '旧密码和新密码不能为空'})
if len(new_password) < 6:
return jsonify({'success': False, 'message': '新密码长度不能少于6位'})
user = g.current_user
# 验证旧密码
users = execute_query(
"SELECT password FROM t_daily_report_admin WHERE id = %s",
(user['id'],)
)
if not users or not users[0]:
return jsonify({'success': False, 'message': '用户不存在'})
old_password_md5 = md5_password(old_password)
if users[0]['password'] != old_password_md5:
return jsonify({'success': False, 'message': '旧密码错误'})
# 更新密码
new_password_md5 = md5_password(new_password)
execute_update(
"UPDATE t_daily_report_admin SET password = %s, token = NULL, token_expire = NULL, update_time = NOW() WHERE id = %s",
(new_password_md5, user['id'])
)
log_info(f"[修改密码] 用户 {user['username']} 修改密码成功", 'auth')
return jsonify({'success': True, 'message': '密码修改成功,请重新登录'})
except Exception as e:
log_error(f"[修改密码] 异常: {e}", 'auth', exc_info=True)
return jsonify({'success': False, 'message': '修改失败: ' + str(e)})