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测试,并不需要重新登录。

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

相关推荐
SomUrim3 小时前
ruoyi-vue-plus中await axios报错undefined的问题(请求正常)
前端·ruoyi
贺今宵3 小时前
electron运行项目better-sqlite3连接失败的问题,ABI版本不匹配,使用使用 electron-rebuild 重新编译
javascript·electron·sqlite
广州华水科技3 小时前
如何通过单北斗形变监测一体机提高大坝安全监测效率?
前端
我是人机不吃鸭梨3 小时前
Flutter AI 集成革命(2025版):从 Gemini 模型到智能表单验证器的终极方案
开发语言·javascript·人工智能·flutter·microsoft·架构
不染尘.3 小时前
cookie和session技术及实现
服务器·网络·网络协议·计算机网络
_F_y4 小时前
五种IO模型
服务器·网络
掘根4 小时前
【消息队列项目】服务器实现
运维·服务器
一只旭宝4 小时前
Linux专题十:I/O 复用进阶(LT/ET 模式)同步,异步阻塞,以及 libevent 库核心知识点
linux·服务器·网络
over6974 小时前
用 React Context 实现全局主题切换:从零搭建暗黑/亮色模式系统
前端·react.js·面试