欢迎各位兄弟 发布技术文章
这里的技术是共享的
最近在开发一个 Android 程序,需要做用户登录和认证功能,另外服务器用的是 Laravel 框架搭建的。最终决定用 JWT 实现API接口的认证。
JWT 是 Json Web Tokens 的缩写,与传统 Web 的 Cookies 或者 Session 方式的认证不同的是,JWT 是无状态的,服务器上不需要对 token 进行存储,也不需要和客户端保持连接。而 JWT 的 token 分3个部分,首先是头部 ,表明这是一个JWT,并指明加密方式,第二部分是负载,其中可以包含 账户名、ID、邮箱等用户信息,同时也包含了token到期时间,以 Unix 时间戳的方式记录,头部和负载都会进行 base64 编码,最后一部分是签名,用来验证负载的信息是否正确。
服务器上会保存一个全局 JWT_SECRET ,用于生成 token 和验证 token。在用户登录成功后,服务器从数据库获取用户的相关信息,计算出 token 到期时间,生成头部和负载并编码,再将前面的内容使用 JWT_SECRET 进行加密,生成签名,最后将3个部分合并返回给客户端。
客户端访问需要认证的客户端时,在 Http 请求头部加上 Authrization 字段,内容为 Bearer 加 token。服务器收到请求后,利用 JWT_SECRET 验证 token 是否合法,从负载中提取到期时间确认 token 是否过期,再从 token 提取用户信息,与数据库进行对比。如果这些都通过的话就可以进行后续操作了。
这里指大概介绍一下 JWT 的特点和验证流程,更详细的介绍大家可以自行搜索,或者访问 https://jwt.io/introduction/ 。
先上代码:
https://github.com/zhongchenyu/jokes-laravel
因为后续代码可能会做重构,本文所介绍的代码保存在 demo2 分支,请 checkout demo2
。
我们从一个空的 Laravel 项目开始着手,这里假设通过 laravel new project_name 命令安装了一个空的项目,并且完成了初始化配置,已经可以访问一些简单的测试 API了。下面开始搞 JWT。
首先通过 composer 安装 PHP 的 JWT 库: composer require tymon/jwt-auth 0.5.*
在 config/app.php 下添加 JWTAuthServiceProvider:
也是在 config/app.php 下,注册门面:
发布 JWT 的配置文件到 config/jwt.php :
生成 JWT_SECRET,执行命令php artisan jwt:generate
,
会在 config/jwt.php 下生成'secret' => env('JWT_SECRET', 'UCDncib***wOY6gj0sD'),
,从 .env 的 JWT_SECRET 取值,如果没有再取后面的默认值,因为 config/jwt.php 是要随着 Git 版本发布的,所有最好在不同的环境上分别执行命令来生成 secret ,并且保持到 .env 文件中, .env 文件默认是 gitignore 的。
这样 JWT 库就安装好可以使用了。
首先创建好数据库,并修改 .env 文件中相关的值:
这是数据库还是空的,没有任何 table,需要先进行数据库迁移。要做用户认证,肯定需要一个用户表,这个其实 Laravel 在已经帮我们做好的,在 database/migrations 下面已经生成了迁移文件:
看一下 create_users_table.php 的代码,创建一个 users 表,包含 id、name、email、password等列。
执行php artisan migrate
命令就会在 Jokes 数据库下创建好 users
表了。
对应的 Model 也已经自动创建好了,就是 app/User.php 文件。
users table 创建好后,当然是要生成 user 数据了,可以用 seeder 来生成测试用的 Faker 数据,初始代码也基本完成了 users 的 seeder。不过我们直接实现注册 API,通过 API 来创建数据。
在 Routes/api.php 下增加一条路由,这里我们使用的是 Dingo/Api :
1
访问 register 路径,会调用 Auth\RegisterController.php 控制器下的 register 方法,看下代码:
其实大部分代码 Laravel 已经自动生成好了,但是由于原来的代码是用于 Web 注册的,注册成功后会重定向到登录页面,但是我们是给 API 做认证,所有就把这块代码改一下。
validator 方法是用来验证 Http 请求参数的。
'name' => 'required|string|max:255'
表示 name 是必须的,并且是最大长度255的字符串。
'email' => 'required|string|email|max:255|unique:users'
表示 email 是必须的,为最长255的邮箱格式的字符串,并且要和 users 数据库中的email不重复。
'password' => 'required|string|min:6'
表示 password是必须的,为最短6位的字符串,也可以加上 confirmed,表示需要二次确认,在请求参数中要在加上 password_confirmation ,并且和 password 是相同的才能通过验证,这里我们就先不用这个确认了。
create 方法将注册的用户信息写到数据库的 users table 中,其中 password 是经过加密存储的。
在 register 方法中, 先调用 validator,检查请求参数是否合法,通过后调用 create 将此用户数据写入数据库,在根据用户信息生成一个 token,返回给客户端。
测试一下,注册成功,返回 token:
首先在 Routes/api.php 下添加路由: $api->get('login', 'Auth\AuthenticateController@authenticate');
看下 Auth\AuthenticateController 下的 authenticate 方法:
代码也是基本初始化好了的,但是原来的代码登录后只返回 token,我们修改下,加上返回 User 信息。
先将 请求中的 email 和 password 存到 $credentials 中,再通过$token = JWTAuth::attempt($credentials)
检验 email 和 password,并尝试转换成 token,如果失败,则返回异常,如果成功则将 user 和 token 返回。
测试一下,用刚才的账号登录,成功获取到用户信息和 token:
用户完成注册登录后,获取到 token,接下来就可以访问需要认证的 API 了,这里我们建一个简单的 API 来说明认证的实现方法。
假设用户需要获取通知信息 notices,服务器要求必须在登录后才能获取。
首先添加路由,这里用到了路由组和中间件(middleware):
在 app/kernel.php 文件下添加路由中间件:
此 group 下的所有路由,都需要先通过中间件处理,这里用的是 jwt.auth,及只有通过 jwt 认证之后,才能继续后面的访问。
这里只用来测试,NoticeController 下的 index 方法的返回数据直接是一句话:
测试一下用正确的 token 访问,在 Header 添加 Authrization 项,记住在 token 前加 Bearer ,可以获取数据:
测试用非法 token 访问,返回400错误:
测试过期 token 访问, 返回401错误:
同时我们还添加了 user 路由,JWTAuth::parseToken()->authenticate()
通过 token 获取用户:
请求中添加 Authrization头,测试,这个 API 不仅用来获取用户信息,也作为 客户端存储的 token是否有效的检测 API:
至此,服务器上的认证相关的 API 接口就都准备好了,下篇文章将讲 Android 客户端的实现。