
Qwen2.5-72B-Instruct-GPTQ-Int4保姆级教程Chainlit用户认证会话权限控制配置1. 引言为什么需要用户认证和权限控制想象一下你部署了一个功能强大的大模型应用比如Qwen2.5-72B它不仅能回答各种问题还能处理敏感信息。这时候你肯定不希望任何人都能随意访问对吧这就是我们今天要解决的问题。如果你已经用vLLM部署了Qwen2.5-72B-Instruct-GPTQ-Int4模型并且用Chainlit搭建了前端界面那么恭喜你基础工作已经完成了。但要让这个应用真正可用、安全还需要最后一道关卡用户认证和权限控制。简单来说就是用户认证确保只有注册的用户才能登录使用。权限控制不同用户能看到不同的内容或者拥有不同的操作权限。这篇文章我就手把手带你在现有的Chainlit应用上加上这两层安全防护。整个过程不复杂跟着做半小时内就能搞定。2. 准备工作检查你的环境在开始之前我们先确认一下你的环境是否就绪。2.1 确认模型服务已启动首先确保你的Qwen2.5-72B模型服务正在运行。打开终端运行以下命令查看日志cat /root/workspace/llm.log如果看到模型加载成功、服务启动的日志信息说明模型服务是正常的。这是后续所有操作的基础。2.2 确认Chainlit应用能正常访问打开浏览器访问你的Chainlit应用地址通常是http://你的服务器IP:8000。你应该能看到Chainlit的聊天界面并且可以正常提问、获取模型回复。如果这两步都确认无误那么我们就可以开始动手了。3. 第一步为Chainlit添加基础用户认证Chainlit本身支持简单的基于密码的认证。我们先从这里开始实现最基本的“登录才能用”功能。3.1 修改Chainlit配置文件在你的Chainlit应用目录下通常包含chainlit.md和app.py文件我们需要创建一个或修改配置文件。创建一个名为.chainlit的文件夹如果不存在的话然后在里面创建一个config.toml文件。文件内容如下# .chainlit/config.toml [ui] name 我的Qwen2.5智能助手 description 基于Qwen2.5-72B模型构建的对话应用 [auth] # 启用认证 enabled true # 这里我们使用简单的密码认证 method password # 定义用户可以定义多个 [[auth.users]] username admin password 你的强密码123 # 请务必修改为一个强密码 role admin # 角色可以是 admin, user 等 [[auth.users]] username user1 password 另一个密码456 role user重要提示密码一定要改示例中的密码只是示意你必须改成自己设定的、复杂的密码。可以添加多个用户按照[[auth.users]]的格式你可以添加任意多个用户。角色字段这里的role字段目前主要用于标识后续我们可以用它来做更细粒度的权限控制。3.2 重启Chainlit应用保存配置文件后重启你的Chainlit应用。如果你是用类似下面的命令启动的chainlit run app.py -w那么需要先停止按CtrlC然后重新运行。现在再次访问你的Chainlit应用你会看到一个登录界面输入你在配置文件中设置的用户名和密码就能进入聊天界面了。这样我们就实现了最基本的大门守卫。4. 第二步实现基于角色的会话权限控制只有登录认证还不够。比如你可能有这样的需求管理员可以访问所有功能查看所有对话历史。普通用户只能看到自己的对话不能看到别人的。某些高级功能比如上传文件分析只对特定用户开放。这就需要我们实现更细粒度的会话权限控制。Chainlit提供了会话Session和用户User对象我们可以利用它们来实现。4.1 理解Chainlit的会话和用户对象在Chainlit中用户User就是登录的人包含username、role等信息。会话Session一次浏览器访问就是一个会话里面包含了当前用户的信息。我们可以在代码中通过cl.user_session.get(user)来获取当前登录的用户信息。4.2 修改app.py实现权限控制假设你原来的app.py文件大概长这样一个简单的Chainlit调用vLLM的例子# 原来的app.py简化版 import chainlit as cl from vllm import LLM, SamplingParams # 初始化vLLM客户端假设模型服务在本地8001端口 # 注意这里需要根据你的实际部署方式调整 llm LLM(modelQwen2.5-72B-Instruct, api_urlhttp://localhost:8001/v1) cl.on_chat_start async def start_chat(): await cl.Message(content你好我是Qwen2.5助手有什么可以帮您).send() cl.on_message async def main(message: cl.Message): # 构建提示词 prompt f用户说{message.content}\n助手 # 调用vLLM API sampling_params SamplingParams(temperature0.7, max_tokens500) response llm.generate(prompt, sampling_params) # 发送回复 await cl.Message(contentresponse[0].outputs[0].text).send()现在我们要在这个基础上添加权限控制。修改后的代码如下# 修改后的app.py - 添加权限控制 import chainlit as cl from vllm import LLM, SamplingParams import json from typing import Dict, List # 初始化vLLM客户端 llm LLM(modelQwen2.5-72B-Instruct, api_urlhttp://localhost:8001/v1) # 定义一个简单的“数据库”来存储用户会话历史 # 在实际生产环境中你应该使用真正的数据库如SQLite、PostgreSQL等 user_conversations: Dict[str, List[Dict]] { admin: [], # 管理员可以看到所有对话 user1: [], # 普通用户只能看到自己的 # 可以添加更多用户 } cl.on_chat_start async def start_chat(): 聊天开始时的处理 # 获取当前用户 user cl.user_session.get(user) if not user: await cl.Message(content未检测到用户信息请先登录).send() return username user.username role user.role # 根据用户角色显示不同的欢迎信息 if role admin: welcome_msg f欢迎回来管理员 {username}您可以查看所有用户的对话历史。 # 为管理员显示历史记录按钮 actions [ cl.Action(nameview_all_history, valueview_all, label 查看所有对话历史), cl.Action(nameclear_my_history, valueclear_mine, label️ 清空我的历史), ] await cl.Message(contentwelcome_msg, actionsactions).send() else: welcome_msg f您好{username}我是Qwen2.5助手有什么可以帮您 # 为普通用户只显示清空自己历史的按钮 actions [ cl.Action(nameclear_my_history, valueclear_mine, label️ 清空我的历史), ] await cl.Message(contentwelcome_msg, actionsactions).send() # 初始化该用户的对话历史记录如果还没有的话 if username not in user_conversations: user_conversations[username] [] cl.on_message async def main(message: cl.Message): 处理用户消息 # 获取当前用户 user cl.user_session.get(user) if not user: await cl.Message(content请先登录).send() return username user.username # 记录用户的问题 user_conversations[username].append({ role: user, content: message.content, timestamp: cl.utils.get_timestamp() }) # 构建提示词 - 这里可以添加系统提示来增强安全性 system_prompt 你是一个有帮助的AI助手。请以安全、负责任的方式回答用户的问题。 # 如果是管理员可以添加更多上下文 if user.role admin: system_prompt 你当前正在与管理员对话可以提供更详细的信息。 full_prompt f{system_prompt}\n\n用户说{message.content}\n助手 # 显示“正在思考”的提示 msg cl.Message(content) await msg.send() try: # 调用vLLM API sampling_params SamplingParams(temperature0.7, max_tokens500) response llm.generate(full_prompt, sampling_params) answer response[0].outputs[0].text # 记录助手的回复 user_conversations[username].append({ role: assistant, content: answer, timestamp: cl.utils.get_timestamp() }) # 更新消息内容 msg.content answer await msg.update() except Exception as e: error_msg f调用模型时出错{str(e)} msg.content error_msg await msg.update() cl.action_callback(view_all_history) async def on_view_all_history(action: cl.Action): 管理员查看所有对话历史 user cl.user_session.get(user) if not user or user.role ! admin: await cl.Message(content权限不足只有管理员可以查看所有历史。).send() return # 构建历史记录显示 history_text ## 所有用户的对话历史\n\n for username, conversations in user_conversations.items(): if conversations: # 只显示有对话记录的用户 history_text f### 用户{username}\n for conv in conversations[-5:]: # 显示最近5条 role_icon if conv[role] user else history_text f{role_icon} **{conv[role]}** ({conv[timestamp]}): {conv[content][:100]}...\n history_text \n if history_text ## 所有用户的对话历史\n\n: history_text 暂无对话历史。 await cl.Message(contenthistory_text).send() cl.action_callback(clear_my_history) async def on_clear_my_history(action: cl.Action): 清空当前用户的对话历史 user cl.user_session.get(user) if not user: await cl.Message(content请先登录).send() return username user.username if username in user_conversations: # 保存清空前的记录数用于显示 record_count len(user_conversations[username]) # 清空历史 user_conversations[username] [] await cl.Message(contentf已清空您的对话历史共{record_count}条记录。).send() else: await cl.Message(content您还没有任何对话历史。).send() cl.on_chat_end async def on_chat_end(): 聊天结束时的处理可选 user cl.user_session.get(user) if user: print(f用户 {user.username} 结束了对话会话。)4.3 代码要点解析这段代码实现了几个关键功能用户识别通过cl.user_session.get(user)获取当前登录用户。角色判断根据用户的role字段来自config.toml决定显示什么内容、提供什么功能。对话历史隔离每个用户的对话历史单独存储普通用户只能看到自己的。管理员特权管理员可以查看所有用户的对话历史。交互按钮通过Chainlit的Action功能添加了可点击的按钮。5. 第三步高级功能 - 文件上传权限控制在实际应用中文件上传是一个常见需求但也存在安全风险。我们可以基于用户角色来控制上传权限。5.1 添加文件上传处理在app.py中添加文件上传的处理函数cl.on_file_upload(accept[text/plain, application/pdf, image/*]) async def on_file_upload(files: List[cl.File]): 处理文件上传 user cl.user_session.get(user) if not user: await cl.Message(content请先登录才能上传文件).send() return # 检查用户权限 - 假设只有admin和user1可以上传文件 allowed_users [admin, user1] if user.username not in allowed_users: await cl.Message(content抱歉您没有文件上传权限).send() return # 处理上传的文件 for file in files: file_info f已收到文件{file.name} ({file.size}字节) # 这里可以根据文件类型进行不同的处理 if file.type.startswith(image/): file_info \n检测到图片文件我可以尝试描述图片内容。 # 在实际应用中这里可以调用图像识别模型 analysis 这是一张图片我可以帮您分析其中的内容。 elif file.type application/pdf: file_info \n检测到PDF文件我可以尝试提取其中的文本内容。 # 在实际应用中这里可以调用PDF解析库 analysis 这是一个PDF文档我可以帮您总结内容。 else: file_info \n检测到文本文件我可以分析其中的内容。 # 读取文本文件内容 try: content file.content.decode(utf-8) analysis f文件内容预览{content[:200]}... except: analysis 无法读取文件内容。 # 发送文件信息和分析结果 await cl.Message(contentf{file_info}\n\n{analysis}).send() # 记录到用户对话历史 if user.username in user_conversations: user_conversations[user.username].append({ role: system, content: f用户上传了文件{file.name}, timestamp: cl.utils.get_timestamp() })5.2 权限控制说明在这个文件上传函数中我们做了两层控制基础控制必须登录才能上传文件。细粒度控制只有admin和user1用户可以上传文件其他用户会被拒绝。你可以根据实际需求调整allowed_users列表或者改为基于角色user.role来判断。6. 第四步部署测试与问题排查代码写好了现在我们来测试一下。6.1 重启应用并测试重启Chainlit# 如果之前有运行先停止 # 然后重新启动 chainlit run app.py -w --port 8000测试登录功能用admin账号登录应该能看到“查看所有对话历史”的按钮。用user1账号登录应该看不到这个按钮。用其他未配置的账号如果有的话应该无法登录。测试对话隔离用admin账号说几句话。用user1账号说几句话。用admin账号点击“查看所有对话历史”应该能看到两个用户的对话。用user1账号点击同样的按钮如果能看到的话应该只能看到自己的对话。测试文件上传用admin或user1账号应该可以上传文件。用其他账号如果有的话应该会看到权限不足的提示。6.2 常见问题与解决问题1登录后还是看不到用户信息检查确保config.toml文件在正确的路径.chainlit/config.toml。解决重启Chainlit应用清除浏览器缓存重新登录。问题2按钮点击没反应检查浏览器的开发者工具F12查看Console是否有错误。解决确保cl.action_callback的函数名与按钮的name属性完全一致。问题3对话历史没有保存原因我们用的是内存中的字典来存储应用重启后数据会丢失。解决对于生产环境建议使用数据库。这里提供一个SQLite的简单示例import sqlite3 import json from datetime import datetime def init_db(): 初始化数据库 conn sqlite3.connect(conversations.db) c conn.cursor() c.execute(CREATE TABLE IF NOT EXISTS conversations (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, timestamp TEXT NOT NULL)) conn.commit() conn.close() def save_conversation(username, role, content): 保存对话到数据库 conn sqlite3.connect(conversations.db) c conn.cursor() timestamp datetime.now().isoformat() c.execute(INSERT INTO conversations (username, role, content, timestamp) VALUES (?, ?, ?, ?), (username, role, content, timestamp)) conn.commit() conn.close() def get_user_conversations(username): 获取用户的对话历史 conn sqlite3.connect(conversations.db) c conn.cursor() c.execute(SELECT role, content, timestamp FROM conversations WHERE username ? ORDER BY timestamp, (username,)) rows c.fetchall() conn.close() return [{role: row[0], content: row[1], timestamp: row[2]} for row in rows] def get_all_conversations(): 获取所有对话历史管理员用 conn sqlite3.connect(conversations.db) c conn.cursor() c.execute(SELECT username, role, content, timestamp FROM conversations ORDER BY username, timestamp) rows c.fetchall() conn.close() # 按用户分组 conversations {} for row in rows: username, role, content, timestamp row if username not in conversations: conversations[username] [] conversations[username].append({ role: role, content: content, timestamp: timestamp }) return conversations然后在app.py中用这些数据库函数替换原来的内存字典操作。7. 总结你的安全AI应用现已就绪通过以上步骤我们成功地为基于Qwen2.5-72B和Chainlit的AI应用加上了两层安全防护7.1 我们实现了什么基础用户认证只有注册的用户才能登录使用防止未授权访问。会话权限控制不同用户看到不同的界面和功能数据相互隔离。文件上传控制基于用户角色控制上传权限增加安全性。管理员特权管理员可以查看所有用户的对话历史便于管理和监控。7.2 关键要点回顾配置简单通过config.toml文件轻松添加用户和密码。代码清晰利用Chainlit提供的user_session对象很容易获取用户信息。灵活可控你可以根据实际需求调整权限控制的粒度。易于扩展这个框架可以轻松添加更多功能比如更复杂的角色系统多级权限使用数据库持久化存储添加操作日志集成第三方认证如OAuth7.3 下一步可以做什么如果你想让这个应用更加完善可以考虑使用真正的数据库替换内存存储使用SQLite、MySQL或PostgreSQL。添加更多用户管理功能用户注册、密码修改、权限调整等。集成第三方认证支持GitHub、Google等OAuth登录。添加操作日志记录谁在什么时候做了什么便于审计。实现更细的权限控制比如控制每个用户可以使用的模型功能、对话次数限制等。现在你的Qwen2.5-72B应用不仅功能强大而且安全可控。你可以放心地分享给团队成员使用或者部署到公网供特定用户访问了。记住安全是一个持续的过程。随着应用的发展你可能需要不断调整和完善权限控制策略。但有了今天打下的基础后续的扩展都会变得容易很多。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。