Guardian(基础)
Guardian 是一个广泛使用的,基于 JWT (JSON Web Tokens) 的验证程序库。
JWTs
一个 JWT 能为验证提供一个包含了丰富的信息的令牌。不像其它验证系统,可能只提供了资源主体的标识符,JWTs 除此之外还能提供如下信息:
谁签发的令牌
令牌的拥有者是谁
哪个系统使用这个令牌
令牌签发的时间
令牌过期的时间
除了这些字段以外,Guardian 还提供了另一些字段来辅助其它一些功能的使用:
令牌的类型是什么
令牌的拥有者具有哪些权限
这些只是 JWT 里面的基础字段。你可以任意添加你的应用需要的额外信息到里面。要记住的是保持 JWT 简短,因为它是需要通过 HTTP header 传递的。
JWT 内涵的丰富性意味着你可以把它当作完整的认证信息在系统内传递。
使用场景
JWT 令牌可以被用来认证系统的任何一个部分。
单页应用
控制器(通过浏览器会话)
控制器(通过 API 的验证 headers)
Phoenix Channels
服务之间的请求
进程间通信
第三方访问(OAuth)
“记住我”功能
其它接口 - 原始的 TCP,UDP,CLI 等
JWT 令牌可以用在任何需要提供可验证的身份认证信息之处。
我需要使用数据库吗?
你不需要使用数据库来跟踪 JWT 的使用情况。简单的依靠发放日期和过期时间来控制访问就可以了。通常可能你会使用数据库来查询用户的资源,但是 JWT 本身并不需要使用数据库。
例如,如果你想使用 JWT 来验证 UDP 通信,你不太需要用上数据库。在发放 JWT 的时候,直接包含所有需要的信息到令牌里面就可以了。只要它是可信的(检查它有正确的签名信息),就没问题。
但是,你也可以使用数据库来跟踪 JWT。这样的好处是,你可以验证令牌是否还是有效,没有被吊销的。或者你还可以通过检查存储在数据库的记录,一次过强制登出所有的用户。使用 GuardianDb 能非常容易地实现上面的功能。GuardianDb 使用 Guardians 'Hooks' 来实现验证检查,数据库的保存和删除操作。稍后会介绍。
配置
配置 Guardian 有好几种方式。我们都会一一介绍,先从最简单的开始。
最简配置
你需要更改好几个配置。
文件配置
mix.exs
config/config.ex
以上就是 Guardian 所需的最基本的信息。把密钥(secret key)直接写在配置文件里面并不是好的做法,而是每个环境应该有自己的密钥。在开发和测试环境使用 Mix 环境变量是比较常见的做法。但是,在预发和生产环境,就必须使用强密钥(比如,使用 mix phoenix.gen.secret
来生成)。
lib/my_app/guardian_serializer.ex
你的 seralizer 负责根据 sub
(主体)字段找到相应的资源。它可以是从数据库查找,调用 API 获取,或者只是返回简单的字符串。它同时也负责把资源序列化为 sub
字段值。
这就是最简配置了。虽然还有更多其它选项,但是这样起步已经足够了。
应用集成
现在 Guardian 的配置已经准备好了,就可以和我们的应用集成起来了。我们先看看如何在 HTTP 请求中使用。
HTTP 请求
Guardian 提供了好一些和 HTTP 请求集成的 Plugs。你可以从另一章课程中了解 Plug。Guardian 不是必须和 Phoenix 集成,但是最容易的。
最简单的和 HTTP 集成的方式是通过路由器。因为 Guardian 的 HTTP 集成是基于 plugs 的,所以你可以在任何用 plug 的地方使用它。
Guardian plug 使用的一般流程是这样的:
从请求(任何地方)中找到令牌,并验证:
Verify*
plugs可选地从令牌中获取相应的资源:
LoadResource
plug确保请求中包含有效的令牌,如果没有,拒绝访问:
EnsureAuthenticated
plug
为了满足应用开发者的需求,Guardian 分开实现了这些不同的阶段。使用 Verify*
plugs 可以获取令牌。
我们创建一些 Phoenix 管道(pipelines)来看看如何操作。
这些管道可以被实现成不同的组合来满足各种验证的业务场景。第一个首先尝试从会话中获取令牌,如果没有的话就从 header 从找。如果找到了,它会帮你定位对应的资源。
第二个管道确保必须有一个有效的令牌存在,并且是“access”类型。把这些 piplines 加到 scope 里就可以使用了。
上面的 login 路由如果发现请求里带有验证通过的用户信息的话,会注入相应的用户。第二个 scope 则确保必须有有效的令牌才能通过后续的操作。你不是_必须_把它们配置在管道中,你还可以很灵活地配置到控制器(controller)里。不过这里是最简配置,所以才这样操作。
往事具备,只差东风了。就是添加到 EnsureAuthenticated
plug 的错误处理。这是一个简单的模块:
unauthenticated/2
unauthorized/2
这两个函数都同样接收 Plug.Conn 这个结构体,和一个参数映射表,并且处理相应的错误。你甚至还可以使用 Phoenix 的控制器!
和控制器结合
在控制器里,有好几种方式从中获取当前登录的用户。我们先看最简单的方法。
通过 Guardian.Phoenix.Controller
模块,你的操作(actions)可以获得额外两个参数。要记住的是,如果没有使用 EnsureAuthenticated
,你获得的可能是空的 user 和 claims 值。
另一种更灵活,或者说繁琐的方式是,使用 plug 辅助函数。
登入/登出
登入,和登出浏览器会话非常容易实现。在你的 login 控制器里:
当通过 API 登录的时候,处理的方式会有点不同。因为当前并没有会话,你还必须把原始的令牌返回给客户端。要处理 API 登录,你需要从 Authorization
header 中获取令牌。这种方式对不需要保持和使用会话是可行的。
浏览器会话登录实际上会调用 encode_and_sign
这个函数,所以你这样调用也是可以的。
最后更新于