Laravel Passport API 认证使用小结

八月 4, 2017 发布在 Laravel

看到Laravel-China 社区常有人问 Laravel Passport 用于密码验证方式来获取 Token 的问题,刚好我最近一个 API 项目使用 Laravel Dingo Api+Passport,也是使用 Oauth2 的'grant_type' => 'password'密码授权来做 Auth 验证,对于如何做登录登出,以及多账号系统的认证等常用场景做一下简单的使用小总结。

基本配置

基本安装配置主要参照官方文档,具体不详细说,列出关键代码段

config/auth.php

'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
], 'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\Models\User::class
],
],

Providers/AuthServiceProvider.php

    public function boot()
{
$this->registerPolicies(); //默认令牌发放的有效期是永久
//Passport::tokensExpireIn(Carbon::now()->addDays(2));
//Passport::refreshTokensExpireIn(Carbon::now()->addDays(4));
Passport::routes(function (RouteRegistrar $router) {
//对于密码授权的方式只要这几个路由就可以了
config(['auth.guards.api.provider' => 'users']);
$router->forAccessTokens();
});
}

Middleware/AuthenticateApi.php 自定义中间件返回

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authenticate;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; class AuthenticateApi extends Authenticate
{
protected function authenticate(array $guards)
{ if ($this->auth->guard('api')->check()) {
return $this->auth->shouldUse('api');
} throw new UnauthorizedHttpException('', 'Unauthenticated');
}
}

App/Http/Kernel.php

    /**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'api-auth' => AuthenticateApi::class,
......
];
}

账号验证字段不止邮箱

对于账号验证不止是数据表中的 emial 字段,还可能是用户名或者手机号字段只需要在 User 模型中添加findForPassport方法,示例代码如下:

App\Models\Users

class User extends Authenticatable implements Transformable
{
use TransformableTrait, HasApiTokens, SoftDeletes;
public function findForPassport($login)
{
return $this->orWhere('email', $login)->orWhere('phone', $login)->first();
}
}

客户端获取 access_token 请求只传用户名和密码

对于密码授权的方式需要提交的参数如下:

$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);

但是客户端请求的时候不想把grant_type,client_id,client_secret,scope放到请求参数中或者暴露给客户端,只像 JWT 一样只发送usernamepassword 怎么办?很简单我们只要将不需要请求的放到配置文件中,然后客户端请求用户名密码以后我们再向oauth/token发送请求带上相关的配置就可以了。

.env.php

OAUTH_GRANT_TYPE=password
OAUTH_CLIENT_ID=1
OAUTH_CLIENT_SECRET=EvE4UPGc25TjXwv9Lmk432lpp7Uzb8G4fNJsyJ83
OAUTH_SCOPE=*

config/passport.php 当然该配置你可以配置多个client

return [
'grant_type' => env('OAUTH_GRANT_TYPE'),
'client_id' => env('OAUTH_CLIENT_ID'),
'client_secret' => env('OAUTH_CLIENT_SECRET'),
'scope' => env('OAUTH_SCOPE', '*'),
];

LoginController.php的示例代码如下,因为用了Dingo Api配置了api前缀,所以请求/api/oauth/token

 	/**
* 获取登录TOKEN
* @param LoginRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function token(LoginRequest $request)
{
$username = $request->get('username');
$user = User::orWhere('email', $username)->orWhere('phone', $username)->first(); if ($user && ($user->status == 0)) {
throw new UnauthorizedHttpException('', '账号已被禁用');
} $client = new Client();
try {
$request = $client->request('POST', request()->root() . '/api/oauth/token', [
'form_params' => config('passport') + $request->only(array_keys($request->rules()))
]); } catch (RequestException $e) {
throw new UnauthorizedHttpException('', '账号验证失败');
} if ($request->getStatusCode() == 401) {
throw new UnauthorizedHttpException('', '账号验证失败');
}
return response()->json($request->getBody()->getContents());
}

退出登录并清除 Token

对于客户端退出后并清除记录在oauth_access_tokens表中的记录,示例代码如下:

  /**
* 退出登录
*/
public function logout()
{
if (\Auth::guard('api')->check()) {
\Auth::guard('api')->user()->token()->delete();
} return response()->json(['message' => '登出成功', 'status_code' => 200, 'data' => null]);
}

根据用户 ID 认证用户

app('auth')->guard('api')->setUser(User::find($userId));

多用户表(多 Auth)认证

比如针对客户表和管理员表分别做 Auth 认证的情况,也列出关键代码段:

 'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
], 'admin_api' => [
'driver' => 'passport',
'provider' => 'admin_users',
],
], 'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\Models\User::class
], 'admin_users' => [
'driver' => 'eloquent',
'model' => \App\Models\AdminUser::class
],
],

