增加登陆功能,并修改正式环境用5002端口
This commit is contained in:
@@ -24,6 +24,9 @@ download_bp = Blueprint('download', __name__, url_prefix='/api')
|
||||
# 求和数据管理蓝图
|
||||
sum_data_bp = Blueprint('sum_data', __name__, url_prefix='/api')
|
||||
|
||||
# 认证蓝图
|
||||
from .auth_api import auth_bp
|
||||
|
||||
# 导入各模块的路由
|
||||
from . import config_api, entity_api, field_api, report_api, init_api, download_api, sum_data_api
|
||||
|
||||
@@ -37,3 +40,4 @@ def register_blueprints(app):
|
||||
app.register_blueprint(init_bp)
|
||||
app.register_blueprint(download_bp)
|
||||
app.register_blueprint(sum_data_bp)
|
||||
app.register_blueprint(auth_bp)
|
||||
|
||||
221
lib/api/auth_api.py
Normal file
221
lib/api/auth_api.py
Normal file
@@ -0,0 +1,221 @@
|
||||
"""
|
||||
认证相关 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)})
|
||||
Reference in New Issue
Block a user