使用Guardian实现Elixir应用的身份认证教程

使用Guardian实现Elixir应用的身份认证教程

guardian Elixir Authentication guardian 项目地址: https://gitcode.com/gh_mirrors/gu/guardian

前言

在现代Web应用开发中,身份认证是必不可少的功能。本文将详细介绍如何使用Guardian这个强大的Elixir认证库来为你的应用添加安全可靠的认证系统。

Guardian简介

Guardian是一个基于JWT(JSON Web Tokens)的Elixir认证框架,它提供了一套完整的解决方案来处理用户认证和授权。主要特点包括:

  • 支持JWT令牌
  • 灵活的管道设计
  • 多种认证策略
  • 易于集成到Phoenix框架

环境准备

首先确保你已经安装了Elixir和Phoenix框架。我们将创建一个新的Phoenix项目作为示例:

mix phx.new auth_demo

添加依赖

在mix.exs文件中添加必要的依赖:

defp deps do
  [
    {:guardian, "~> 2.3"},
    {:argon2_elixir, "~> 2.0"}
  ]
end

运行mix deps.get安装依赖。

用户模型

我们需要一个用户模型来存储认证信息。可以使用Phoenix生成器创建:

mix phx.gen.context UserManager User users username:string password:string

这会在数据库中创建users表,包含username和password字段。

密码安全

使用Argon2算法对密码进行哈希处理,修改用户changeset:

def changeset(user, attrs) do
  user
  |> cast(attrs, [:username, :password])
  |> validate_required([:username, :password])
  |> put_password_hash()
end

defp put_password_hash(%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset) do
  change(changeset, password: Argon2.hash_pwd_salt(password))
end

认证逻辑

实现用户认证函数:

def authenticate_user(username, plain_text_password) do
  query = from u in User, where: u.username == ^username
  case Repo.one(query) do
    nil ->
      Argon2.no_user_verify()
      {:error, :invalid_credentials}
    user ->
      if Argon2.verify_pass(plain_text_password, user.password) do
        {:ok, user}
      else
        {:error, :invalid_credentials}
      end
  end
end

Guardian实现模块

创建Guardian实现模块,这是Guardian的核心配置:

defmodule AuthDemo.UserManager.Guardian do
  use Guardian, otp_app: :auth_demo

  alias AuthDemo.UserManager

  def subject_for_token(user, _claims) do
    {:ok, to_string(user.id)}
  end

  def resource_from_claims(%{"sub" => id}) do
    user = UserManager.get_user!(id)
    {:ok, user}
  rescue
    Ecto.NoResultsError -> {:error, :resource_not_found}
  end
end

配置Guardian

生成密钥并配置Guardian:

mix guardian.gen.secret

将生成的密钥添加到config.exs:

config :auth_demo, AuthDemo.UserManager.Guardian,
  issuer: "auth_demo",
  secret_key: "your_generated_secret"

认证管道

创建认证管道来处理HTTP请求:

defmodule AuthDemo.UserManager.Pipeline do
  use Guardian.Plug.Pipeline,
    otp_app: :auth_demo,
    error_handler: AuthDemo.UserManager.ErrorHandler,
    module: AuthDemo.UserManager.Guardian

  plug Guardian.Plug.VerifySession, claims: %{"typ" => "access"}
  plug Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"}
  plug Guardian.Plug.LoadResource, allow_blank: true
end

错误处理

实现错误处理模块:

defmodule AuthDemo.UserManager.ErrorHandler do
  import Plug.Conn

  @behaviour Guardian.Plug.ErrorHandler

  @impl Guardian.Plug.ErrorHandler
  def auth_error(conn, {type, _reason}, _opts) do
    body = to_string(type)
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(401, body)
  end
end

控制器实现

创建会话控制器处理登录/登出:

defmodule AuthDemoWeb.SessionController do
  use AuthDemoWeb, :controller

  alias AuthDemo.{UserManager, UserManager.Guardian}

  def new(conn, _) do
    render(conn, "new.html")
  end

  def login(conn, %{"user" => %{"username" => username, "password" => password}}) do
    UserManager.authenticate_user(username, password)
    |> login_reply(conn)
  end

  def logout(conn, _) do
    conn
    |> Guardian.Plug.sign_out()
    |> redirect(to: "/login")
  end

  defp login_reply({:ok, user}, conn) do
    conn
    |> put_flash(:info, "Welcome back!")
    |> Guardian.Plug.sign_in(user)
    |> redirect(to: "/protected")
  end

  defp login_reply({:error, _reason}, conn) do
    conn
    |> put_flash(:error, "Invalid credentials")
    |> redirect(to: "/login")
  end
end

路由配置

最后配置路由:

pipeline :auth do
  plug AuthDemo.UserManager.Pipeline
end

pipeline :ensure_auth do
  plug Guardian.Plug.EnsureAuthenticated
end

scope "/", AuthDemoWeb do
  pipe_through [:browser, :auth]

  get "/login", SessionController, :new
  post "/login", SessionController, :login
  get "/logout", SessionController, :logout
end

scope "/", AuthDemoWeb do
  pipe_through [:browser, :auth, :ensure_auth]

  get "/protected", PageController, :protected
end

测试认证流程

  1. 创建迁移并运行:
mix ecto.migrate
  1. 创建测试用户:
AuthDemo.UserManager.create_user(%{username: "test", password: "password"})
  1. 启动服务器:
mix phx.server

现在你可以访问:

  • /login - 登录页面
  • /protected - 需要认证的页面
  • /logout - 登出

总结

通过本教程,我们实现了:

  1. 用户模型和密码哈希
  2. Guardian核心配置
  3. 认证管道和错误处理
  4. 登录/登出功能
  5. 受保护路由

Guardian提供了强大而灵活的身份认证解决方案,可以轻松集成到Elixir应用中。你可以在此基础上扩展更多功能,如权限管理、多因素认证等。

guardian Elixir Authentication guardian 项目地址: https://gitcode.com/gh_mirrors/gu/guardian

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈宝彤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值