Enhance user role management and access control in the application. Added user management link in the navigation for admins. Updated login flow to redirect to Casdoor for authentication. Improved logging for user actions and unauthorized access attempts. Adjusted role checks across various views to include '管理员' and 'admin' for access permissions.

This commit is contained in:
2025-12-10 15:53:17 +08:00
parent bb39314940
commit 0bb323bbff
24 changed files with 1285 additions and 61 deletions
Binary file not shown.
+2
View File
@@ -11,6 +11,7 @@ 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
@@ -80,6 +81,7 @@ def create_app():
init_device_routes(app)
init_about_routes(app)
init_dashboard_routes(app)
init_user_routes(app)
return app
+60
View File
@@ -7,3 +7,63 @@
2025-12-10 15:06:50 INFO: 用户 韦矍森 访问历史记录页面
2025-12-10 15:06:51 INFO: 用户 韦矍森 访问统计页面
2025-12-10 15:06:54 INFO: 用户 韦矍森 访问仪表盘页面
2025-12-10 15:09:46 INFO: 应用启动
2025-12-10 15:09:46 INFO: 错误处理器初始化完成
2025-12-10 15:11:12 INFO: 应用启动
2025-12-10 15:11:12 INFO: 错误处理器初始化完成
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:12 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:16 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:11:16 WARNING: 未授权访问:用户 New User - usp76h 尝试访问仪表盘页面
2025-12-10 15:14:09 INFO: 用户 韦矍森 访问仪表盘页面
2025-12-10 15:15:06 INFO: 用户 韦矍森 开始同步离线设备
2025-12-10 15:15:06 INFO: 同步完成:创建了 8 个工单,恢复了 7 个设备
2025-12-10 15:15:19 INFO: 用户 韦矍森 访问统计页面
2025-12-10 15:15:20 INFO: 用户 韦矍森 访问历史记录页面
2025-12-10 15:15:21 INFO: 用户 韦矍森 访问仪表盘页面
2025-12-10 15:15:42 INFO: 用户 韦矍森(19977899008) 登出
2025-12-10 15:16:07 INFO: 重定向到 Casdoor 授权页面
2025-12-10 15:16:22 INFO: Casdoor 用户信息: {'sub': '798f4436-fbb6-45f4-bf0c-6e1cf4add605', 'iss': 'http://10.10.10.14:18000', 'aud': '8f323670e073612794ef', 'preferred_username': 'test', 'name': '韦矍森', 'email': 'voole@vip.qq.com', 'email_verified': True, 'picture': 'https://cdn.casbin.org/img/casbin.svg', 'phone': '19977899008'}
2025-12-10 15:16:22 INFO: 更新用户信息: 韦矍森(19977899008)
2025-12-10 15:16:22 INFO: 用户 韦矍森(19977899008) 通过 Casdoor 登录成功
2025-12-10 15:16:23 INFO: 用户 韦矍森 访问仪表盘页面
2025-12-10 15:16:43 INFO: 用户 韦矍森 访问历史记录页面
2025-12-10 15:16:44 INFO: 用户 韦矍森 访问统计页面
2025-12-10 15:17:08 INFO: 用户 韦矍森 访问历史记录页面
2025-12-10 15:17:12 INFO: 用户 韦矍森 访问仪表盘页面
2025-12-10 15:17:15 INFO: 用户 韦矍森 访问统计页面
2025-12-10 15:17:16 INFO: 用户 韦矍森 访问历史记录页面
2025-12-10 15:25:09 INFO: 应用启动
2025-12-10 15:25:09 INFO: 错误处理器初始化完成
2025-12-10 15:26:06 INFO: 应用启动
2025-12-10 15:26:06 INFO: 错误处理器初始化完成
2025-12-10 15:27:03 INFO: 应用启动
2025-12-10 15:27:03 INFO: 错误处理器初始化完成
2025-12-10 15:27:15 INFO: 应用启动
2025-12-10 15:27:15 INFO: 错误处理器初始化完成
2025-12-10 15:27:34 INFO: 应用启动
2025-12-10 15:27:34 INFO: 错误处理器初始化完成
2025-12-10 15:27:53 INFO: 应用启动
2025-12-10 15:27:53 INFO: 错误处理器初始化完成
2025-12-10 15:28:44 INFO: 用户 韦矍森 访问仪表盘页面
2025-12-10 15:28:54 INFO: 应用启动
2025-12-10 15:28:54 INFO: 错误处理器初始化完成
2025-12-10 15:28:54 INFO: 用户信息已更新: 农远灵(19978825450) -> branch=红水河支局, role=装维员
2025-12-10 15:29:14 INFO: 用户信息已更新: 农远灵(19978825450) -> branch=电厂支局, role=装维员
2025-12-10 15:30:20 INFO: 用户信息已更新: 韦矍森(19977899008) -> branch=交付中心, role=管理员
2025-12-10 15:30:22 INFO: 用户 韦矍森(19977899008) 登出
2025-12-10 15:30:32 INFO: 重定向到 Casdoor 授权页面
2025-12-10 15:30:32 INFO: Casdoor 用户信息: {'sub': '92501c0e-db14-4def-8ded-cb8337d8acdb', 'iss': 'http://10.10.10.14:18000', 'aud': '8f323670e073612794ef', 'preferred_username': 'admin', 'name': 'Admin', 'email': 'voole@vip.qq.com', 'email_verified': True, 'picture': 'https://cdn.casbin.org/img/casbin.svg', 'phone': '18878797071'}
2025-12-10 15:30:32 INFO: 创建新用户: Admin(18878797071)
2025-12-10 15:30:32 INFO: 用户 Admin(18878797071) 通过 Casdoor 登录成功
2025-12-10 15:30:32 INFO: 用户 Admin 访问仪表盘页面
2025-12-10 15:32:06 INFO: 应用启动
2025-12-10 15:32:06 INFO: 错误处理器初始化完成
+4 -1
View File
@@ -35,7 +35,7 @@
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0 nav_menu">
{% if current_user.is_authenticated %}
{% if current_user.role == '统计员' %}
{% if current_user.role in ['统计员', '管理员', 'admin'] or current_user.name == 'Admin' %}
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'dashboard' }}" href="{{ url_for('dashboard') }}">仪表盘</a>
</li>
@@ -57,6 +57,9 @@
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'about' }}" href="{{ url_for('about') }}">关于</a>
</li>
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'user_list' }}" href="{{ url_for('user_list') }}">用户管理</a>
</li>
{% elif current_user.role == '装维员' %}
<li class="nav-item">
<a class="nav_link {{ 'active' if request.endpoint == 'dashboard' }}" href="{{ url_for('dashboard') }}">仪表盘</a>
-15
View File
@@ -15,7 +15,6 @@
{% endif %}
{% endwith %}
{% if casdoor_enabled %}
<!-- Casdoor 登录按钮 -->
<div class="login_casdoor_section">
<a href="{{ url_for('casdoor_login') }}" class="login_button login_button_casdoor">
@@ -25,20 +24,6 @@
使用 Casdoor 登录
</a>
</div>
<div class="login_divider">
<span></span>
</div>
{% endif %}
<!-- 原有手机号登录表单 -->
<form method="POST" class="login_form">
<div class="login_input_group">
<label for="phone" class="login_label">用户名(手机号)</label>
<input type="text" id="phone" name="phone" class="login_input" placeholder="请输入手机号" required>
</div>
<button type="submit" class="login_button login_button_phone">手机号登录</button>
</form>
</div>
</div>
{% endblock %}
+59
View File
@@ -0,0 +1,59 @@
{% extends "base.html" %}
{% block content %}
<div class="container mt-4">
<h2 class="mb-3">用户管理</h2>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-info">
{% for message in messages %}
{{ message }}
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="table-responsive">
<table class="table table-bordered align-middle">
<thead class="table-light">
<tr>
<th>手机号</th>
<th>姓名</th>
<th>所属支局</th>
<th>角色</th>
<th style="width: 160px;">操作</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<form method="POST" action="{{ url_for('update_user', phone=user.phone) }}">
<td>{{ user.phone }}</td>
<td>{{ user.name }}</td>
<td>
<select name="branch" class="form-select">
<option value="" {% if not user.branch %}selected{% endif %}>未设置</option>
{% for branch in branch_options %}
<option value="{{ branch }}" {% if user.branch == branch %}selected{% endif %}>{{ branch }}</option>
{% endfor %}
</select>
</td>
<td>
<select name="role" class="form-select" required>
{% for role in role_options %}
<option value="{{ role }}" {% if user.role == role %}selected{% endif %}>{{ role }}</option>
{% endfor %}
</select>
</td>
<td class="text-center">
<button type="submit" class="btn btn-primary btn-sm">保存</button>
</td>
</form>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -18
View File
@@ -9,26 +9,10 @@ def init_auth_routes(app):
@app.route('/login', methods=['GET', 'POST'])
def login():
# 如果已登录直接跳转到仪表盘
# 已登录直接进入仪表盘,否则跳转 Casdoor 登录
if current_user.is_authenticated:
return redirect(url_for('dashboard'))
# 检查是否启用了 Casdoor
casdoor_enabled = bool(app.config.get('CASDOOR_CLIENT_ID'))
# 处理手机号登录
if request.method == 'POST':
phone = request.form['phone']
user = User.query.filter_by(phone=phone).first()
if user:
login_user(user)
app.logger.info(f'用户 {user.name}({user.phone}) 登录成功')
return redirect(url_for('dashboard'))
else:
app.logger.warning(f'登录失败:用户不存在 (手机号: {phone})')
flash('用户不存在')
return render_template('login.html', casdoor_enabled=casdoor_enabled)
return redirect(url_for('casdoor_login'))
@app.route('/casdoor/login')
def casdoor_login():
+2 -1
View File
@@ -9,7 +9,8 @@ def init_dashboard_routes(app):
@app.route('/dashboard')
@login_required
def dashboard():
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问仪表盘页面')
flash('您没有权限访问此页面')
return redirect(url_for('login'))
+2 -1
View File
@@ -14,7 +14,8 @@ def init_device_routes(app):
@app.route('/sync_offline_devices', methods=['POST'])
@login_required
def sync_offline_devices():
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试同步离线设备')
return jsonify({'success': False, 'message': '没有权限执行此操作'})
+4 -2
View File
@@ -7,7 +7,8 @@ def init_history_routes(app):
@app.route('/history')
@login_required
def history():
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问历史记录')
flash('您没有权限访问此页面')
return redirect(url_for('statistics'))
@@ -110,7 +111,8 @@ def init_history_routes(app):
@app.route('/history/<string:order_id>')
@login_required
def history_detail(order_id):
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试查看工单详情 {order_id}')
flash('您没有权限访问此页面')
return redirect(url_for('statistics'))
+2 -1
View File
@@ -11,7 +11,8 @@ def init_manual_routes(app):
@app.route('/manual', methods=['GET', 'POST'])
@login_required
def manual():
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问手工故障单页面')
flash('您没有权限访问此页面')
return redirect(url_for('statistics'))
+2 -1
View File
@@ -13,7 +13,8 @@ def init_statistics_routes(app):
@app.route('/statistics')
@login_required
def statistics():
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问统计页面')
flash('您没有权限访问此页面')
return redirect(url_for('login'))
+56
View File
@@ -0,0 +1,56 @@
from flask import render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from scripts.models import db, User, Device
def init_user_routes(app):
def is_admin_user():
"""允许统计员/管理员或名称为 Admin 的用户管理账号"""
return current_user.role in ['统计员', '管理员', 'admin'] or current_user.name == 'Admin'
@app.route('/users', methods=['GET'])
@login_required
def user_list():
if not is_admin_user():
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试访问用户管理')
flash('您没有权限访问此页面')
return redirect(url_for('dashboard'))
users = User.query.order_by(User.name).all()
branch_options = [b[0] for b in db.session.query(Device.branch).distinct().all() if b[0]]
role_options = ['统计员', '装维员', '管理员']
return render_template(
'users.html',
users=users,
branch_options=branch_options,
role_options=role_options
)
@app.route('/users/<phone>', methods=['POST'])
@login_required
def update_user(phone):
if not is_admin_user():
app.logger.warning(f'未授权访问:用户 {current_user.name} 尝试修改用户 {phone}')
flash('您没有权限执行此操作')
return redirect(url_for('dashboard'))
user = User.query.filter_by(phone=phone).first()
if not user:
flash('用户不存在')
return redirect(url_for('user_list'))
new_branch = request.form.get('branch', '').strip()
new_role = request.form.get('role', '').strip()
if new_role and new_role not in ['统计员', '装维员', '管理员']:
flash('角色不合法')
return redirect(url_for('user_list'))
user.branch = new_branch
user.role = new_role
db.session.commit()
app.logger.info(f'用户信息已更新: {user.name}({user.phone}) -> branch={user.branch}, role={user.role}')
flash('用户信息已更新')
return redirect(url_for('user_list'))
+4 -2
View File
@@ -17,7 +17,8 @@ def init_work_order_routes(app):
@app.route('/dispatch', methods=['GET', 'POST'])
@login_required
def dispatch():
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
flash('您没有权限访问此页面')
return redirect(url_for('statistics'))
@@ -283,7 +284,8 @@ def init_work_order_routes(app):
@app.route('/receive/<string:work_order_id>', methods=['GET', 'POST'])
@login_required
def receive_detail(work_order_id):
if current_user.role not in ['装维员', '统计员']:
allowed_roles = ['装维员', '统计员', '管理员', 'admin']
if current_user.role not in allowed_roles and current_user.name != 'Admin':
flash('您没有权限访问此页面')
return redirect(url_for('statistics'))
+1067
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
1645179
1693159