Elixir / Phoenix:如何实现会话超时/过期
Elixir / Phoenix: How to implement session timeout / expiration
我正在开发一个普通的 Elixir / Phoenix 应用程序,并按照 Programming Phoenix 书中的一般步骤来实现一个基本的登录和退出系统(见下面的片段)。但是,我在书中或网上没有看到关于如何设置基于 cookie 的插件会话在一定时间后过期的建议。 Phoenix 应用程序中的会话超时有哪些方法?
这是我的基本身份验证系统的一些相关片段:
在 endpoint.ex
中,应用配置为使用基于 cookie 的只读会话:
plug Plug.Session,
store: :cookie,
key: "_zb_key",
signing_salt: "RANDOM HEX"
我写了一个插件 auth.ex
,它(除其他外)可以登录经过身份验证的用户,并可以根据在后续请求中找到的会话 user_id
设置 current_user
:
def login!(conn, user) do
conn
|> assign(:current_user, user)
|> put_session(:user_id, user.id)
|> configure_session(renew: true)
end
# ... more ...
def load_current_user(conn, _opts) do
cond do
conn.assigns[:current_user] ->
conn # If :current_user was already set, honor it
user_id = get_session(conn, :user_id) ->
user = Zb.Repo.get!(Zb.User, user_id)
assign(conn, :current_user, user)
true ->
conn # No user_id was found; make no changes
end
end
# ... more ...
我首先在 the Plug library 中寻找 cookie 过期选项,然后意识到更简单(也更安全)的方法是简单地在会话中设置过期日期时间以及 user_id。会话是 tamper-proof,所以当我收到每个请求时,我可以将日期时间与现在进行比较;如果会话尚未过期,我将 current_user
设置为正常。否则我调用 logout!
删除过期的会话。
实现看起来像这样(需要 Timex 库):
# Assign current_user to the conn, if a user is logged in
def load_current_user(conn, _opts) do
cond do
no_login_session?(conn) ->
conn # No user_id was found; make no changes
current_user_already_set?(conn) ->
conn
session_expired?(conn) ->
logout!(conn)
user = load_user_from_session(conn) ->
conn
|> put_session(:expires_at, new_expiration_datetime_string)
|> assign(:current_user, user)
end
end
defp session_expired?(conn) do
expires_at = get_session(conn, :expires_at) |> Timex.parse!("{ISO:Extended}")
Timex.after?(Timex.now, expires_at)
end
# ... more ...
# Start a logged-in session for an (already authenticated) user
def login!(conn, user) do
conn
|> assign(:current_user, user)
|> put_session(:user_id, user.id)
|> put_session(:expires_at, new_expiration_datetime_string)
|> configure_session(renew: true)
end
defp new_expiration_datetime_string do
Timex.now |> Timex.shift(hours: +2) |> Timex.format("{ISO:Extended}")
end
# ... more ...
这是我们的生产解决方案
(view in a Gist):
sliding_session_timeout.ex
defmodule Auth.SlidingSessionTimeout do
import Plug.Conn
def init(opts \ []) do
Keyword.merge([timeout_after_seconds: 3600], opts)
end
def call(conn, opts) do
timeout_at = get_session(conn, :session_timeout_at)
if timeout_at && now() > timeout_at do
logout_user(conn)
else
put_session(conn, :session_timeout_at, new_session_timeout_at(opts[:timeout_after_seconds]))
end
end
defp logout_user(conn) do
conn
|> clear_session()
|> configure_session([:renew])
|> assign(:session_timeout, true)
end
defp now do
DateTime.utc_now() |> DateTime.to_unix
end
defp new_session_timeout_at(timeout_after_seconds) do
now() + timeout_after_seconds
end
end
如何使用
将其插入 Phoenix 应用 router.ex
中 :browser
管道的末尾。
请注意身份验证(从会话中获取 user_id
,从数据库中加载用户)和授权是其他插件的关注点,在管道中。因此,请确保在基于会话的身份验证和授权插件之前将其插入。
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :put_secure_browser_headers
plug Auth.SlidingSessionTimeout, timeout_after_seconds: 3600 # <=
end
Plug.Sessions 模块有一个内置选项,可以使用 max_age
键设置 cookie 的过期时间。例如,扩展您的 endpoint.ex
代码段如下所示:
plug Plug.Session,
store: :cookie,
key: "_zb_key",
signing_salt: "RANDOM HEX",
max_age: 24*60*60*37 # 37 days
来源:https://teamgaslight.com/blog/til-how-to-explicitly-set-session-expiration-in-phoenix
我正在开发一个普通的 Elixir / Phoenix 应用程序,并按照 Programming Phoenix 书中的一般步骤来实现一个基本的登录和退出系统(见下面的片段)。但是,我在书中或网上没有看到关于如何设置基于 cookie 的插件会话在一定时间后过期的建议。 Phoenix 应用程序中的会话超时有哪些方法?
这是我的基本身份验证系统的一些相关片段:
在 endpoint.ex
中,应用配置为使用基于 cookie 的只读会话:
plug Plug.Session,
store: :cookie,
key: "_zb_key",
signing_salt: "RANDOM HEX"
我写了一个插件 auth.ex
,它(除其他外)可以登录经过身份验证的用户,并可以根据在后续请求中找到的会话 user_id
设置 current_user
:
def login!(conn, user) do
conn
|> assign(:current_user, user)
|> put_session(:user_id, user.id)
|> configure_session(renew: true)
end
# ... more ...
def load_current_user(conn, _opts) do
cond do
conn.assigns[:current_user] ->
conn # If :current_user was already set, honor it
user_id = get_session(conn, :user_id) ->
user = Zb.Repo.get!(Zb.User, user_id)
assign(conn, :current_user, user)
true ->
conn # No user_id was found; make no changes
end
end
# ... more ...
我首先在 the Plug library 中寻找 cookie 过期选项,然后意识到更简单(也更安全)的方法是简单地在会话中设置过期日期时间以及 user_id。会话是 tamper-proof,所以当我收到每个请求时,我可以将日期时间与现在进行比较;如果会话尚未过期,我将 current_user
设置为正常。否则我调用 logout!
删除过期的会话。
实现看起来像这样(需要 Timex 库):
# Assign current_user to the conn, if a user is logged in
def load_current_user(conn, _opts) do
cond do
no_login_session?(conn) ->
conn # No user_id was found; make no changes
current_user_already_set?(conn) ->
conn
session_expired?(conn) ->
logout!(conn)
user = load_user_from_session(conn) ->
conn
|> put_session(:expires_at, new_expiration_datetime_string)
|> assign(:current_user, user)
end
end
defp session_expired?(conn) do
expires_at = get_session(conn, :expires_at) |> Timex.parse!("{ISO:Extended}")
Timex.after?(Timex.now, expires_at)
end
# ... more ...
# Start a logged-in session for an (already authenticated) user
def login!(conn, user) do
conn
|> assign(:current_user, user)
|> put_session(:user_id, user.id)
|> put_session(:expires_at, new_expiration_datetime_string)
|> configure_session(renew: true)
end
defp new_expiration_datetime_string do
Timex.now |> Timex.shift(hours: +2) |> Timex.format("{ISO:Extended}")
end
# ... more ...
这是我们的生产解决方案 (view in a Gist):
sliding_session_timeout.ex
defmodule Auth.SlidingSessionTimeout do
import Plug.Conn
def init(opts \ []) do
Keyword.merge([timeout_after_seconds: 3600], opts)
end
def call(conn, opts) do
timeout_at = get_session(conn, :session_timeout_at)
if timeout_at && now() > timeout_at do
logout_user(conn)
else
put_session(conn, :session_timeout_at, new_session_timeout_at(opts[:timeout_after_seconds]))
end
end
defp logout_user(conn) do
conn
|> clear_session()
|> configure_session([:renew])
|> assign(:session_timeout, true)
end
defp now do
DateTime.utc_now() |> DateTime.to_unix
end
defp new_session_timeout_at(timeout_after_seconds) do
now() + timeout_after_seconds
end
end
如何使用
将其插入 Phoenix 应用 router.ex
中 :browser
管道的末尾。
请注意身份验证(从会话中获取 user_id
,从数据库中加载用户)和授权是其他插件的关注点,在管道中。因此,请确保在基于会话的身份验证和授权插件之前将其插入。
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :put_secure_browser_headers
plug Auth.SlidingSessionTimeout, timeout_after_seconds: 3600 # <=
end
Plug.Sessions 模块有一个内置选项,可以使用 max_age
键设置 cookie 的过期时间。例如,扩展您的 endpoint.ex
代码段如下所示:
plug Plug.Session,
store: :cookie,
key: "_zb_key",
signing_salt: "RANDOM HEX",
max_age: 24*60*60*37 # 37 days
来源:https://teamgaslight.com/blog/til-how-to-explicitly-set-session-expiration-in-phoenix