streamlit实现登录功能

目录

一,项目说明

二,项目目录

三,主要代码

1,app.py

2,login_page.py

3,logout_page.py

4,home_page.py

四,运行项目

五,测试


一,项目说明

由于streamlit无法读取浏览器前端是数据,会话数据是保存在服务端st.session_state中。当浏览器F5刷新时会创建新的会话,通过st.session_state实现的登录需要重新登录,与大多数网站的使用习惯不符。现在通过读文的方式校验登录,实现web刷新不需要重新登录功能。实际项目也可以用数据库,redis等替换读写文件的方式实现。

二,项目目录

三,主要代码

1,app.py

python 复制代码
# app.py
import streamlit as st

# 隐藏右上角的部署按钮,保留其他菜单项
st.set_page_config(
    page_title="登录系统",
    page_icon="🔒",
    initial_sidebar_state="auto",
    layout="wide",
    menu_items={
        'Get Help': None,
        'Report a bug': None,
        'About': '关于本应用'
    }
)

# Define the pages
login = st.Page("pages/login_page.py", title="登录", icon="🎉")
logout = st.Page("pages/logout_page.py", title="退出", icon="❄️")
main_page = st.Page("pages/query_page.py", title="知识库", icon="🎈")  # 修改为正确的文件名和路径

# Set up navigation
pg = st.navigation([login, logout, main_page])

# Run the selected page
pg.run()

2,login_page.py

python 复制代码
import streamlit as st
import os
import json
from datetime import datetime, timedelta

st.markdown("# 登录  ❄️")
st.sidebar.markdown("# 登录 ❄️")


def login():
    """
    Creates a login page using Streamlit.
    """
    st.title("Logins")

    # 初始化会话状态
    if 'username' not in st.session_state:
        st.session_state['username'] = ''
    if 'logged_in' not in st.session_state:
        st.session_state['logged_in'] = False

    # 尝试从文件恢复登录状态
    if not st.session_state['logged_in']:
        restore_login_status()

    # 登录验证函数
    def authenticate():
        """
        Handles user login.  For this example, authentication is simulated.
        """
        if st.session_state.username == "user" and st.session_state.password == "1234":
            st.session_state.logged_in = True
            st.session_state['username'] = st.session_state.username  # store username
            
            # 保存登录状态到文件,包含过期时间(当前时间+30分钟)
            save_login_status()
        else:
            st.error("Invalid username or password")

    # 登出函数
    def logout():
        """Handles user logout"""
        st.session_state.logged_in = False
        st.session_state['username'] = ''
        
        # 清除登录状态文件
        clear_login_status()

    # 显示登录表单或登出按钮
    if not st.session_state.logged_in:
        st.text_input("Username", key="username")
        st.text_input("Password", type="password", key="password")
        st.button("Login", on_click=authenticate)
    else:
        st.write(f"Welcome, {st.session_state['username']}!")
        st.button("Logout", on_click=logout)

    # 检查登录状态并跳转页面
    if st.session_state.get('logged_in'):
        # 检查登录是否过期
        if is_login_expired():
            st.session_state.logged_in = False
            st.session_state['username'] = ''
            clear_login_status()
            st.rerun()
        else:
            st.switch_page("pages/home_page.py")  # 更新路径

def save_login_status():
    """保存登录状态到文件,包含过期时间"""
    expiration_time = datetime.now() + timedelta(minutes=30)
    login_data = {
        'username': st.session_state['username'],
        'logged_in': st.session_state['logged_in'],
        'expiration': expiration_time.isoformat()  # 使用ISO格式保存时间
    }
    with open('login_status.json', 'w') as f:
        json.dump(login_data, f)

def restore_login_status():
    """从文件恢复登录状态"""
    if os.path.exists('login_status.json'):
        try:
            with open('login_status.json', 'r') as f:
                login_data = json.load(f)
                
                # 检查是否过期
                if is_login_expired(login_data):
                    # 如果过期,清除状态文件
                    clear_login_status()
                    return
                
                st.session_state['username'] = login_data.get('username', '')
                st.session_state['logged_in'] = login_data.get('logged_in', False)
        except:
            pass  # 文件损坏时忽略

def is_login_expired(login_data=None):
    """检查登录是否过期"""
    if login_data is None:
        if not os.path.exists('login_status.json'):
            return True
        try:
            with open('login_status.json', 'r') as f:
                login_data = json.load(f)
        except:
            return True
    
    if 'expiration' not in login_data:
        return True  # 没有过期时间的视为已过期
    
    try:
        expiration_time = datetime.fromisoformat(login_data['expiration'])
        current_time = datetime.now()
        return current_time > expiration_time
    except:
        return True  # 解析时间失败时视为已过期

