html guardian_如何使用Guardian验证Elixir / Phoenix API

html guardian

by Nirmalya Ghosh

由Nirmalya Ghosh

如何使用Guardian验证Elixir / Phoenix API (How to Authenticate your Elixir/Phoenix APIs using Guardian)

Authentication is always a tricky subject. People tend to use so many types of authentication in their apps. Authentication using an email address and a password with an option confirm password field is the most common. But every time you see a registration form, you feel bored thinking that you’ll have to type in so much just to register! What’s the fun in that!

身份验证始终是一个棘手的问题。 人们倾向于在其应用程序中使用多种身份验证。 最常见的是使用电子邮件地址和带有选项确认密码字段的密码进行身份验证。 但是,每次看到注册表格时,您都会觉得无聊,您只需要输入那么多即可注册! 那有什么乐趣!

So, for this app, I’ll be doing authentication using your Google account. It’s pretty straightforward. You just need to click on a button, give the app the necessary permissions to access you basic Google profile and you’re set! Cool, isn’t it?

因此,对于这个应用程序,我将使用您的Google帐户进行身份验证。 这很简单。 您只需要单击一个按钮,为该应用授予必要的权限,即可访问您的基本Google个人资料,您已设置好! 不错,不是吗?

We will be using Ueberauth, Ueberauth Google and Guardian for authenticating our user. Ueberauth and Ueberauth Google will help authenticate the user with their Google credentials. Guardian will help us in generating a JSON Web Token for logged in users. That token is necessary and needs to be in the header of each request for any route which needs authentication.

我们将使用UeberauthUeberauth GoogleGuardian来验证我们的用户。 Ueberauth和Ueberauth Google将帮助使用其Google凭据对用户进行身份验证。 Guardian将帮助我们为登录用户生成JSON Web令牌。 对于需要验证的任何路由,该令牌都是必需的,并且必须位于每个请求的标头中。

Guardian will check for that token in the requests’ header and if the token is valid, the authenticated routes will be available to the user. I’ll explain these things in details.

Guardian将在请求的标头中检查该令牌,并且如果该令牌有效,则经过身份验证的路由将对用户可用。 我将详细解释这些事情。

If you hadn’t already installed Phoenix and its necessary dependencies, you can head over to the Phoenix Guides to install and get up and running.

如果尚未安装Phoenix及其必需的依赖项,则可以转到Phoenix Guides进行安装,启动和运行。

To get started add the dependencies to our mix.exs.

首先,将依赖项添加到我们的mix.exs

defp deps do  [   ...      {:ueberauth, "~> 0.4"},   {:ueberauth_google, "~> 0.2"},   {:ja_serializer, "~> 0.11.2"},   {:guardian, "~> 0.14.2"}]end

After this, run mix deps.get to fetch the dependencies.

之后,运行mix deps.get获取依赖项。

You also need to add ueberauth and ueberauth_google to our application in mix.exs.

您还需要添加ueberauthueberauth_google我们在应用mix.exs

def application do  [mod: {SocialAppApi, []},   applications: [   ...      :ueberauth, :ueberauth_google]]end

Now, you will need to add your ueberauth, ueberauth_google and guardian configuration to your config/config.exs file.

现在,您需要将ueberauthueberauth_googleguardian配置添加到config/config.exs文件中。

# Ueberauth Config for oauthconfig :ueberauth, Ueberauth,  base_path: "/api/v1/auth",  providers: [    google: { Ueberauth.Strategy.Google, [] },    identity: { Ueberauth.Strategy.Identity, [        callback_methods: ["POST"],        uid_field: :username,        nickname_field: :username,      ] },  ]
# Ueberauth Strategy Config for Google oauthconfig :ueberauth, Ueberauth.Strategy.Google.OAuth,  client_id: System.get_env("GOOGLE_CLIENT_ID"),  client_secret: System.get_env("GOOGLE_CLIENT_SECRET"),  redirect_uri: System.get_env("GOOGLE_REDIRECT_URI")
# Guardian configurationconfig :guardian, Guardian,  allowed_algos: ["HS512"], # optional  verify_module: Guardian.JWT,  # optional  issuer: "SocialAppApi",  ttl: { 30, :days },  allowed_drift: 2000,  verify_issuer: true, # optional  secret_key: System.get_env("GUARDIAN_SECRET") || "rFtDNsugNi8jNJLOfvcN4jVyS/V7Sh+9pBtc/J30W8h4MYTcbiLYf/8CEVfdgU6/",  serializer: SocialAppApi.GuardianSerializer

