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

如何基于Gradio构建带令牌功能的完整登录系统?

基于Gradio实现完整身份验证系统方案

一、仅用Gradio+Python实现的思路

Gradio自带的auth参数仅支持简单用户名密码校验,要实现带access/refresh token、过期续期及登出的完整系统,需结合Gradio的Request对象和会话管理,用Python内存存储或轻量持久化方案维护token状态:

1. 核心逻辑

  • Token生成与存储:用户名密码验证通过后,生成短期有效access token和长期有效refresh token,关联用户信息存储,同时记录过期时间。
  • 请求校验:聊天接口前置token校验,通过gr.Request获取请求头中的access token,判断有效性;若过期,用refresh token生成新的access token。
  • 登出处理:删除用户对应的token记录,使后续请求失效。

2. 完整代码实现

import gradio as gr
import random
import time
import jwt
from datetime import datetime, timedelta
from typing import Optional

# 配置参数
USERNAME = "admin"
PASSWORD = "123456"
SECRET_KEY = "your-secure-secret-key"  # 生产环境务必使用随机强密钥
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 7

# 内存存储token(生产环境建议替换为Redis等持久化存储)
active_tokens = {}

def create_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm="HS256")

def authenticate(username, password):
    if username == USERNAME and password == PASSWORD:
        # 生成access和refresh token
        access_token = create_token(
            data={"sub": username},
            expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        )
        refresh_token = create_token(
            data={"sub": username, "type": "refresh"},
            expires_delta=timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
        )
        # 存储refresh token用于续期
        active_tokens[username] = {
            "refresh_token": refresh_token,
            "expires_at": datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
        }
        return True, access_token, refresh_token
    return False, "", ""

def refresh_access_token(refresh_token):
    try:
        payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=["HS256"])
        username = payload.get("sub")
        if payload.get("type") != "refresh" or username not in active_tokens:
            return "", "无效的refresh token"
        if datetime.utcnow() > datetime.fromtimestamp(payload["exp"]):
            del active_tokens[username]
            return "", "refresh token已过期,请重新登录"
        # 生成新access token
        new_access_token = create_token(
            data={"sub": username},
            expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        )
        return new_access_token, ""
    except jwt.ExpiredSignatureError:
        return "", "refresh token已过期,请重新登录"
    except jwt.InvalidTokenError:
        return "", "无效的refresh token"

def check_access_token(request: gr.Request):
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        return False, "请提供有效的access token"
    access_token = auth_header.split(" ")[1]
    try:
        payload = jwt.decode(access_token, SECRET_KEY, algorithms=["HS256"])
        username = payload.get("sub")
        if username not in active_tokens:
            return False, "用户未登录"
        return True, ""
    except jwt.ExpiredSignatureError:
        return False, "access token已过期,请刷新token"
    except jwt.InvalidTokenError:
        return False, "无效的access token"

def logout(username):
    if username in active_tokens:
        del active_tokens[username]
    return "已成功登出"

def respond(message, chat_history, request: gr.Request):
    # 前置token校验
    valid, err_msg = check_access_token(request)
    if not valid:
        return "", chat_history + [(message, f"错误:{err_msg}")]
    bot_message = random.choice(["你好呀!", "我有点饿了😋"])
    chat_history.append((message, bot_message))
    time.sleep(2)
    return "", chat_history

with gr.Blocks() as demo:
    # 登录标签页
    with gr.Tab("登录"):
        username_input = gr.Textbox(label="用户名")
        password_input = gr.Textbox(label="密码", type="password")
        login_btn = gr.Button("登录")
        login_status = gr.Textbox(label="登录状态", interactive=False)
        access_token_display = gr.Textbox(label="Access Token", interactive=False)
        refresh_token_display = gr.Textbox(label="Refresh Token", interactive=False)
        
        login_btn.click(
            authenticate,
            inputs=[username_input, password_input],
            outputs=[gr.State(), access_token_display, refresh_token_display]
        ).then(
            lambda success, token: f"登录成功!Access Token:{token}" if success else "用户名或密码错误",
            inputs=[gr.State(), access_token_display],
            outputs=login_status
        )
    
    # Token刷新标签页
    with gr.Tab("刷新Token"):
        refresh_token_input = gr.Textbox(label="Refresh Token")
        refresh_btn = gr.Button("刷新Access Token")
        new_access_token_output = gr.Textbox(label="新Access Token", interactive=False)
        refresh_status = gr.Textbox(label="刷新状态", interactive=False)
        
        refresh_btn.click(
            refresh_access_token,
            inputs=[refresh_token_input],
            outputs=[new_access_token_output, refresh_status]
        )
    
    # 聊天标签页
    with gr.Tab("聊天"):
        chatbot = gr.Chatbot()
        msg = gr.Textbox(label="输入消息")
        clear = gr.ClearButton([msg, chatbot])
        logout_btn = gr.Button("登出")
        logout_status = gr.Textbox(label="登出状态", interactive=False)
        
        msg.submit(
            respond,
            inputs=[msg, chatbot],
            outputs=[msg, chatbot],
            api_name="chat"
        )
        
        logout_btn.click(
            logout,
            inputs=[username_input],
            outputs=logout_status
        )

if __name__ == "__main__":
    demo.launch(share=True)

3. 关键注意点

  • 内存存储active_tokens仅适合测试场景,生产环境必须用Redis等分布式存储维护token,避免服务重启后数据丢失。
  • SECRET_KEY要使用随机生成的强字符串,绝对不能泄露。
  • 前端调用聊天接口时,需将access token放在请求头Authorization: Bearer <token>中,Gradio的Request对象可直接读取该头信息。

二、兼容Gradio的第三方框架推荐

如果需要更成熟的生产级身份验证机制,可结合以下Python框架与Gradio集成:

  • FastAPI:支持标准OAuth2.0流程,内置JWT工具,可快速实现access/refresh token、token黑名单、登出等功能。将Gradio应用挂载为FastAPI的子应用,由FastAPI处理身份验证拦截,Gradio专注UI交互。
  • Flask-Login:基于Flask的登录扩展,提供会话管理、token续期等功能。将Gradio嵌入Flask应用,通过Flask路由拦截实现身份校验,适合已有Flask项目的场景。
  • Django REST Framework:若项目基于Django,可利用DRF的TokenAuthentication或JWTAuthentication组件,结合Django的用户系统和权限控制,实现完整身份验证流程,再集成Gradio界面。

这些框架提供了更完善的安全机制和生态支持,适合生产环境部署,与Gradio结合可兼顾功能完整性和交互体验。

内容的提问来源于stack exchange,提问作者Robin Kuri

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 06:45:03