def clear_login_status():
    """清除登录状态文件"""
    if os.path.exists('login_status.json'):
        os.remove('login_status.json')

if __name__ == "__main__":
    login()

3,logout_page.py

python 复制代码
import streamlit as st
import os

st.markdown("# 退出 🎉")
st.sidebar.markdown("# 退出 🎉")

# 处理登出逻辑
if 'logged_in' in st.session_state:
    st.session_state.logged_in = False
    
if 'username' in st.session_state:
    st.session_state['username'] = ''

# 清除登录状态文件
if os.path.exists('login_status.json'):
    os.remove('login_status.json')

st.write("您已成功退出登录")
st.info("即将返回登录页面...")

# 立即跳转回登录页面
st.switch_page("pages/login_page.py")

4,home_page.py

python 复制代码
import streamlit as st
import os
import json
from datetime import datetime

# 检查登录是否过期
def is_login_expired(login_data=None):
    """检查登录是否过期"""
    if login_data is None:
        if not os.path.exists('login_status.json'):
            return True
        try:
            with open('login_status.json', 'r') as f:
                login_data = json.load(f)
        except:
            return True
    
    if 'expiration' not in login_data:
        return True  # 没有过期时间的视为已过期
    
    try:
        expiration_time = datetime.fromisoformat(login_data['expiration'])
        current_time = datetime.now()
        return current_time > expiration_time
    except:
        return True  # 解析时间失败时视为已过期

# 尝试从文件恢复登录状态
if 'logged_in' not in st.session_state:
    if os.path.exists('login_status.json'):
        try:
            with open('login_status.json', 'r') as f:
                login_data = json.load(f)
                
                # 检查是否过期
                if is_login_expired(login_data):
                    # 如果过期,清除状态文件
                    if os.path.exists('login_status.json'):
                        os.remove('login_status.json')
                    st.session_state['logged_in'] = False
                    st.session_state['username'] = ''
                else:
                    st.session_state['username'] = login_data.get('username', '')
                    st.session_state['logged_in'] = login_data.get('logged_in', False)
        except:
            st.session_state['logged_in'] = False
            st.session_state['username'] = ''

# 检查用户是否已登录且未过期
if 'logged_in' not in st.session_state or not st.session_state.logged_in or is_login_expired():
    # 如果登录已过期,清除状态
    if os.path.exists('login_status.json'):
        os.remove('login_status.json')
    st.session_state['logged_in'] = False
    st.session_state['username'] = ''
    st.error("登录已过期,请重新登录")
    st.switch_page("pages/login_page.py")
else:
    # Main page content
    st.markdown("# 知识库 🎈")
    st.sidebar.markdown("# 知识库 🎈")
    
    st.write(f"欢迎, {st.session_state.get('username', '用户')}!")
    
    # 在侧边栏添加导航选项
    st.sidebar.write(f"欢迎, {st.session_state.get('username', '用户')}!")
    if st.sidebar.button("退出登录"):
        st.session_state.logged_in = False
        st.session_state['username'] = ''
        
        # 清除登录状态文件
        if os.path.exists('login_status.json'):
            os.remove('login_status.json')
            
        st.switch_page("pages/logout_page.py")

四,运行项目

streamlit run app.py

五,测试

按F5测试,并不需要重新登录。

注意:因为是在后端维护登录状态,在没过期的情况下,用其他浏览器,换电脑都能登录。

相关推荐
程序员大阳8 小时前
副队长JavaScript教程(49)-主动抛出异常与错误类型
javascript·异常处理·error·throw·错误类型
hhhjhl9 小时前
Protobuf.js 跨端三方库仓颉适配全流程
javascript·网络·udp
康小汪9 小时前
AI 写 Vue 总写成同一个味道:给它装 Skills,装的不是知识是边界
前端·vue.js·人工智能·ai编程·最佳实践
琥珀.9 小时前
mysql中json数组转列表查询
linux·运维·服务器
智能运维指南10 小时前
研制过程“两张皮“怎么破:研制一体化支撑平台的定位、能力边界与落地前提
java·前端·人工智能·嘉为蓝鲸
白山编程大哥11 小时前
Java 后端如何优雅地把 List 集合返回给前端
java·前端·list
Flynt12 小时前
我把热榜那篇"禁用 axios"逐条实测了一遍:结论大体对,坑在它没讲的地方
前端·javascript·axios
云边云科技_云网融合12 小时前
连锁门店组网怎么做?SD-WAN 多分支组网的架构选型、部署与成本结构
服务器·数据库·php
网络小江12 小时前
组网第35课:静态路由——下一跳、出接口、默认路由、浮动静态与黑洞路由
服务器·网络·默认路由·黑洞路由·浮动静态路由
Csvn13 小时前
bundle优化
前端