As you can see here, I’ve used System.get_env() . This is a way to store credentials in your app which you don’t want to be a part of your codebase. You can create a .env file and store all of these credentials like:

如您所见,我使用了System.get_env() . 这是一种将凭据存储在您的应用程序中的方式,而您不想成为您的代码库的一部分。 您可以创建一个.env文件并存储所有这些凭据,例如:

export DB_NAME_PROD="social_app_api_db"export DB_PASSWORD_PROD="password"export DB_USERNAME_PROD="password"

After this, you need to do source .env and then, you can use them in your app.

之后,您需要执行source .env ,然后可以在您的应用程序中使用它们。

Now, we’ll need to do a bunch of stuff with our controllers which will let the user to sign up or sign in.

现在,我们需要使用控制器做很多事情,这将允许用户注册或登录。

First, create a new file web/controllers/auth_controller.ex.

首先,创建一个新文件web/controllers/auth_controller.ex

defmodule SocialAppApi.AuthController do  use SocialAppApi.Web, :controller  plug Ueberauth
alias SocialAppApi.User  alias MyApp.UserQuery
plug :scrub_params, "user" when action in [:sign_in_user]
def request(_params) do  end
def delete(conn, _params) do    # Sign out the user    conn    |> put_status(200)    |> Guardian.Plug.sign_out(conn)  end
def callback(%{assigns: %{ueberauth_failure: _fails}} = conn, _params) do    # This callback is called when the user denies the app to get the data from the oauth provider    conn    |> put_status(401)    |> render(SocialAppApi.ErrorView, "401.json-api")  end
def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do    case AuthUser.basic_info(auth) do      {:ok, user} ->        sign_in_user(conn, %{"user" => user})    end
case AuthUser.basic_info(auth) do      {:ok, user} ->        conn        |> render(SocialAppApi.UserView, "show.json-api", %{data: user})      {:error} ->        conn        |> put_status(401)        |> render(SocialAppApi.ErrorView, "401.json-api")    end  end
def sign_in_user(conn, %{"user" => user}) do    try do      # Attempt to retrieve exactly one user from the DB, whose      # email matches the one provided with the login request      user = User      |> where(email: ^user.email)      |> Repo.one!
cond do        true ->          # Successful login          # Encode a JWT          { :ok, jwt, _ } = Guardian.encode_and_sign(user, :token)
auth_conn = Guardian.Plug.api_sign_in(conn, user)          jwt = Guardian.Plug.current_token(auth_conn)          {:ok, claims} = Guardian.Plug.claims(auth_conn)
auth_conn          |> put_resp_header("authorization", "Bearer #{jwt}")          |> json(%{access_token: jwt}) # Return token to the client
false ->          # Unsuccessful login          conn          |> put_status(401)          |> render(SocialAppApi.ErrorView, "401.json-api")      end    rescue      e ->        IO.inspect e # Print error to the console for debugging
# Successful registration        sign_up_user(conn, %{"user" => user})    end  end
def sign_up_user(conn, %{"user" => user}) do    changeset = User.changeset %User{}, %{email: user.email,      avatar: user.avatar,      first_name: user.first_name,      last_name: user.last_name,      auth_provider: "google"}
case Repo.insert changeset do      {:ok, user} ->        # Encode a JWT        { :ok, jwt, _ } = Guardian.encode_and_sign(user, :token)
conn        |> put_resp_header("authorization", "Bearer #{jwt}")        |> json(%{access_token: jwt}) # Return token to the client      {:error, changeset} ->        conn        |> put_status(422)        |> render(SocialAppApi.ErrorView, "422.json-api")    end  end
def unauthenticated(conn, params) do    conn    |> put_status(401)    |> render(SocialAppApi.ErrorView, "401.json-api")  end
def unauthorized(conn, params) do    conn    |> put_status(403)    |> render(SocialAppApi.ErrorView, "403.json-api")  end
def already_authenticated(conn, params) do    conn    |> put_status(200)    |> render(SocialAppApi.ErrorView, "200.json-api")  end
def no_resource(conn, params) do    conn    |> put_status(404)    |> render(SocialAppApi.ErrorView, "404.json-api")  endend

