如何在 Streamlit 中的多页应用程序中保留会话状态变量

How to keep session state variables across a multipage app in Streamlit

提问人:Luis Valencia 提问时间:11/6/2023 更新时间:11/21/2023 访问量:139

问:

我的应用有几个页面,第一次加载时,它会检测会话状态中是否有访问令牌,如果没有用户单击登录名转到 Azure Id,然后在查询参数中返回代码,然后检索访问令牌。 然后我可以根据用户组等来渲染一些按钮。

这在我的主页上工作得很好。

Security.py

# Initialize the MSAL ConfidentialClientApplication
app = msal.ConfidentialClientApplication(CLIENT_ID, authority=AUTHORITY, client_credential=CLIENT_SECRET)


# Function to get the authorization URL
def get_auth_url():
    """
    Generate the authorization URL for user sign-in.
    """
    auth_url = app.get_authorization_request_url(SCOPE, redirect_uri=REDIRECT_URI)
    return auth_url


# Function to exchange authorization code for an access token
def get_token_from_code(auth_code):
    """
    Exchange an authorization code for an access token.
    """
    result = app.acquire_token_by_authorization_code(auth_code, scopes=SCOPE, redirect_uri=REDIRECT_URI)
    return result['access_token']


# Function to get user information
def get_user_info(access_token):
    """
    Get user information using the provided access token.
    """
    headers = {'Authorization': f'Bearer {access_token}'}
    response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers)
    return response.json()


# Function to get user's groups with display names
def get_user_groups(access_token):
    """
    Get the user's groups with display names using the provided access token.
    """
    headers = {'Authorization': f'Bearer {access_token}'}
    params = {
        '$select': 'displayName, id',  # Specify the fields you want to retrieve
    }
    response = requests.get('https://graph.microsoft.com/v1.0/me/memberOf', headers=headers, params=params)
    return response.json()


# Function to handle the OAuth2 redirect flow
def handle_redirect():
    """
    Handle the OAuth2 redirect flow, retrieve the access token, and store it in the session.
    """
    if not st.session_state.get('access_token'):
        code = st.experimental_get_query_params().get('code')
        if code:
            access_token = get_token_from_code(code)
            st.session_state['access_token'] = access_token
            st.experimental_set_query_params()

那么 utils.py 有这个:

def setup_page(): 如果 st.experimental_get_query_params().get('code'): # 如果存在授权代码,则处理 OAuth2 重定向 handle_redirect() access_token = st.session_state.get('access_token')

if access_token:
    # If an access token is available, retrieve and display user information and groups
    user_info = get_user_info(access_token)
    st.session_state['user_info'] = user_info
    # Get user's group information
    user_groups = get_user_groups(access_token)
    st.session_state['user_groups'] = user_groups

    # Display user's group information, handling cases where 'displayName' is not available
    # if 'value' in user_groups:
    # for group in user_groups['value']:
    # group_display_name = group.get('displayName', group.get('mailNickname', group["id"]))
    # group_id = group["id"]
    # st.write(group_id)

    # return True
else:
    # If there's no access token, prompt the user to sign in
    st.write("Please sign-in to use this app.")
    auth_url = get_auth_url()
    st.markdown(f"<a href='{auth_url}' target='_self'>Sign In</a>", unsafe_allow_html=True)
    st.stop()

然后在我的主页和 page1.py 上,page2.py 我只是打电话给setup_page。

但是,当我从主页转到第 1 页时,session_state丢失了,访问令牌不再存在。

是什么原因造成的?

蟒蛇 流光

评论

0赞 ferdy 11/14/2023
在何处部署应用?
0赞 Luis Valencia 11/14/2023
Azure Web 应用...
0赞 Christopher Hoffman 11/17/2023
这有帮助吗?stackoverflow.com/questions/74968179/......
0赞 Luis Valencia 11/17/2023
那没有用

答:

0赞 Sai Prakash Reddy 11/21/2023 #1

Streamlit 的session_state旨在在重新运行脚本时保持特定于会话的状态。但是,它并非旨在跨不同的页面或应用实例保留。当您在 Streamlit 中从一个页面导航到另一个页面时,每个页面本质上都是脚本的单独实例。

在本例中,当您从主页移动到 page1 时,将重新执行 page1 的脚本,并重置session_state。若要跨不同页面维护状态,可以考虑使用其他方法,例如通过 URL 传递参数、在数据库中存储信息或使用全局变量。

下面是一个高级建议:

  1. 通过 URL 传递访问令牌 参数:

将用户重定向到 page1 时,将访问令牌作为参数附加到 URL 中。 在 page1 中,读取 URL 参数并使用访问令牌。

例:

# Redirect from homepage
page1_url = f"/page1?access_token={access_token}"
st.markdown(f"<a href='{page1_url}'>Go to Page 1</a>", unsafe_allow_html=True)
# In page1.py
access_token = st.experimental_get_query_params().get('access_token')
  1. 将访问令牌存储在数据库或外部存储中:

将访问令牌存储在数据库或外部存储中(考虑安全隐患)。 根据用户的会话在每个页面中检索访问令牌。

例:

# Store access token
st.session_state['access_token'] = access_token
# Retrieve access token in page1
access_token = st.session_state.get('access_token')

选择适合应用程序要求和安全注意事项的方法。在 URL 中传递敏感信息或将其存储在外部存储中时,请注意安全性。

快乐学习!!