通过 Passport 实现 API 请求认证:移动端应用篇(密码授权令牌)
学院君注:虽然教程的名字叫移动端应用篇,但是完全适用于同一个公司不同系统间的认证,包括不同 Web 网站间认证。
虽然我们在上一篇教程中做好了 Passport 后端初始化配置,但是注册的相关路由和初始化的数据库数据并没有用到,从这一篇教程开始,我们将基于 Passport 实现一些更复杂的 API 授权认证,会逐步用到这些路由和数据库记录。
我们在上一篇教程中已经介绍了单页面应用的 API 认证,这篇教程我们将就移动端应用的 API 请求认证展开讨论,这些移动端应用包括客户端 App、H5应用(从 Web 网站分离出去的独立移动网站,基于 HTML5 开发,通常简称 H5 应用,域名与 Web 网站不同)。对于这些自有网站,我们通常也不希望进行常见的 OAuth 跳转授权,会影响用户体验,因此我们基于 Passport 提供的密码授权令牌来实现相应的 API 请求认证。
1、创建一个测试移动端应用
既然是分离的独立应用之间的认证,我们先来创建一个新的应用用来测试,名字叫做 testapp
:
composer create-project --prefer-dist laravel/laravel testapp
安装完成后,进入应用根目录运行 npm install
初始化前端资源,然后配置这个应用的域名为 app.test
。
下面我们将通过 testapp
应用来模拟移动端应用,通过主项目 blog
作为后端应用,移动端应用访问后端应用的认证 API 接口时,需要后端应用授权才能访问,在 OAuth 服务中,这个授权通过颁发一个访问令牌实现。我们使用密码授权令牌的原因是和授权码令牌相比,这个过程中没有授权确认和跳转,整个过程就像是用户提交登录表单进行认证一样。下面我们就来简单演示下这个授权过程。
2、在后端应用中注册移动端应用
要让移动端应用和后端应用之间进行授权认证,需要在后端应用 blog
中注册对应的移动端应用 testapp
,我们在 blog
项目根目录下运行这个命令来注册 testapp
:
php artisan passport:client --password
我们将应用的名称设置为 testapp
就好了,其它都会自动生成,执行完毕后在 oauth_clients
数据表中修改对应数据库记录的 redirect
字段值为 http://app.test/auth/callback
:
这样,我们就完成了移动端应用在后端应用中的注册。
注:对于大型项目来说,可以通过后台注册中心申请审核的方式完成新应用的注册,方便统一管理。这里我们只是通过 Artisan 命令快速演示。
3、配置移动端应用
回到 testapp
应用,在项目根目录下的 .env
文件中新增两个配置项,将刚刚在后端应用中注册的 testapp
应用配置信息填写到这里:
CLIENT_ID=7
CLIENT_SECRET=2JPrCvRyoJ14f0OqCe6nnQZNDfPLNNPY7TcfDnco
然后在 config/services.php
中新增如下配置项:
'blog' => [
'appid' => env('CLIENT_ID'),
'secret' => env('CLIENT_SECRET'),
'callback' => 'http://app.test/auth/callback'
]
4、在移动端应用填写登录表单进行登录
接下来,在 testapp
应用中,我们将借助 Laravel 自带的认证脚手架快速实现认证路由和视图,以完成登录表单和请求提交:
php artisan make:auth
然后在 Auth/LoginController
控制器中重写 login
方法,将默认到数据库检查用户登录凭证的逻辑改为将请求发送到后端应用获取授权令牌:
// 在控制器顶部引入如下命名空间
use GuzzleHttp\Client;
use Illuminate\Http\Request;
// 重写 AuthenticatesUsers 中的 login 方法
public function login(Request $request)
{
$request->validate([
'email' => 'required|string',
'password' => 'required|string',
]);
$http = new Client();
// 发送相关字段到后端应用获取授权令牌
$response = $http->post('http://blog.test/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => config('services.blog.appid'),
'client_secret' => config('services.blog.secret'),
'username' => $request->input('email'), // 这里传递的是邮箱
'password' => $request->input('password'), // 传递密码信息
'scope' => '*'
],
]);
return response($response->getBody());
}
5、测试后端应用授权移动端认证
这一次,我们不需要在 testapp
中创建用户表数据库,直接在浏览器访问移动端应用登录表单 http://app.test/login
,输入我们在后端应用 blog
中注册过的用户信息,就好像我们在 testapp
中注册过它们一样:
点击登录,会将用户登录凭证和应用配置数据传递到后端应用的 oauth/token
路由,通过后端应用中 Passport 底层的密码授权类进行校验,校验成功后就会返回令牌信息给移动端应用:
返回结果中包含四个字段,access_token
是授权令牌,token_type
表示认证类型是 Bearer
,我们可以将这个 access_token
值设置到 Bearer Authentication
请求头去请求需要认证的后端 API 接口。refresh_token
在令牌过期后刷新令牌时使用,最后 expires_in
表示令牌有效期(单位是秒,即有效期一年)。
接下来,以上篇教程中提到的后端认证 API 接口 http://blog.test/api/user
为例在 Postman 中进行演示,我们选择请求 Authorization 的类型为 Bearer Token,然后将 access_token
值拷贝到下图的 Token 字段中,发起请求就可以获取到认证接口返回数据了:
6、通过 JavaScript 消费 API
以上只是模拟测试,如果是在真实的 App 或者 H5 应用环境中完成上述操作,登录表单和获取授权令牌的逻辑和上面类似,只不过需要将对应的 PHP 代码转化为 JavaScript 代码或客户端语言代码。获取到请求令牌(access_token
)值后将其存储到本地 Cookie 或者页面头部的 meta
标签里,后续请求后端认证 API 接口时带上 Bearer 认证请求头就好了,以 H5 为例,如果我们将令牌值存储在 meta 标签中,可以这么设置全局请求头:
let api_token = document.head.querySelector('meta[name="api-token"]');
if (api_token) {
window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + api_token.content;
}
这样,我们就可以通过 axios
库发起对后端认证 API 的请求了。
7、令牌的有效期
设置有效期
如果你做过微信、微博之类开发平台接口接入一定对令牌有效期有所了解,Passport 生成的授权令牌默认有效期是一年,但是为了提升系统安全性,也可以自定义配置其有效期。我们可以在 AuthServiceProvider
的 boot
方法中通过 Passport 门面上的 tokensExpireIn
或 refreshTokensExpireIn
方法来设置令牌有效期:
Passport::tokensExpireIn(now()->addDays(7));
Passport::refreshTokensExpireIn(now()->addDays(7));
以上两个方法等效,使用任意一个都可以,都是设置授权令牌有效期为 7
天。过期之后,需要刷新令牌。
刷新令牌
当授权访问令牌过期后,我们可以通过在 oauth/token
路由请求中指定操作类型为 refresh_token
来刷新令牌:
$http = new GuzzleHttp\Client;
$response = $http->post('http://blog.test/oauth/token', [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => 'your-refresh-token',
'client_id' => 'your-client-id',
'client_secret' => 'your-client-secret',
'scope' => '*',
],
]);
return response($response->getBody());
刷新令牌后,会生成新的令牌并返回,同时将老的令牌撤销。
31 Comments
你好,测试后端应用授权移动端认证,这边我测试的一直报错:Client error:
POST http://blog.com/oauth/token
resulted in a401 Unauthorized
response 求解答你好,为什么我这里app端登录时一直返回500,“Server error:
POST http://apidemo2.test/oauth/token
resulted in a500 Internal Server Error
response:\n”服务端报错了 看下错误日志
查看日志,显示 'app.oauth_clients' 表未找到,但SQL语句却是 (SQL: select * from
oauth_clients
whereid
= 3 limit 1).env 文件中的数据库配置的是 apidemo2,是不是应该把移动端的 .env 文件数据库也配置为后端应用对应的数据库?
解决了,是移动端的数据库未配置成后端对应的数据库,谢谢学院君
学院君你好,按理来说移动端照这样配置的话,是可以不用选择数据库的,为什么我把移动端 .env中数据库的配置留空会报错,而且错误还是在服务端的日志里记录的,服务端的.env文件是配置正确的。并且移动端即使配置了数据库,数据库不选择服务端的数据库也会报错,在下一篇教程中也是这样,那这样的话不就失去了授权的意义了吗?
是不是认证服务搞到移动端去了?
刚才到看了下,移动端的 vendor/laravel 文件夹中并没有 passport 子文件夹,OAuth 服务的私钥和公钥也是在服务端的 storage 下
其余的步骤是和文章中所写的是一样的