Here sign_in_user will sign the user in and throw an access_token as the response. The sign_up_user will sign the user up using their Google credentials and then throw an access_token as the response. This token is essential in the way that Guardian will check for this access_token in all the requests’ header. It will check if the user is currently in session or not. If yes, all the authenticated routes will be available to the user. Otherwise, he will receive a 401 response for the authenticated routes.

在这里, sign_in_user将登录用户并抛出一个access_token作为响应。 sign_up_user将使用其Google凭据对用户进行注册,然后抛出access_token作为响应。 该令牌对于Guardian在所有请求标头中检查此access_token的方式至关重要。 它将检查用户当前是否在会话中。 如果是,则所有通过身份验证的路由都将对用户可用。 否则,他将收到针对经过身份验证的路由的401响应。

Let’s add some routes to our app. Our router.ex file looks like this:

让我们为我们的应用添加一些路线。 我们的router.ex文件如下所示:

defmodule SocialAppApi.Router do  use SocialAppApi.Web, :router
pipeline :api do    plug :accepts, ["json", "json-api"]    plug JaSerializer.Deserializer  end
pipeline :api_auth do    plug :accepts, ["json", "json-api"]    plug Guardian.Plug.VerifyHeader, realm: "Bearer"    plug Guardian.Plug.LoadResource    plug JaSerializer.Deserializer  end
scope "/api/v1", SocialAppApi do    pipe_through :api_auth
resources "/users", UserController, except: [:new, :edit]    get "/user/current", UserController, :current, as: :current_user    delete "/logout", AuthController, :delete  end
scope "/api/v1/auth", SocialAppApi do    pipe_through :api
get "/:provider", AuthController, :request    get "/:provider/callback", AuthController, :callback    post "/:provider/callback", AuthController, :callback  endend

Here, pipeline api_auth is the one which is authenticated. The pipeline api isn’t. So, we can visit get “/:provider”, AuthController, :request without signing in.

在这里,管道api_auth是经过身份验证的管道。 管道api不是。 因此,我们无需登录即可访问get “/:provider”, AuthController, :request

Create another file called web/models/auth_user.ex with the following code:

使用以下代码创建另一个名为web/models/auth_user.ex的文件:

defmodule AuthUser do  alias Ueberauth.Auth
def basic_info(%Auth{} = auth) do    {:ok,      %{        avatar: auth.info.image,        email: auth.info.email,        first_name: auth.info.first_name,        last_name: auth.info.last_name      }    }  endend

You will also need to create a User model.

您还需要创建一个User模型。

mix phoenix.gen.json User users email:string auth_provider:string first_name:string last_name:string avatar:string

This will generate your necessary model and migration.

这将生成必要的模型和迁移。

Your model will look something like this:

您的模型将如下所示:

defmodule SocialAppApi.User do  use SocialAppApi.Web, :model
schema "users" do    field :email, :string    field :auth_provider, :string    field :first_name, :string    field :last_name, :string    field :avatar, :string
timestamps()  end
def changeset(struct, params \\ %{}) do    struct    |> cast(params, [:email, :auth_provider, :first_name, :last_name, :avatar])    |> validate_required([:email, :auth_provider, :first_name, :last_name, :avatar])    |> unique_constraint(:email)  endend

Your migration file will look something like this:

您的迁移文件将如下所示:

defmodule SocialAppApi.Repo.Migrations.CreateUser do  use Ecto.Migration
def change do    create table(:users) do      add :email, :string      add :auth_provider, :string      add :first_name, :string      add :last_name, :string      add :avatar, :string
timestamps()    end
# Unique email address constraint, via DB index    create index(:users, [:email], unique: true)  endend

Now, run the migration.

现在,运行迁移。

mix ecto.migrate

Also, create a UserController for our user model. That will contain the following code:

另外,为我们的user模型创建一个UserController 。 它将包含以下代码:

defmodule SocialAppApi.UserController do  use SocialAppApi.Web, :controller
alias SocialAppApi.User
plug Guardian.Plug.EnsureAuthenticated, handler:     SocialAppApi.AuthController
def index(conn, _params) do    users = Repo.all(User)    render(conn, "index.json-api", data: users)  end
def current(conn, _) do    user = conn    |> Guardian.Plug.current_resource
conn    |> render(SocialAppApi.UserView, "show.json-api", data: user)  endend

