目录
一,项目说明
由于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测试,并不需要重新登录。
注意:因为是在后端维护登录状态,在没过期的情况下,用其他浏览器,换电脑都能登录。