我正在尝试使用Laravel Passport设置多个身份验证,但它似乎并不支持它。我使用密码授予发出令牌,要求我传递想要访问令牌的用户的用户名/密码。
我有3个auth警卫/提供者设置,总共4个。 用户,供应商,管理员和API
其中2个Auths需要访问护照,因此每个用户都需要能够发放令牌。但Passport会自动获取API身份验证提供程序,但我希望根据登录的用户进行更改。如果用户执行用户提供程序,如果是供应商,那么供应商提供商
但Passport目前仅支持1种用户类型,因此默认为 API提供程序。
这有什么更好的吗?或者我应该使用基于角色的身份验证。
答案 0 :(得分:5)
如果你还需要。
我更喜欢使用角色,有一个惊人的插件:https://github.com/larapacks/authorization
但是如果你以某种方式需要它,你将能够按照下面的步骤使用。
对于多重防护,你必须覆盖一些代码。
您可以创建自己的PassportServiceProvider,而不是加载PassportServiceProvider,并扩展PassportServiceProvider并覆盖方法makePasswordGrant。 在此方法上,您将更改自己的存储库扩展的Passport UserRepository。在用户存储库上,您必须更改动态模型配置(我从请求属性加载,但您可以从任何地方获取)。
你可能不得不覆盖别的东西,但我做了一个测试并且有效。
例如:
PassportServiceProvider
namespace App\Providers;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant\PasswordGrant;
use Laravel\Passport\PassportServiceProvider as BasePassportServiceProvider;
use Laravel\Passport\Passport;
class PassportServiceProvider extends BasePassportServiceProvider
{
/**
* Create and configure a Password grant instance.
*
* @return PasswordGrant
*/
protected function makePasswordGrant()
{
$grant = new PasswordGrant(
$this->app->make(\App\Repositories\PassportUserRepository::class),
$this->app->make(\Laravel\Passport\Bridge\RefreshTokenRepository::class)
);
$grant->setRefreshTokenTTL(Passport::refreshTokensExpireIn());
return $grant;
}
}
UserRepository
namespace App\Repositories;
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 PassportUserRepository extends UserRepository
{
/**
* {@inheritdoc}
*/
public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity)
{
$guard = App::make(Request::class)->attributes->get('guard') ?: 'api';
$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());
}
}
PS:对不起,我的英文不好。
答案 1 :(得分:3)
您必须修改主库文件。
1)文件:vendor \ laravel \ passport \ src \ Bridge \ UserRepository.php
查找 getUserEntityByUserCredentials 并复制完整方法,然后使用名称 getEntityByUserCredentials粘贴此方法。不要修改主要功能,因为它在某处使用。
//Add the $provider variable at last or replace this line.
public function getEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity, $provider)
然后,在新的重复功能中,找到以下内容:
$provider = config('auth.guards.api.provider');
并将其替换为:
$provider = config('auth.guards.'.$provider.'.provider');
2)文件:vendor \ league \ oauth2-server \ src \ Grant \ PasswordGrant.php
在函数 validateUser 中,在行号后添加以下代码。 88
$provider = $this->getRequestParameter('provider', $request);
if (is_null($provider)) {
throw OAuthServerException::invalidRequest('provider');
}
添加后,用
替换以下代码$user = $this->userRepository->getEntityByUserCredentials(
$username,
$password,
$this->getIdentifier(),
$client,
$provider
);
现在尝试使用邮递员
在输入字段中添加提供者字段,如
provider = api_vendors
OR
provider = api_admins
OR
provider = api_users
And so on....
确保已添加提供程序并在config / auth.php中设置驱动程序
'guards' => [
'api_admins' => [
'driver' => 'passport',
'provider' => 'admins',
],
'api_vendors' => [
'driver' => 'passport',
'provider' => 'vendors',
],
我希望这会有所帮助。
答案 2 :(得分:1)
我为此问题创建了一个小包。这是完整文档link
的链接但要点是,只要user
实体登录,它就会检查警卫和提供者。
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'customers' => [
'driver' => 'passport',
'provider' => 'customers'
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => 'App\User',
],
/**
* This is the important part. You can create as many providers as you like but right now,
* we just need the customer
*/
'customers' => [
'driver' => 'eloquent',
'model' => 'App\Customer',
],
],
你应该有一个像这样的控制器:
<?php
namespace App\Http\Controllers\Auth;
use App\Customers\Customer;
use App\Customers\Exceptions\CustomerNotFoundException;
use Illuminate\Database\ModelNotFoundException;
use Laravel\Passport\Http\Controllers\AccessTokenController;
use Laravel\Passport\TokenRepository;
use League\OAuth2\Server\AuthorizationServer;
use Psr\Http\Message\ServerRequestInterface;
use Lcobucci\JWT\Parser as JwtParser;
class CustomerTokenAuthController extends AccessTokenController
{
/**
* The authorization server.
*
* @var \League\OAuth2\Server\AuthorizationServer
*/
protected $server;
/**
* The token repository instance.
*
* @var \Laravel\Passport\TokenRepository
*/
protected $tokens;
/**
* The JWT parser instance.
*
* @var \Lcobucci\JWT\Parser
*/
protected $jwt;
/**
* Create a new controller instance.
*
* @param \League\OAuth2\Server\AuthorizationServer $server
* @param \Laravel\Passport\TokenRepository $tokens
* @param \Lcobucci\JWT\Parser $jwt
*/
public function __construct(AuthorizationServer $server,
TokenRepository $tokens,
JwtParser $jwt)
{
parent::__construct($server, $tokens, $jwt);
}
/**
* Override the default Laravel Passport token generation
*
* @param ServerRequestInterface $request
* @return array
* @throws UserNotFoundException
*/
public function issueToken(ServerRequestInterface $request)
{
$body = (parent::issueToken($request))->getBody()->__toString();
$token = json_decode($body, true);
if (array_key_exists('error', $token)) {
return response()->json([
'error' => $token['error'],
'status_code' => 401
], 401);
}
$data = $request->getParsedBody();
$email = $data['username'];
switch ($data['provider']) {
case 'customers';
try {
$user = Customer::where('email', $email)->firstOrFail();
} catch (ModelNotFoundException $e) {
return response()->json([
'error' => $e->getMessage(),
'status_code' => 401
], 401);
}
break;
default :
try {
$user = User::where('email', $email)->firstOrFail();
} catch (ModelNotFoundException $e) {
return response()->json([
'error' => $e->getMessage(),
'status_code' => 401
], 401);
}
}
return compact('token', 'user');
}
}
并且请求应该是:
POST /api/oauth/token HTTP/1.1
Host: localhost
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
grant_type=password&username=test%40email.com&password=secret&provider=customers
要检入您的控制器谁是登录用户,您可以执行以下操作:
auth()->guard('customers')->user()
答案 3 :(得分:1)
我们正在等待Laravel将此功能添加到其程序包中,但是对于那些想要添加此功能的人,我建议使用this程序包
答案 4 :(得分:1)
我已经在 Laravel 7 中完成了此操作,没有任何自定义代码,如建议的其他答案一样。我刚刚如下更改了3个文件
config/auth.php
文件(我的新表名为doctors
)
'guards' => [
...
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
'api-doctors' => [
'driver' => 'passport',
'provider' => 'doctors',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
'doctors' => [
'driver' => 'eloquent',
'model' => App\Doctor::class,
],
],
类似于Doctor
模型(User
)修改我的App/Doctor.php
模型
....
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class Doctor extends Authenticatable
{
use HasApiTokens, Notifiable;
最后使用中间件routes/api.php
文件定义路由,如下所示
//normal middleware which exist already
Route::post('/choose', 'PatientController@appointment')->middleware('auth:api');
//newly created middleware provider (at config/auth.php)
Route::post('/accept', 'Api\DoctorController@allow')->middleware('auth:api-doctors');
现在,当您创建新的oauth客户端时,可以使用artisan passport:client --password --provider
此命令,该命令会在命令行中提示您选择表
在此之前,不要忘记运行
php artisan config:cache
php artisan cache:clear
您还可以通过将oauth_clients
列值provider
替换为users
在doctors
表中手动创建用户
参考链接的一些提示 https://laravel.com/docs/7.x/passport#customizing-the-user-provider
答案 5 :(得分:0)
Laravel护照仅与用户一起提供。即使您通过使用PasswordGrant&amp;添加上述更改来获取令牌UserRepository,当您为Post发送API调用并获取请求时,不会使用除User之外的已更改的护照提供程序。
如果必须作为供应商和客户需要,最好使用会话驱动程序创建多个auth。让用户&#39;仅适用于表列支持admin,API,vendor等的护照的模型。
答案 6 :(得分:0)
如果您正在寻找 Passport Multi-Auth API 的解决方案 我建议您this solution