This is useful in case you want to check if the authenticated routes work or not after all your hard work.

在您经过所有辛苦的工作后,如果要检查已验证的路由是否有效,这很有用。

Create two more views at web/views/error_view.ex with the following code:

使用以下代码在web/views/error_view.ex上创建另外两个视图:

defmodule SocialAppApi.ErrorView do  use SocialAppApi.Web, :view  use JaSerializer.PhoenixView
def render("401.json-api", _assigns) do    %{title: "Unauthorized", code: 401}    |> JaSerializer.ErrorSerializer.format  end
def render("403.json-api", _assigns) do    %{title: "Forbidden", code: 403}    |> JaSerializer.ErrorSerializer.format  end
def render("404.json-api", _assigns) do    %{title: "Page not found", code: 404}    |> JaSerializer.ErrorSerializer.format  end
def render("422.json-api", _assigns) do    %{title: "Unprocessable entity", code: 422}    |> JaSerializer.ErrorSerializer.format  end
def render("500.json-api", _assigns) do    %{title: "Internal Server Error", code: 500}    |> JaSerializer.ErrorSerializer.format  end
# In case no render clause matches or no  # template is found, let's render it as 500  def template_not_found(_template, assigns) do    render "500.json-api", assigns  endend

Also, create another view web/views/user_view.ex with the following code:

另外,使用以下代码创建另一个视图web/views/user_view.ex

defmodule SocialAppApi.UserView do  use SocialAppApi.Web, :view  use JaSerializer.PhoenixView
attributes [:avatar, :email, :first_name, :last_name, :auth_provider]end

And, you are all set. Fire up your server:

而且,您已经准备好了。 启动服务器:

mix phoenix.server

Now, go to http://localhost:4000/api/v1/auth/google and you will be redirected to Google’s login page. Once you give the app the necessary permissions, you will get an access_token in the response:

现在,转到http:// localhost:4000 / api / v1 / auth / google ,您将被重定向到Google的登录页面。 授予应用必要的权限后,您将在响应中获得一个access_token

{  access_token: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJVc2VyOjIiLCJleHAiOjE0ODk4NjM4MzUsImlhdCI6MTQ4NzI3MTgzNSwiaXNzIjoiU29jaWFsQXBwQXBpIiwianRpIjoiODU0NzJhODAtN2Q4Ny00MjM0LWIxNmUtODgyMTBmYWZkZDJmIiwicGVtIjp7fSwic3ViIjoiVXNlcjoyIiwidHlwIjoiYWNjZXNzIn0.L2LjpsyJAjF1r99hR11WVGcQ"}

Now, you can install the Modheader extension for Chrome and any other extension through which you can set response headers. Add Authorization as a Request Header and the access_token with Bearer <access_token>.

现在,您可以安装适用于Chrome的Modheader扩展程序以及可以通过其设置响应头的任何其他扩展程序。 添加Authorization作为Request Header和所述access_tokenBearer <access_tok烯>。

Go to http://localhost:4000/api/v1/users and you will be able to see an array of users you’ve already sign up with. You can also go to http://localhost:4000/api/v1/user/current to see the current user in the session.

转到http:// localhost:4000 / api / v1 / users ,您将能够看到已经注册的一系列用户。 您也可以转到http:// localhost:4000 / api / v1 / user / current查看会话中的当前用户。

If you remove that value from Modheader and go to http://localhost:4000/api/v1/users, you will get the following response:

如果您从Modheader中删除该值并转到http:// localhost:4000 / api / v1 / users ,则会收到以下响应:

{  jsonapi: {    version: "1.0"  },  errors: [{    title: "Unauthorized",    code: 401  }]}

As I’ve mentioned earlier, you need to send the access_token received to view the authenticated routes. Now, you know how to do API authentication in Elixir. You can compare your code with my code on Github.

正如我之前提到的,您需要发送接收到的access_token来查看经过身份验证的路由。 现在,您知道了如何在Elixir中进行API身份验证。 您可以将自己的代码与我在Github上的代码进行比较。

If you have some feedback, let me know in the comments below.

如果您有任何反馈意见,请在下面的评论中告诉我。

翻译自: https://www.freecodecamp.org/news/authentication-using-elixir-phoenix-f9c162b2c398/

html guardian

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值