Fix Casdoor login URL issue and enhance user experience. Updated organization name in environment variables, improved error handling for login failures, and added automatic redirection to Casdoor login page. Enhanced user interface with new styles for user display in the navigation bar. Updated logging to capture detailed user actions and application events.
This commit is contained in:
@@ -40,5 +40,5 @@ CASDOOR_ENDPOINT=https://casdoor.dhdx.fun
|
||||
CASDOOR_CLIENT_ID=8f323670e073612794ef
|
||||
CASDOOR_CLIENT_SECRET=bc37d89f9220fe5a46c17125c731d2daf22f1299
|
||||
CASDOOR_REDIRECT_URI=https://zd.dhdx.fun/callback
|
||||
CASDOOR_ORGANIZATION_NAME=built-in
|
||||
CASDOOR_ORGANIZATION_NAME=dahua
|
||||
CASDOOR_APPLICATION_NAME=4an
|
||||
Binary file not shown.
Binary file not shown.
+98
-98
@@ -1,99 +1,99 @@
|
||||
from flask import Flask, redirect, url_for
|
||||
from flask_login import LoginManager
|
||||
from flask_migrate import Migrate
|
||||
from scripts.models import db, User, Device, WorkOrder, WorkOrderHandling, ManualWorkOrder
|
||||
from config import Config
|
||||
from views.auth import init_auth_routes
|
||||
from views.work_order import init_work_order_routes
|
||||
from views.statistics import init_statistics_routes
|
||||
from views.history import init_history_routes
|
||||
from views.manual import init_manual_routes
|
||||
from views.device import init_device_routes
|
||||
from views.about import init_about_routes
|
||||
from views.dashboard import init_dashboard_routes
|
||||
from views.user import init_user_routes
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import os
|
||||
from utils.error_handlers import ErrorHandler
|
||||
|
||||
def create_app():
|
||||
# 创建应用实例
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
|
||||
# 配置日志记录
|
||||
if not os.path.exists('logs'):
|
||||
os.makedirs('logs')
|
||||
|
||||
# 创建日志处理器,设置最大文件大小为10MB,保留5个备份文件
|
||||
file_handler = RotatingFileHandler(
|
||||
'logs/app.log',
|
||||
maxBytes=10*1024*1024, # 10MB
|
||||
backupCount=5,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
# 设置日志格式
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s %(levelname)s: %(message)s',
|
||||
'%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
# 设置日志级别
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.logger.info('应用启动')
|
||||
|
||||
# 初始化数据库
|
||||
db.init_app(app)
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
# 初始化异常处理器
|
||||
error_handler = ErrorHandler(app)
|
||||
|
||||
# 初始化登录管理器
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'login'
|
||||
# 默认未登录提示改为中文
|
||||
login_manager.login_message = '请先登录以访问此页面'
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
try:
|
||||
return db.session.get(User, user_id)
|
||||
except Exception as e:
|
||||
app.logger.error(f"加载用户时出错: {str(e)}")
|
||||
return None
|
||||
|
||||
# 注册路由
|
||||
@app.route('/')
|
||||
def index():
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
# 初始化各模块路由
|
||||
init_auth_routes(app)
|
||||
init_work_order_routes(app)
|
||||
init_statistics_routes(app)
|
||||
init_history_routes(app)
|
||||
init_manual_routes(app)
|
||||
init_device_routes(app)
|
||||
init_about_routes(app)
|
||||
init_dashboard_routes(app)
|
||||
init_user_routes(app)
|
||||
|
||||
return app
|
||||
|
||||
app = create_app()
|
||||
|
||||
if __name__ == '__main__':
|
||||
with app.app_context():
|
||||
try:
|
||||
db.create_all()
|
||||
app.logger.info("数据库表创建成功")
|
||||
except Exception as e:
|
||||
app.logger.error(f"创建数据库表时出错: {str(e)}")
|
||||
from flask import Flask, redirect, url_for
|
||||
from flask_login import LoginManager
|
||||
from flask_migrate import Migrate
|
||||
from scripts.models import db, User, Device, WorkOrder, WorkOrderHandling, ManualWorkOrder
|
||||
from config import Config
|
||||
from views.auth import init_auth_routes
|
||||
from views.work_order import init_work_order_routes
|
||||
from views.statistics import init_statistics_routes
|
||||
from views.history import init_history_routes
|
||||
from views.manual import init_manual_routes
|
||||
from views.device import init_device_routes
|
||||
from views.about import init_about_routes
|
||||
from views.dashboard import init_dashboard_routes
|
||||
from views.user import init_user_routes
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import os
|
||||
from utils.error_handlers import ErrorHandler
|
||||
|
||||
def create_app():
|
||||
# 创建应用实例
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
|
||||
# 配置日志记录
|
||||
if not os.path.exists('logs'):
|
||||
os.makedirs('logs')
|
||||
|
||||
# 创建日志处理器,设置最大文件大小为10MB,保留5个备份文件
|
||||
file_handler = RotatingFileHandler(
|
||||
'logs/app.log',
|
||||
maxBytes=10*1024*1024, # 10MB
|
||||
backupCount=5,
|
||||
encoding='utf-8'
|
||||
)
|
||||
|
||||
# 设置日志格式
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s %(levelname)s: %(message)s',
|
||||
'%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
# 设置日志级别
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.logger.info('应用启动')
|
||||
|
||||
# 初始化数据库
|
||||
db.init_app(app)
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
# 初始化异常处理器
|
||||
error_handler = ErrorHandler(app)
|
||||
|
||||
# 初始化登录管理器
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'login'
|
||||
# 默认未登录提示改为中文
|
||||
login_manager.login_message = '请先登录以访问此页面'
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
try:
|
||||
return db.session.get(User, user_id)
|
||||
except Exception as e:
|
||||
app.logger.error(f"加载用户时出错: {str(e)}")
|
||||
return None
|
||||
|
||||
# 注册路由
|
||||
@app.route('/')
|
||||
def index():
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
# 初始化各模块路由
|
||||
init_auth_routes(app)
|
||||
init_work_order_routes(app)
|
||||
init_statistics_routes(app)
|
||||
init_history_routes(app)
|
||||
init_manual_routes(app)
|
||||
init_device_routes(app)
|
||||
init_about_routes(app)
|
||||
init_dashboard_routes(app)
|
||||
init_user_routes(app)
|
||||
|
||||
return app
|
||||
|
||||
app = create_app()
|
||||
|
||||
if __name__ == '__main__':
|
||||
with app.app_context():
|
||||
try:
|
||||
db.create_all()
|
||||
app.logger.info("数据库表创建成功")
|
||||
except Exception as e:
|
||||
app.logger.error(f"创建数据库表时出错: {str(e)}")
|
||||
app.run(host='0.0.0.0', port=18019, debug=True)
|
||||
+1101
-40
File diff suppressed because it is too large
Load Diff
@@ -93,6 +93,15 @@
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.nav_user_name {
|
||||
color: rgba(255,255,255,0.9) !important;
|
||||
padding: 0.5rem 1rem !important;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 接单平台样式 */
|
||||
.receive_container {
|
||||
padding: 20px;
|
||||
|
||||
@@ -84,6 +84,9 @@
|
||||
</ul>
|
||||
<ul class="navbar-nav nav_menu">
|
||||
{% if current_user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<span class="nav_link nav_user_name">{{ current_user.name }}</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav_link" href="{{ url_for('logout') }}">退出</a>
|
||||
</li>
|
||||
|
||||
+25
-18
@@ -5,25 +5,32 @@
|
||||
<h2 class="login_title">欢迎使用</h2>
|
||||
<div class="login_subtitle">大化重点项目工单管理平台</div>
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="login_error">
|
||||
{% for message in messages %}
|
||||
{{ message }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="login_error">
|
||||
{% for message in messages %}
|
||||
{{ message }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Casdoor 登录按钮 -->
|
||||
<div class="login_casdoor_section">
|
||||
<a href="{{ url_for('casdoor_login') }}" class="login_button login_button_casdoor">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-right: 8px;">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/>
|
||||
</svg>
|
||||
使用 Casdoor 登录
|
||||
</a>
|
||||
</div>
|
||||
<!-- Casdoor 登录跳转 -->
|
||||
<div class="login_casdoor_section">
|
||||
<a href="{{ url_for('casdoor_login') }}" class="login_button login_button_casdoor">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-right: 8px;">
|
||||
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor"/>
|
||||
</svg>
|
||||
使用 Casdoor 登录
|
||||
</a>
|
||||
<p style="margin-top: 12px; color: #666;">正在跳转至 Casdoor 登录页,如未跳转请点击按钮。</p>
|
||||
</div>
|
||||
<script>
|
||||
// 自动跳转至 Casdoor 登录
|
||||
window.onload = function() {
|
||||
window.location.href = "{{ url_for('casdoor_login') }}";
|
||||
};
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Binary file not shown.
@@ -123,6 +123,136 @@ class CasdoorAuth:
|
||||
session.pop('casdoor_state', None)
|
||||
return True
|
||||
return False
|
||||
|
||||
def login_with_password(self, username, password):
|
||||
"""
|
||||
使用用户名和密码通过 API 登录 Casdoor
|
||||
|
||||
Args:
|
||||
username: 用户名
|
||||
password: 密码
|
||||
|
||||
Returns:
|
||||
dict: 包含 access_token 和用户信息的字典,失败返回 None
|
||||
"""
|
||||
# Casdoor 使用 OAuth 2.0 password grant 方式登录
|
||||
# 端点应该是 /api/login/oauth/access_token
|
||||
login_url = f"{self.endpoint}/api/login/oauth/access_token"
|
||||
|
||||
# OAuth 2.0 password grant 需要的参数
|
||||
data = {
|
||||
'grant_type': 'password',
|
||||
'username': username,
|
||||
'password': password,
|
||||
'client_id': self.client_id,
|
||||
'client_secret': self.client_secret,
|
||||
'scope': 'openid profile email phone'
|
||||
}
|
||||
|
||||
try:
|
||||
current_app.logger.info(f"尝试 Casdoor OAuth password grant 登录: username={username}")
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
# OAuth 2.0 password grant 通常使用 form-data 格式
|
||||
response = requests.post(login_url, data=data, timeout=10)
|
||||
|
||||
# 如果返回 415,尝试 JSON 格式
|
||||
if response.status_code == 415:
|
||||
response = requests.post(login_url, json=data, timeout=10)
|
||||
|
||||
# 检查响应状态
|
||||
if response.status_code != 200:
|
||||
try:
|
||||
error_msg = response.text[:200] # 限制错误消息长度
|
||||
current_app.logger.warning(f"Casdoor OAuth 登录失败 (状态码 {response.status_code}): {error_msg}")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
# 尝试解析 JSON 响应
|
||||
try:
|
||||
result = response.json()
|
||||
except ValueError as e:
|
||||
try:
|
||||
current_app.logger.error(f"Casdoor API 登录响应不是有效的 JSON: {str(e)}, 响应内容: {response.text[:200]}")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
# 检查 result 是否为 None
|
||||
if result is None:
|
||||
try:
|
||||
current_app.logger.error("Casdoor API 登录返回 None")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
# OAuth 2.0 返回格式通常是 {"access_token": "...", "token_type": "Bearer", ...}
|
||||
# 或者 Casdoor 格式 {"status": "ok", "data": {"access_token": "..."}}
|
||||
access_token = None
|
||||
if isinstance(result, dict):
|
||||
# 检查是否有错误
|
||||
if result.get('status') == 'error':
|
||||
try:
|
||||
error_msg = result.get('msg', '未知错误')
|
||||
current_app.logger.warning(f"Casdoor OAuth 登录错误: {error_msg}")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
# 标准 OAuth 2.0 格式
|
||||
access_token = result.get('access_token')
|
||||
|
||||
# Casdoor 格式 {"status": "ok", "data": {...}}
|
||||
if not access_token and result.get('status') == 'ok':
|
||||
token_data = result.get('data', {})
|
||||
if isinstance(token_data, dict):
|
||||
access_token = token_data.get('access_token') or token_data.get('token') or token_data.get('accessToken')
|
||||
|
||||
# 如果还是没有,尝试其他可能的字段
|
||||
if not access_token:
|
||||
access_token = result.get('token') or result.get('accessToken')
|
||||
|
||||
# 如果还是没有 token,尝试从其他字段获取
|
||||
if not access_token and 'data' in result and isinstance(result.get('data'), dict):
|
||||
access_token = result['data'].get('access_token') or result['data'].get('token')
|
||||
|
||||
if not access_token:
|
||||
try:
|
||||
current_app.logger.warning(f"Casdoor API 登录返回格式异常,未找到 token: {result}")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
# 使用 token 获取用户信息
|
||||
user_info = self.get_user_info(access_token)
|
||||
if not user_info:
|
||||
return None
|
||||
|
||||
return {
|
||||
'access_token': access_token,
|
||||
'user_info': user_info
|
||||
}
|
||||
except requests.exceptions.Timeout:
|
||||
try:
|
||||
current_app.logger.error("Casdoor API 登录超时")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
except requests.exceptions.RequestException as e:
|
||||
try:
|
||||
current_app.logger.error(f"Casdoor API 登录失败: {str(e)}")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
except Exception as e:
|
||||
try:
|
||||
current_app.logger.error(f"Casdoor API 登录发生未知错误: {str(e)}")
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
# 创建全局实例
|
||||
|
||||
Binary file not shown.
+112
-112
@@ -1,113 +1,113 @@
|
||||
from flask import render_template, request, redirect, url_for, flash, session
|
||||
import urllib.parse
|
||||
from flask_login import login_user, login_required, logout_user, current_user
|
||||
from scripts.models import db, User
|
||||
from utils.casdoor_auth import casdoor_auth
|
||||
|
||||
def init_auth_routes(app):
|
||||
# 初始化 Casdoor 认证
|
||||
casdoor_auth.init_app(app)
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
# 已登录直接进入仪表盘,否则跳转 Casdoor 登录
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('casdoor_login'))
|
||||
|
||||
@app.route('/casdoor/login')
|
||||
def casdoor_login():
|
||||
"""Casdoor 登录入口,重定向到 Casdoor 授权页面"""
|
||||
if not app.config.get('CASDOOR_CLIENT_ID'):
|
||||
flash('Casdoor 未配置,请使用手机号登录')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
auth_url = casdoor_auth.get_authorization_url()
|
||||
app.logger.info('重定向到 Casdoor 授权页面')
|
||||
return redirect(auth_url)
|
||||
|
||||
@app.route('/callback')
|
||||
def casdoor_callback():
|
||||
"""Casdoor OAuth 回调处理"""
|
||||
code = request.args.get('code')
|
||||
state = request.args.get('state')
|
||||
error = request.args.get('error')
|
||||
|
||||
if error:
|
||||
app.logger.error(f'Casdoor 认证失败: {error}')
|
||||
flash(f'登录失败: {error}')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
if not code:
|
||||
app.logger.warning('Casdoor 回调缺少授权码')
|
||||
flash('登录失败:未收到授权码')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
# 验证 state 参数
|
||||
if not casdoor_auth.verify_state(state):
|
||||
app.logger.warning('Casdoor state 验证失败,可能存在 CSRF 攻击')
|
||||
flash('登录失败:安全验证失败')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
# 获取访问令牌
|
||||
token_data = casdoor_auth.get_token(code)
|
||||
if not token_data or 'access_token' not in token_data:
|
||||
app.logger.error('获取 Casdoor token 失败')
|
||||
flash('登录失败:无法获取访问令牌')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
access_token = token_data['access_token']
|
||||
|
||||
# 获取用户信息
|
||||
user_info = casdoor_auth.get_user_info(access_token)
|
||||
if not user_info:
|
||||
app.logger.error('获取 Casdoor 用户信息失败')
|
||||
flash('登录失败:无法获取用户信息')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
# 从 Casdoor 用户信息中提取数据
|
||||
# Casdoor 返回的用户信息可能包含:name, email, phone, id 等字段
|
||||
casdoor_id = user_info.get('sub') or user_info.get('id') or user_info.get('name')
|
||||
phone = user_info.get('phone') or user_info.get('phoneNumber') or casdoor_id
|
||||
name = user_info.get('name') or user_info.get('displayName') or '未知用户'
|
||||
|
||||
app.logger.info(f'Casdoor 用户信息: {user_info}')
|
||||
|
||||
# 查找或创建本地用户
|
||||
user = User.query.filter_by(phone=phone).first()
|
||||
if not user:
|
||||
# 如果用户不存在,创建新用户
|
||||
user = User(
|
||||
phone=phone,
|
||||
name=name,
|
||||
branch=user_info.get('affiliation', ''),
|
||||
role=user_info.get('type', '')
|
||||
)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
app.logger.info(f'创建新用户: {name}({phone})')
|
||||
else:
|
||||
# 更新用户信息
|
||||
user.name = name
|
||||
if user_info.get('affiliation'):
|
||||
user.branch = user_info.get('affiliation')
|
||||
if user_info.get('type'):
|
||||
user.role = user_info.get('type')
|
||||
db.session.commit()
|
||||
app.logger.info(f'更新用户信息: {name}({phone})')
|
||||
|
||||
# 登录用户
|
||||
login_user(user)
|
||||
app.logger.info(f'用户 {user.name}({user.phone}) 通过 Casdoor 登录成功')
|
||||
|
||||
# 清除可能残留的未登录提示
|
||||
session.pop('_flashes', None)
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
app.logger.info(f'用户 {current_user.name}({current_user.phone}) 登出')
|
||||
logout_user()
|
||||
# 仅退出本地会话,保留 Casdoor 登录态
|
||||
from flask import render_template, request, redirect, url_for, flash, session
|
||||
import urllib.parse
|
||||
from flask_login import login_user, login_required, logout_user, current_user
|
||||
from scripts.models import db, User
|
||||
from utils.casdoor_auth import casdoor_auth
|
||||
|
||||
def init_auth_routes(app):
|
||||
# 初始化 Casdoor 认证
|
||||
casdoor_auth.init_app(app)
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
# 如果已登录,直接跳转到仪表盘
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('dashboard'))
|
||||
# 未登录直接跳转 Casdoor 登录页
|
||||
return redirect(url_for('casdoor_login'))
|
||||
|
||||
@app.route('/casdoor/login')
|
||||
def casdoor_login():
|
||||
"""重定向到 Casdoor 登录页面"""
|
||||
if not app.config.get('CASDOOR_CLIENT_ID'):
|
||||
flash('Casdoor 未配置,无法登录')
|
||||
return redirect(url_for('login'))
|
||||
auth_url = casdoor_auth.get_authorization_url()
|
||||
app.logger.info('重定向到 Casdoor 授权页面')
|
||||
return redirect(auth_url)
|
||||
|
||||
@app.route('/callback')
|
||||
def casdoor_callback():
|
||||
"""Casdoor OAuth 回调处理"""
|
||||
code = request.args.get('code')
|
||||
state = request.args.get('state')
|
||||
error = request.args.get('error')
|
||||
|
||||
if error:
|
||||
app.logger.error(f'Casdoor 认证失败: {error}')
|
||||
flash(f'登录失败: {error}')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
if not code:
|
||||
app.logger.warning('Casdoor 回调缺少授权码')
|
||||
flash('登录失败:未收到授权码')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
# 验证 state 参数
|
||||
if not casdoor_auth.verify_state(state):
|
||||
app.logger.warning('Casdoor state 验证失败,可能存在 CSRF 攻击')
|
||||
flash('登录失败:安全验证失败')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
# 获取访问令牌
|
||||
token_data = casdoor_auth.get_token(code)
|
||||
if not token_data or 'access_token' not in token_data:
|
||||
app.logger.error('获取 Casdoor token 失败')
|
||||
flash('登录失败:无法获取访问令牌')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
access_token = token_data['access_token']
|
||||
|
||||
# 获取用户信息
|
||||
user_info = casdoor_auth.get_user_info(access_token)
|
||||
if not user_info:
|
||||
app.logger.error('获取 Casdoor 用户信息失败')
|
||||
flash('登录失败:无法获取用户信息')
|
||||
return redirect(url_for('login'))
|
||||
|
||||
# 从 Casdoor 用户信息中提取数据
|
||||
# Casdoor 返回的用户信息可能包含:name, email, phone, id 等字段
|
||||
casdoor_id = user_info.get('sub') or user_info.get('id') or user_info.get('name')
|
||||
phone = user_info.get('phone') or user_info.get('phoneNumber') or casdoor_id
|
||||
name = user_info.get('name') or user_info.get('displayName') or '未知用户'
|
||||
|
||||
app.logger.info(f'Casdoor 用户信息: {user_info}')
|
||||
|
||||
# 查找或创建本地用户
|
||||
user = User.query.filter_by(phone=phone).first()
|
||||
if not user:
|
||||
# 如果用户不存在,创建新用户
|
||||
user = User(
|
||||
phone=phone,
|
||||
name=name,
|
||||
branch=user_info.get('affiliation', ''),
|
||||
role=user_info.get('type', '')
|
||||
)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
app.logger.info(f'创建新用户: {name}({phone})')
|
||||
else:
|
||||
# 更新用户信息
|
||||
user.name = name
|
||||
if user_info.get('affiliation'):
|
||||
user.branch = user_info.get('affiliation')
|
||||
if user_info.get('type'):
|
||||
user.role = user_info.get('type')
|
||||
db.session.commit()
|
||||
app.logger.info(f'更新用户信息: {name}({phone})')
|
||||
|
||||
# 登录用户
|
||||
login_user(user)
|
||||
app.logger.info(f'用户 {user.name}({user.phone}) 通过 Casdoor 登录成功')
|
||||
|
||||
# 清除可能残留的未登录提示
|
||||
session.pop('_flashes', None)
|
||||
return redirect(url_for('dashboard'))
|
||||
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
app.logger.info(f'用户 {current_user.name}({current_user.phone}) 登出')
|
||||
logout_user()
|
||||
# 仅退出本地会话,保留 Casdoor 登录态
|
||||
return redirect(url_for('login'))
|
||||
Reference in New Issue
Block a user