新建一个PasspordAdminServiceProvider来实现我们自己的PasswordGrant,别忘了添加到config/app.phpproviders配置段中

AppProviders/PasspordAdminServiceProvider

<?php

namespace App\Providers;

use App\Foundation\Repository\AdminUserPassportRepository;
use League\OAuth2\Server\Grant\PasswordGrant;
use Laravel\Passport\PassportServiceProvider as BasePassportServiceProvider;
use Laravel\Passport\Passport; class PasspordAdminServiceProvider extends BasePassportServiceProvider
{
/**
* Create and configure a Password grant instance.
*
* @return PasswordGrant
*/
protected function makePasswordGrant()
{
$grant = new PasswordGrant(
//主要是这里,我们调用我们自己UserRepository
$this->app->make(AdminUserPassportRepository::class),
$this->app->make(\Laravel\Passport\Bridge\RefreshTokenRepository::class)
); $grant->setRefreshTokenTTL(Passport::refreshTokensExpireIn()); return $grant;
} }

新建AdminUserPassportRepository,Password 的验证主要通过getUserEntityByUserCredentials,它读取配置的guards对应的provider来做认证,我们重写该方法,通过传递一个参数来告诉它我们要用哪个guard来做客户端认证

<?php

namespace App\Foundation\Repository;

use App;
use Illuminate\Http\Request;
use League\OAuth2\Server\Entities\ClientEntityInterface;
use Laravel\Passport\Bridge\UserRepository;
use Laravel\Passport\Bridge\User;
use RuntimeException; class AdminUserPassportRepository extends UserRepository
{ public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity)
{
$guard = App::make(Request::class)->get('guard') ?: 'api';//其实关键的就在这里,就是通过传递一个guard参数来告诉它我们是使用api还是admin_api provider来做认证
$provider = config("auth.guards.{$guard}.provider");
if (is_null($model = config("auth.providers.{$provider}.model"))) {
throw new RuntimeException('Unable to determine user model from configuration.');
} if (method_exists($model, 'findForPassport')) {
$user = (new $model)->findForPassport($username);
} else {
$user = (new $model)->where('email', $username)->first();
} if (!$user) {
return;
} elseif (method_exists($user, 'validateForPassportPasswordGrant')) {
if (!$user->validateForPassportPasswordGrant($password)) {
return;
}
} elseif (!$this->hasher->check($password, $user->password)) {
return;
} return new User($user->getAuthIdentifier());
}
}

登录和单用户系统一样,只是在请求oauth/token的时候带上guard参数,示例代码如下:

Admin/Controllers/Auth/LoginController.php

<?php

namespace Admin\Controllers\Auth;

use Admin\Requests\Auth\LoginRequest;
use App\Http\Controllers\Controller;
use App\Models\User;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/ use AuthenticatesUsers; /**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
} /**
* 获取登录TOKEN
* @param LoginRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function token(LoginRequest $request)
{
$username = $request->get('username');
$user = User::orWhere('email', $username)->orWhere('phone', $username)->first(); if ($user && ($user->status == 0)) {
throw new UnauthorizedHttpException('', '账号已被禁用');
} $client = new Client();
try {
$request = $client->request('POST', request()->root() . '/api/oauth/token', [
'form_params' => config('passport') + $request->only(array_keys($request->rules())) + ['guard' => 'admin_api']
]);
} catch (RequestException $e) {
throw new UnauthorizedHttpException('', '账号验证失败');
} if ($request->getStatusCode() == 401) {
throw new UnauthorizedHttpException('', '账号验证失败');
}
return response()->json($request->getBody()->getContents());
} /**
* 退出登录
*/
public function logout()
{
if (\Auth::guard('admin_api')->check()) {
\Auth::guard('admin_api')->user()->token()->delete();
} return response()->json(['message' => '登出成功', 'status_code' => 200, 'data' => null]);
}
}

转载请注明:  转载自Ryan 是菜鸟 | LNMP 技术栈笔记

