我尝试使用Fernet.Ecto.String.dump
以这种方式加密String
...
defp encrypt_password(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: password}} ->
put_change(changeset, :password_hash, Fernet.Ecto.String.dump(password))
_ ->
changeset
end
end
...在此自动生成的测试中(mix phoenix.gen.json User users...
)...
@valid_attrs %{email: "email@domain.tld", password: "q1w2e3"}
test "creates and renders resource when data is valid", %{conn: conn} do
conn = post conn, user_path(conn, :create), user: @valid_attrs
body = json_response(conn, 201)
|> IO.inspect
assert body["data"]["id"]
assert body["data"]["email"]
assert body["data"]["password"]
assert body["data"]["password_hash"]
assert Repo.get_by(User, email: "email@domain.tld")
end
......得到了这个:
1) test creates and renders resource when data is valid (Api.UserControllerTest)
test/controllers/user_controller_test.exs:32
** (ArgumentError) argument error
stacktrace:
:erlang.byte_size({:ok, "gAAAAABYzq2ofkNpJauc1vIDNrze1ORAMJXW0j0WN5bDong-cpgYYfaV75RYbX3A5yPl4fo86ctkZfszI8ePXGMejW50eRnfeg=="})
(fernetex) lib/fernetex.ex:193: Fernet.pad/1
(fernetex) lib/fernetex.ex:176: Fernet.encrypt/3
(fernetex) lib/fernetex.ex:154: Fernet.generate/4
(fernet_ecto) lib/fernet_ecto/type.ex:9: Fernet.Ecto.Type.encrypt/2
(ecto) lib/ecto/type.ex:662: Ecto.Type.process_dumpers/3
(ecto) lib/ecto/repo/schema.ex:695: Ecto.Repo.Schema.dump_field!/6
(ecto) lib/ecto/repo/schema.ex:708: anonymous fn/6 in Ecto.Repo.Schema.dump_fields!/5
(stdlib) lists.erl:1263: :lists.foldl/3
(ecto) lib/ecto/repo/schema.ex:706: Ecto.Repo.Schema.dump_fields!/5
(ecto) lib/ecto/repo/schema.ex:655: Ecto.Repo.Schema.dump_changes!/6
(ecto) lib/ecto/repo/schema.ex:200: anonymous fn/13 in Ecto.Repo.Schema.do_insert/4
(api) web/controllers/user_controller.ex:16: Api.UserController.create/2
(api) web/controllers/user_controller.ex:1: Api.UserController.action/2
(api) web/controllers/user_controller.ex:1: Api.UserController.phoenix_controller_pipeline/2
(api) lib/api/endpoint.ex:1: Api.Endpoint.instrument/4
(api) lib/phoenix/router.ex:261: Api.Router.dispatch/2
(api) web/router.ex:1: Api.Router.do_call/2
(api) lib/api/endpoint.ex:1: Api.Endpoint.phoenix_pipeline/1
(api) lib/api/endpoint.ex:1: Api.Endpoint.call/2
当我在iex -S mix phoenix.server
中使用该命令时,它可以正常工作:
iex(15)> Fernet.Ecto.String.dump("teste")
{:ok, "gAAAAABYzq-iDjQb5i1MYY8RXbFl9ZFfDQp2wyVWqAdPJIVKgMtFap-P8AtuvsNXqrcedp5CGaJjS9M2kBD60OPOR4l_tkDO8w=="}
iex(16)>
如果我使用put_change(changeset, :password_hash, password)
避免加密,则测试将通过。
这是DB中的结构:
schema "users" do
field :name, :string
field :email, :string
field :password, :string, virtual: true
field :password_hash, Fernet.Ecto.String
根据documentation,Fernet.Ecto.String
存储在数据库的:binary
列中:
add :password_hash, :binary
任何人都可以帮我弄清楚为什么这个测试失败了吗?
Documentation about Fernet,以防有人想知道。
为什么我尝试这样做而不是使用comeonin
:JWT (JSON Web Tokens) is a Bad Standard That Everyone Should Avoid - Paragon Initiative Enterprises Blog
我确实在fernet_ecto github上发布了issue(详情较少)。
答案 0 :(得分:2)
我认为这是因为Fernet.Ecto.String.dump(password)
返回{:ok, "hash-goes-here"}
的元组,并且您正在尝试将此元组分配给字符串字段。
你应该试试这个:
defp encrypt_password(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{password: password}} ->
{:ok, hash} = Fernet.Ecto.String.dump(password)
put_change(changeset, :password_hash, hash)
_ ->
changeset
end
end
这样,它只会将哈希值分配给字段。