Laravel授权
大约 6 分钟
Passport 多表认证
自定义验证
// UserModel
/**
* 自定义username验证字段
*/
public function findForPassport($username)
{
return $this->where("phone", $username)->first();
}
/**
* 通过Passport的密码授权验证用户使用的密码
*
* @param string $password
* @return bool
*/
public function validateForPassportPasswordGrant($password)
{
return $password == $this->password ? true : false;
// return Hash::check($password, $this->password);
}
public function getUserByPhone($phone)
{
return $this->where("phone", $phone)->first();
}
1、创建授权令牌
2、路由设置: app/Providers/RouteServiceProvider.php
Route::prefix('api')
->middleware([
'web',
"client:admin,admin", // scopes授权名称,作用域
"auth:admin", // 认证的grud
"checkUserAuth",
"scope:admin"
])
->namespace($this->namespace)
->group(base_path('routes/api.php'));
授权方法
<?php
/**
* Created by OrangBus
* User email: orangbus40400@gmail.com
* website: orangbus.cn
* blog: doc.orangbus.cn
* github: github.com/orangbus
*/
namespace App\Traits;
use App\Enum\ArgumentEnum;
use App\Exceptions\ArgumentEmptyException;
use App\Exceptions\BusinessException;
use Illuminate\Http\Client\HttpClientException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
trait PassportSecret
{
/**
* 获取client_id
* @param string $provider
* @return mixed
*/
private function getClientId($provider = 'user')
{
$list = [
"user" => config("app.CLIENT_ID"),
"admin" => config("app.ADMIN_CLIENT_ID"),
"platform" => config("app.PLATFORM_CLIENT_ID"),
"partner" => config("app.PARTNER_CLIENT_ID"),
];
return $list[$provider];
}
public function getClientSecret($provider = 'user')
{
$list = [
"user" => config("app.CLIENT_SECRET"),
"admin" => config("app.ADMIN_CLIENT_SECRET"),
"platform" => config("app.PLATFORM_CLIENT_SECRET"),
"partner" => config("app.PARTNER_CLIENT_SECRET"),
];
return $list[$provider];
}
/**
* 获取token
* @param $user
* @param string $provider admin|platform
* @param string $scope
* @return mixed
*/
private function getToken($user, $provider = "user", $scope = "*")
{
$response = Http::withOptions(["verify" => false])->asForm()->post($this->getTokenUrl(), [
'grant_type' => 'password',
'username' => $user->id,
'password' => $user->password,
'client_id' => $this->getClientId($provider),
'client_secret' => $this->getClientSecret($provider),
'scope' => $scope,
'provider' => $provider, //设置provider
]);
$status = $response->status();
$body = json_decode($response->body(), true);
if ($status !== 200) {
// error,error_description,message
throw new BusinessException($body['error'] ?? "授权服务器错误", $status);
}
return $body;
}
/**
* @param $refreshToken
* @param string $scope
* @return mixed
*/
public function refresh($refreshToken, $provider = "*", $scope = "*")
{
$response = Http::withOptions(["verify" => false])->asForm()->post($this->getTokenUrl(), [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $this->getClientId($provider),
'client_secret' => $this->getClientSecret($provider),
'scope' => $scope,
'provider' => $provider
]);
$status = $response->status();
$body = json_decode($response->body(), true);
if ($status !== 200) {
throw new BusinessException($body['error_description'] ?? '服务器错误', $status);
}
return $body;
}
/**
* 获取认证用户的所有客户端
*/
public function getAuthClients()
{
$response = Http::withOptions(["verify" => false])->asForm()->get("/oauth/clients");
$status = $response->status();
if ($status !== 200) {
throw new BusinessException("请求错误", $status);
}
return json_decode($response->body(), true);
}
/**
* 获取 token 请求地址
*/
private function getTokenUrl(): string
{
return config("app.url") . "/oauth/token";
}
/**
* 获取刷新 token 请求地址
*/
private function getRefreshTokenUrl()
{
return config("app.url") . "/oauth/refresh_token";
}
}
获取token
$token = $this->getToken($user, "platform", 'platform');
撤销授权
$this->request->user()->token()->revoke();
配置中间件
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// passport,必须放在最后
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
// passport 主要配置
'client' => CheckClientCredentials::class,
"passportProvider" => PassportProvider::class,
// 作用于检查
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
// 检查用户信息
"checkUserAuth" => CheckUserAuth::class,
// 平台登录鉴权
"PlatFormLogin" => PlatFormLogin::class,
// 商户登录鉴权路由
"MerchantLogin" => MerchantLogin::class,
// 商户路由检查
"checkRouteAuth" => CheckRouteAuth::class,
];
切换授权中间件app/Http/Middleware/CheckUserAuth.php
<?php
namespace App\Http\Middleware;
use App\Enum\GlobalEnum;
use App\Events\LogUserErrorInfo;
use App\Exceptions\BusinessException;
use App\Exceptions\DangerErrorExcetion;
use Closure;
use Illuminate\Http\Request;
class CheckUserAuth
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$authorization = $request->header("authorization");
if (empty($authorization)) {
return abort(GlobalEnum::AuthorizationErrorCode, GlobalEnum::AuthorizationErrorMessage);
}
// 用户身份验证设置
$user = $request->user();
$provider = $user->provider;
if (is_null($provider) || empty($provider) || $user->mer_id <= 0) {
// 记录错误用户信息
event(new LogUserErrorInfo("用户类型错误或者mer_id为0异常", $request->user(), GlobalEnum::UserInfoError_TYPE));
throw new DangerErrorExcetion(GlobalEnum::UserInfoErrorMessage, GlobalEnum::UserInfoErrorCode);
}
// 主要是这个
\Config::set('auth.guards.api.provider', $provider);
return $next($request);
}
}
撤销登录
public static function makeMerchantLogout($mer_id)
{
if (empty($mer_id)) return true;
// 获取商户用户
$adminIds = Admin::where("mer_id", $mer_id)->pluck("id")->toArray();
return DB::table("oauth_access_tokens")
->where("client_id", env("ADMIN_CLIENT_ID"))
->whereIn("user_id", $adminIds)
->delete();
}
授权:app/Providers/AuthServiceProvider.php
<?php
namespace App\Providers;
use App\Enum\GlobalEnum;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
use Laravel\Passport\RouteRegistrar;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies(); // 授权域
//passport 默认只会认证 config('auth.guards.api.provider') 指定的模型 想要多表认证 需要动态改变认证模型
Passport::routes(function (RouteRegistrar $router) {
// 可以选择注册哪些passport路由,此处只注册管理 Token 的路由
$router->forAccessTokens();
}, ['middleware' => ['checkUserAuth']]); //'passportProvider',
注册 passport 路由
Passport::routes();
// passport 秘钥加密
Passport::hashClientSecrets();
// token 15天时效
Passport::tokensExpireIn(now()->addDays(GlobalEnum::TokenExpireTime));
// 刷新token时效时间 30天
Passport::refreshTokensExpireIn(now()->addDays(GlobalEnum::RefreshTokenExpireTime));
// 个人访问 token 时效时间6天
Passport::personalAccessTokensExpireIn(now()->addMonths(GlobalEnum::PersonalAccessTokenExpireTime));
// 令牌的作用于,防止出现窜表
Passport::tokensCan([
"user" => "用户表",
"admin" => "商户用户表",
"platform" => "平台用户表",
]);
}
}
配置文件
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'admin' => [
'driver' => 'passport',
'provider' => 'admin',
],
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];
sanctum多角色(多表)认证
在项目中存在商户和客户两种身份,使用 sanctum 做的 api 认证,
但是问题是 Merchant model
生成的 token 不能通过 auth:merchant
中间件的验证,反而是 Customer model
生成的 token 可以通过 auth:merchant
中间件的验证。请问这块是否有对应的配置能让 token 和对应的验证项对应起来。
配置和代码如下:config/auth.php
'guards' => [
'customer' => [
'driver' => 'sanctum',
'provider' => 'customers'
],
'merchant' => [
'driver' => 'sanctum',
'provider' => 'merchants'
]
],
'providers' => [
'customers' => [
'driver' => 'eloquent',
'model' => App\Models\Customer::class,
],
'merchants' => [
'driver' => 'eloquent',
'model' => App\Models\Merchant::class
]
],
Model
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Merchant extends Authenticatable
{
use HasFactory, SoftDeletes, HasApiTokens;
}
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Customer extends Authenticatable
{
use HasFactory, SoftDeletes, HasApiTokens;
}
merchant.route.php
Route::post('login', [LoginController::class, 'login']);
Route::middleware(['auth:merchant'])->group(function (){
Route::get('test', [ProductController::class, 'test']);
});
customer.route.php
Route::post('login', [LoginController::class, 'login']);
Route::middleware(['auth:customer'])->group(function (){
Route::get('test1', [ProductController::class, 'test1']);
});
MerchantController
public function login(Request $request)
{
$merchant = Merchant::query()->find(1);
$token = $merchant->createToken($merchant->name);
return $this->ok([
'token' => $token->plainTextToken
]);
}
CustomerController
public function login(Request $request)
{
$customer = Customer::query()->find(1);
$token = $customer->createToken($merchant->name);
return $this->ok([
'token' => $token->plainTextToken
]);
}
动作授权: AuthServiceProvider.php
$this->registerPolicies();
// 接口授权
Gate::define("user",function ($user){
return $user->provider === "user";
});
// 后台授权
Gate::define("admin",function ($user){
return $user->provider === "admin";
});