Laravel Passport API 认证使用小结的更多相关文章

  1. Laravel 的 API 认证系统 Passport 三部曲(二、passport的具体使用)

    GQ1994 关注 2018.04.20 09:31 字数 1152 阅读 1316评论 0喜欢 1 参考链接 Laravel 的 API 认证系统 Passport 三部曲(一.passport安装 ...

  2. laravel passport client_credentials

    我是使用 Laravel 5.4 + Dingo Api + passport/jwt 两个验证方式 目前需要用到 passport 的 client_credentials 获取 token成功之后 ...

  3. [ Laravel 5.3 文档 ] 安全 ―― API认证(Passport)保障安全性。

    1.简介 Laravel通过传统的登录表单已经让用户认证变得很简单,但是API怎么办?API通常使用token进行认证并且在请求之间不维护session状态.Laravel使用LaravelPassp ...

  4. laravel Passport - 创建 REST API 用户认证以及Dingo/Api v2.0+Passport实现api认证

    第一部分: 安装passport 使⽤ Composer 依赖包管理器安装 Passport : composer require laravel/passport 接下来,将 Passport 的服 ...

  5. laravel Passport - Dingo/Api v2.0+Passport 实现 api 认证

    第一部分: 安装passport 使⽤ Composer 依赖包管理器安装 Passport : composer require laravel/passport 接下来,将 Passport 的服 ...

  6. laravel 的passport Oauth 认证登录请求 的 oauth_token 重置

    laravel 的passport Oauth 认证登录请求 的 oauth_token 重置    使用API登录认证是需要获取访问令牌,方法为: 参数: grant_type —— 密码模式固定为 ...

  7. Laravel Passport认证-多表、多字段解决方案

    Laravel Passport认证-多表.多字段解决方案 2018年08月19日 09:31:01 醉卧码场君莫笑 阅读数:1632   1. 概述 API 通常使用令牌(token)进行认证并且在 ...

  8. 单点登录 - API 认证系统 Passport(二)

    安装 composer require laravel/passport=~4.0 notes: 1)确保系统安装unzip.zip等命令. 2)composer 安装出现 Authenticatio ...

  9. laravel dingo/api添加jwt-auth认证

    前面我们学了laravel dingo/api创建简单的api,这样api是开放给所有人的,如何查看和限制api的调用呢?可以用jwt-auth来验证,JSON Web Token Authentic ...

随机推荐

  1. Linux系列之压缩与解压

    1.压缩技术 1.常用命令实例 1.zip格式的压缩与解压缩 zip是压缩指令,unzip是解压指令.zip指令既可以压缩文件,也可以压缩目录.压缩会自动保留源文件,解压会自动保留压缩文件. zip  ...

  2. C++目录

    C++ lambda表达式 C++中如何设计一个类只能在堆或者栈上创建对象,面试题 C++之STL总结精华笔记 指针强制类型转换的理解 关于指针类型和指针类型转换的理解 C++继承种类 C++ 单例模 ...

  3. Thinkphp自定义生成缩略图尺寸的方法

    Thinkphp自定义生成缩略图尺寸的方法,本实例中生成两张不同尺寸的图片:第一张是大图350*350,第二张 50*50的缩略图 Image类是Thinkphp系统自带的,可以研究下,这个缩略图类很 ...

  4. Windows安全日志

    在运行中输入:eventvwr.msc,即可打开事件日志. 登录类型 描述 2 互动(键盘和屏幕的登录系统) 3 网络(即连接到共享文件夹从其他地方在这台电脑上网络) 4 批处理(即计划任务) 5 服 ...

  5. 编写程序来实现实现strcat()功能

    strcat(字符数组1,字符串2) 字符串2的内容复制连接在字符数组1的后面,其返回值为字符数组1的地址 /* strcat(字符数组1,字符串2) 字符串2的内容复制连接在字符数组1的后面,其返回 ...

  6. WMIC命令的利用技巧

    WMIC扩展WMI(Windows Management Instrumentation,Windows管理工具),提供了从命令行接口和批命令脚本执行系统管理的支持.在WMIC出现之前,如果要管理WM ...

  7. docker 入门4 - 群 【翻译】

    开始,第 4 部分:群 先决条件 安装 Docker 版本 1.13 或更高版本. 获取第 3 部分先决条件中所述的 Docker Compose. 获取 Docker Machine, Mac 的 ...

  8. 怎样理解document的快捷方式属性

    所谓 "快捷方式属性" , 也就是说它们不是必须的, 只是在操作dom时可以更为方便地获取. 主要有下面8个: 1. 获取当前文档所属的window对象: document.def ...

  9. C# WebForm 屏蔽输入框的验证

    按钮做界面跳转时,屏蔽输入框的验证可添加属性:  CausesValidation="FALSE" <form runat="server"> &l ...

  10. net core体系-web应用程序-4asp.net core2.0 项目实战(CMS)-第二章 入门篇-快速入门ASP.NET Core看这篇就够了

    .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了   原文链接:https://www.cnblogs.com/yilezhu/p/9985451.ht ...