You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Python Dash+Plotly应用添加用户级认证以展示专属图表?

Dash应用添加用户级认证与权限控制方案

1. 基于Flask-Login的基础实现

Dash底层依赖Flask,因此用Flask-Login实现用户认证是最直接的方案,步骤如下:

安装依赖

pip install flask-login plotly dash

定义用户模型与初始化认证模块

import dash
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask import render_template_string, request, redirect, url_for
import plotly.express as px
from dash import html, dcc, Input, Output, callback

# 初始化Dash应用
app = dash.Dash(__name__)
server = app.server  # 获取底层Flask实例

# 配置Flask-Login
login_manager = LoginManager()
login_manager.init_app(server)
login_manager.login_view = '/login'  # 未登录用户跳转的登录页

# 模拟用户数据库(实际项目替换为真实数据库)
users_db = {
    "admin": {"id": 1, "password_hash": "$2b$12$...", "type": "admin"},  # 用bcrypt生成的哈希值
    "analyst": {"id": 2, "password_hash": "$2b$12$...", "type": "analyst"},
    "viewer": {"id": 3, "password_hash": "$2b$12$...", "type": "viewer"}
}

# 用户模型类
class User(UserMixin):
    def __init__(self, user_id, username, user_type):
        self.id = user_id
        self.username = username
        self.user_type = user_type

# 加载用户回调(Flask-Login要求实现)
@login_manager.user_loader
def load_user(user_id):
    user_data = next((u for u in users_db.values() if str(u['id']) == user_id), None)
    if not user_data:
        return None
    username = next(k for k, v in users_db.items() if v['id'] == user_data['id'])
    return User(user_data['id'], username, user_data['type'])

实现登录/登出路由

# 登录页面模板
login_template = '''
<!DOCTYPE html>
<html>
<head>
    <title>Login to Dashboard</title>
</head>
<body>
    <h2>Login</h2>
    <form method="POST">
        <div>
            <label>Username:</label>
            <input type="text" name="username" required>
        </div>
        <div>
            <label>Password:</label>
            <input type="password" name="password" required>
        </div>
        <button type="submit">Submit</button>
    </form>
</body>
</html>
'''

@server.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user_data = users_db.get(username)
        
        # 密码验证(实际用bcrypt,示例简化)
        if user_data and password == "test123":  # 替换为bcrypt.checkpw(password.encode(), user_data['password_hash'].encode())
            user = User(user_data['id'], username, user_data['type'])
            login_user(user)
            return redirect('/')  # 登录成功跳转到Dash主页面
        return "Invalid username or password"
    return render_template_string(login_template)

@server.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect('/login')

根据用户类型渲染专属图表

app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='auth-status'),
    html.Button('Logout', id='logout-btn', n_clicks=0),
    html.Hr(),
    html.Div(id='chart-container')
])

# 显示用户信息
@callback(
    Output('auth-status', 'children'),
    Input('url', 'pathname')
)
def update_auth_status(_):
    if current_user.is_authenticated:
        return html.Div(f"Logged in as: {current_user.username} ({current_user.user_type})")
    return html.Div("Please log in to access the dashboard.")

# 渲染用户专属图表
@callback(
    Output('chart-container', 'children'),
    Input('auth-status', 'children')
)
def render_user_specific_chart(_):
    if not current_user.is_authenticated:
        return html.Div()
    
    user_type = current_user.user_type
    if user_type == 'admin':
        # 管理员专属仪表盘
        fig = px.bar(
            x=['Q1', 'Q2', 'Q3', 'Q4'],
            y=[150, 220, 180, 250],
            title='Admin: Annual Sales Overview'
        )
        return dcc.Graph(figure=fig)
    elif user_type == 'analyst':
        # 分析师专属仪表盘
        fig = px.line(
            x=['Jan', 'Feb', 'Mar', 'Apr'],
            y=[45, 60, 55, 70],
            title='Analyst: Monthly User Growth'
        )
        return dcc.Graph(figure=fig)
    elif user_type == 'viewer':
        # 查看者专属仪表盘
        fig = px.pie(
            values=[35, 40, 25],
            names='Mobile,Desktop,Tablet'.split(','),
            title='Viewer: Traffic Distribution'
        )
        return dcc.Graph(figure=fig)
    return html.Div("No content available for your user role.")

# 处理登出按钮
@callback(
    Output('url', 'pathname'),
    Input('logout-btn', 'n_clicks')
)
def handle_logout(n_clicks):
    if n_clicks > 0:
        return '/logout'
    return '/'

if __name__ == '__main__':
    app.run_server(debug=True)

2. 关键优化点

  • 密码安全:永远不要明文存储密码,使用bcrypt或passlib生成哈希值,替换示例中的密码验证逻辑为哈希校验。
  • 数据库集成:将模拟的users_db替换为MySQL、PostgreSQL或SQLAlchemy ORM,实现持久化用户存储。
  • 细粒度权限:如果需要更复杂的权限控制,可结合Flask-Principal实现角色与权限的绑定,比如限制某些回调仅对特定角色开放。
  • 会话安全:配置Flask的SECRET_KEY(server.secret_key = 'your-secret-key-here'),确保会话加密安全。

内容的提问来源于stack exchange,提问作者sadhu sakethreddy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 06:21:12