借鉴地址:https://github.com/laravel/passport/issues/71#issuecomment-330506407
trait文件
<?
namespace App\Traits;
use App\Models\User;
use GuzzleHttp\Psr7\Response;
use Illuminate\Events\Dispatcher;
use Laravel\Passport\Bridge\AccessToken;
use Laravel\Passport\Bridge\AccessTokenRepository;
use Laravel\Passport\Bridge\Client;
use Laravel\Passport\Bridge\RefreshTokenRepository;
use Laravel\Passport\Bridge\Scope;
use Laravel\Passport\Passport;
use Laravel\Passport\TokenRepository;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Exception\UniqueTokenIdentifierConstraintViolationException;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
//借鉴文档地址https://github.com/laravel/passport/issues/71#issuecomment-330506407
/**
* @mixin User
*/
trait PassportTrait
{
public function createBearerToken($clientId, $scopes = [])
{
$passportToken = $this->createPassportToken($clientId, $scopes);
$bearerToken = $this->sendBearerTokenResponse($passportToken['access_token'], $passportToken['refresh_token']);
return json_decode($bearerToken->getBody()->__toString(), true);
}
protected function createPassportToken($clientId, $scopes = [])
{
//格式化scopes
$scopes = $this->genScopes($scopes);
$client = \Laravel\Passport\Client::find($clientId);
$accessClient = new Client($client->id, $client->name, $client->redirect);
$accessToken = new AccessToken($this->id, $scopes, $accessClient);
$accessToken->setIdentifier($this->generateUniqueIdentifier());
$accessToken->setClient($accessClient);
$accessToken->setExpiryDateTime((new \DateTimeImmutable)->add(Passport::tokensExpireIn()));
$accessTokenRepository = new AccessTokenRepository(new TokenRepository(), app(Dispatcher::class));
//触发 AccessTokenCreated 事件
$accessTokenRepository->persistNewAccessToken($accessToken);
$refreshToken = $this->issueRefreshToken($accessToken);
return [
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
];
}
protected function sendBearerTokenResponse($accessToken, $refreshToken)
{
$response = new BearerTokenResponse();
$response->setAccessToken($accessToken);
$response->setRefreshToken($refreshToken);
$privateKey = new CryptKey(file_get_contents(Passport::keyPath('oauth-private.key')));
$accessToken->setPrivateKey($privateKey);
$response->setPrivateKey($privateKey);
$response->setEncryptionKey(app('encrypter')->getKey());
return $response->generateHttpResponse(new Response);
}
private function generateUniqueIdentifier($length = 40)
{
try {
return bin2hex(random_bytes($length));
} catch (\TypeError | \Error $e) {
throw OAuthServerException::serverError('An unexpected error has occurred');
} catch (\Exception $e) {
// If you get this message, the CSPRNG failed hard.
throw OAuthServerException::serverError('Could not generate a random string');
}
// @codeCoverageIgnoreEnd
}
private function issueRefreshToken(AccessTokenEntityInterface $accessToken)
{
$maxGenerationAttempts = 10;
$refreshTokenRepository = app(RefreshTokenRepository::class);
$refreshToken = $refreshTokenRepository->getNewRefreshToken();
$refreshToken->setExpiryDateTime((new \DateTimeImmutable)->add(Passport::refreshTokensExpireIn()));
$refreshToken->setAccessToken($accessToken);
while ($maxGenerationAttempts-- > 0) {
$refreshToken->setIdentifier($this->generateUniqueIdentifier());
try {
$refreshTokenRepository->persistNewRefreshToken($refreshToken);
return $refreshToken;
} catch (UniqueTokenIdentifierConstraintViolationException $e) {
if ($maxGenerationAttempts === 0) {
throw $e;
}
}
}
}
private function genScopes($scopes)
{
foreach ($scopes as $key => $scope) {
if (is_string($scope)) {
$scopes[$key] = new Scope($scope);
}
}
return $scopes;
}
}
用户模型引用trait
<?php
namespace App\Models;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, HasRoles, PassportTrait;
}
生成token
$user = User::find(1);
$token = $user->createBearerToken(2, ['*']);
token结果为
[
"token_type" => "Bearer",
"expires_in" => 7199,
"access_token" => "eyJ0eXAiOiJKV......",
"refresh_token" => "def5020056704......"
]