baodan/dify-main/api/insurance/auth/routes.py

167 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""认证模块 Blueprint。4 个接口:企微登录、账密登录、刷新 Token、退出。"""
from datetime import datetime, timezone
import bcrypt
from flask import Blueprint, request
from insurance.utils.auth import decode_token, generate_token, login_required, get_current_user
from insurance.utils.response import success, error
auth_bp = Blueprint("insurance_auth", __name__)
def _get_user_by_username(username: str):
"""从 wecom_user_mapping 按 username 查询用户。"""
from extensions.ext_database import db
from insurance.db.models import WecomUserMapping
return db.session.query(WecomUserMapping).filter_by(username=username).first()
def _get_user_by_wecom_userid(userid: str):
"""从 wecom_user_mapping 按 wecom_userid 查询用户。"""
from extensions.ext_database import db
from insurance.db.models import WecomUserMapping
return db.session.query(WecomUserMapping).filter_by(wecom_userid=userid).first()
def _sign_token(user) -> dict:
"""签发 JWT 并返回标准响应。"""
token = generate_token(
user_id=str(user.id),
username=user.username or "",
role=user.role or "sales",
department=user.department or "",
)
return success({
"token": token,
"expires_in": 7200,
"user": {
"id": str(user.id),
"username": user.username,
"wecom_userid": user.wecom_userid,
"role": user.role,
"department": user.department,
},
})
def _record_login_log(user_id: str, action: str = "login"):
"""记录登录/登出日志。"""
try:
from extensions.ext_database import db
from insurance.db.models import SystemOperationLog
log = SystemOperationLog(
user_id=user_id,
action=action,
ip=request.remote_addr,
user_agent=request.headers.get("User-Agent", "")[:256],
)
db.session.add(log)
db.session.commit()
except Exception:
pass
# ──────────────────────────────────────────────
# A1.1.2 账密登录
# ──────────────────────────────────────────────
@auth_bp.route("/password-login", methods=["POST"])
def password_login():
"""账密登录POST /api/auth/password-login"""
data = request.get_json(force=True, silent=True) or {}
username = (data.get("username") or "").strip()
password = data.get("password") or ""
# 参数校验
if not username or len(username) < 3 or len(username) > 64:
return error(1001, "参数错误:用户名不能为空")
if not password or len(password) < 8 or len(password) > 128:
return error(1001, "参数错误:密码不能为空")
# 查用户
user = _get_user_by_username(username)
if user is None:
return error(1002, "用户名或密码错误")
# 校验密码
if not user.password_hash:
return error(1002, "用户名或密码错误")
if not bcrypt.checkpw(password.encode("utf-8"), user.password_hash.encode("utf-8")):
return error(1002, "用户名或密码错误")
# 检查状态
if user.status == "disabled":
return error(1004, "账号已被禁用,请联系管理员")
_record_login_log(str(user.id), "login")
return _sign_token(user)
# ──────────────────────────────────────────────
# A1.1.3 刷新 Token
# ──────────────────────────────────────────────
@auth_bp.route("/refresh-token", methods=["POST"])
@login_required
def refresh_token():
"""刷新 TokenPOST /api/auth/refresh-token"""
user = get_current_user()
if not user:
return error(1003, "登录已过期,请重新登录")
# 旧 Token 加入 Redis 黑名单
try:
from extensions.ext_redis import redis_client
from insurance.config import REDIS_TOKEN_BLACKLIST_PREFIX
old_token = request.headers.get("Authorization", "")[7:]
redis_client.setex(f"{REDIS_TOKEN_BLACKLIST_PREFIX}{old_token}", 7200, "1")
except Exception:
pass
# 签发新 Token
from extensions.ext_database import db
from insurance.db.models import WecomUserMapping
db_user = db.session.query(WecomUserMapping).filter_by(id=user.get("user_id")).first()
if not db_user:
return error(1003, "登录已过期,请重新登录")
return _sign_token(db_user)
# ──────────────────────────────────────────────
# A1.1.4 退出登录
# ──────────────────────────────────────────────
@auth_bp.route("/logout", methods=["POST"])
@login_required
def logout():
"""退出登录POST /api/auth/logout"""
user = get_current_user()
# Token 加入 Redis 黑名单
try:
from extensions.ext_redis import redis_client
from insurance.config import REDIS_TOKEN_BLACKLIST_PREFIX
token = request.headers.get("Authorization", "")[7:]
redis_client.setex(f"{REDIS_TOKEN_BLACKLIST_PREFIX}{token}", 7200, "1")
except Exception:
pass
if user:
_record_login_log(user.get("user_id", ""), "logout")
return success()
# ──────────────────────────────────────────────
# A1.1.1 企微登录POST供企微 H5 调用)
# ──────────────────────────────────────────────
@auth_bp.route("/wework-login", methods=["POST"])
def wework_login():
"""企微 OAuth 登录POST /api/auth/wework-login"""
data = request.get_json(force=True, silent=True) or {}
code = (data.get("code") or "").strip()
if not code or len(code) > 512:
return error(1001, "参数错误:企微授权码不能为空")
# 调企微 API 换用户信息(需要企微配置就绪后实现)
# 暂时返回占位
return error(3001, "企微 API 配置中,请使用账密登录")