代码之家  ›  专栏  ›  技术社区  ›  Mark Karavan

无法理解JWT auth(Phoenix)中的解构

  •  0
  • Mark Karavan  · 技术社区  · 7 年前

    当我发布到 MyApp.SessionsController.create/2 从CURL,我得到一个 user MyApp.Session.authenticate/1 {:ok, jwt, _full_claims} ,然后可以通过管道发送给《卫报》。我使用 IO.inspect user 看看 对象并获取以下错误:

    终端:

    curl -H "Content-Type: application/json" -X POST -d '{"email":"me@myapp.com","password":"password", "session":{"email":"mark@myapp.com", "password":"password"}}' http://localhost:4000/api/v1/sessions
    

    当我 IO.inspect 这个 在IEX中,我看到:

    %MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, avatar_url: nil,
     email: "me@myapp.com", handle: "me", id: 2,
     inserted_at: ~N[2017-08-22 18:26:10.000033], password: nil,
     password_hash: "$2b$12$LpJTWWEEUzrkkzu2w9sRheGHkh0YOgUIOkLluk05StlmTP6EiyPA6",
     updated_at: ~N[2017-08-22 18:26:10.007796]}
    

    我看到了这个错误:

    Request: POST /api/v1/sessions
    ** (exit) an exception was raised:
        ** (MatchError) no match of right hand side value: %MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">, avatar_url: nil, email: "me@myapp.com", handle: "mark", id: 2, inserted_at: ~N[2017-08-22 18:26:10.000033], password: nil, password_hash: "$2b$12$LpJTWWEEUzrkkzu2w9sRheGHkh0YOgUIOkLluk05StlmTP6EiyPA6", updated_at: ~N[2017-08-22 18:26:10.007796]}
            (myapp) web/controllers/api/v1/sessions_controller.ex:11: MyApp.SessionsController.create/2
    

    {:ok, jwt, _full_claims} = user ?


    设置如下:

    # mix.exs
      defp deps do
        [
          {:distillery, "~> 1.4", runtime: false},
          {:phoenix, "~> 1.3.0-rc", override: true},
          {:phoenix_ecto, "~> 3.2"},
          ...
          {:comeonin, "~> 4.0"},
          {:bcrypt_elixir, "~> 0.12.0"},
          {:guardian, "~> 0.14.5"},
    ]
    

    # web/router.ex
      ...
      pipeline :api do
        plug :accepts, ["json"]
        plug Guardian.Plug.VerifyHeader
      end
    
      scope "/api", MyApp do
        pipe_through :api
    
        scope "/v1" do
          post "/sessions", SessionsController, :create
        end
      end
    ...
    

    # web/controllers/session_controller.ex
    defmodule MyApp.SessionsController do  
      use MyApp.Web, :controller
    
      alias MyApp.{Repo, User}
    
      plug :scrub_params, "session" when action in [:create]
    
      def create(conn, %{"session" => session_params}) do
        case MyApp.Session.authenticate(session_params) do
        {:ok, user} ->
          {:ok, jwt, _full_claims} = user
            IO.inspect user       # Trying to test it here 
            |> Guardian.encode_and_sign(:token)
          conn
            |> put_status(:created)
            |> render("show.json", jwt: jwt, user: user)
        :error ->
          conn
          |> put_status(:unprocessable_entity)
          |> render("error.json")
        end
      end
    

    # web/services/session.ex
    defmodule MyApp.Session do 
    
      alias MyApp.{Repo, User}
      import Bcrypt
    
      def authenticate(%{"email" => email, "password" => password}) do
        case Repo.get_by(User, email: email) do
          nil -> 
            :error
          user ->
            case verify_password(password, user.password_hash) do
              true ->
                {:ok, user}
              _ ->
                :error
            end
        end
      end
    
      defp verify_password(password, pw_hash) do
        Comeonin.Bcrypt.checkpw(password, pw_hash)
      end
    end
    

    # lib/MyApp/User.ex
    defmodule MyApp.User do
      use MyApp.Web, :model
    
      schema "users" do
        field :email, :string
        field :handle, :string
        field :password_hash, :string
        field :avatar_url, :string
        field :password, :string, virtual: true
    
        timestamps
      end
    
      def changeset(model, params \\ :empty) do
        model
        |> cast(params, [:email, :handle, :password_hash, :password, :avatar_url])
        |> validate_required([:email])
        |> validate_length(:email, min: 1, max: 255)
        |> validate_format(:email, ~r/@/)
      end
    

    编辑:添加监护人信息

    #config/config.exs
    config :guardian, Guardian,
      issuer: "MyApp",
      ttl: { 30, :days},
      verify_issuer: true,
      secret_key: "abc123",
      serializer: MyApp.GuardianSerializer
    

    #lib/MyApp/guardian_serializer.ex
    defmodule MyApp.GuardianSerializer do
      @behaviour Guardian.Serializer
      alias MyApp.Repo
      alias MyApp.User
    
      def for_token(user = %User{}), do: {:ok, "User:#{user.id}"}
      def for_token(_), do: {:error, "Unknown resource type"}
    
      def from_token("User:" <> id), do: {:ok, Repo.get(User, id)}
      def from_token(_), do: {:error, "Unknown resource type"}
    end
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   Dogbert    7 年前

    {:ok, jwt, _full_claims} 是调用返回的值 Guardian.encode_and_sign(user, :token) . 这是您链接到的教程中的原始代码:

    {:ok, jwt, _full_claims} = user 
      |> Guardian.encode_and_sign(:token)
    

    这与:

    {:ok, jwt, _full_claims} = Guardian.encode_and_sign(user, :token)
    

    另一方面,您的代码可以 {:ok, jwt, _full_claims} = user

    {:ok, jwt, _full_claims} = user
      |> IO.inspect
      |> Guardian.encode_and_sign(:token)
    

    IO.inspect 返回打印后传递的值,因此此代码的功能与教程相同,只是它将打印 user