Laravel passport grant password 401 Unauthorized

Laravel使用Laravel Passport輕松實(shí)現(xiàn)API身份驗(yàn)證,Passport建立在由Andy Millington和Simon Hamp維護(hù)的League OAuth2服務(wù)器之上。

關(guān)于passport的具體使用方法,可以參考Laravel官方文檔,這里記錄當(dāng)使用grant_type為password時(shí)遇到401 Unauthorized的一種解決方法。

首先,重現(xiàn)案發(fā)現(xiàn)場(chǎng):

//請(qǐng)求token
$http = new \GuzzleHttp\Client();
$response = $http->post('http://127.0.0.1/oauth/token', [ 'form_params' => [
     'grant_type' => 'password',
     'client_id' => '3',
     'client_secret' => 'rYu6KUBJYKs7jDpG0Bl0hXV791hM89CZa7MSwzSE',
     'username' => 'my account',
     'password' => 'my password',
     'scope' => '*'
], ]);
$tokenData = json_decode((string)$response->getBody(), true);

//返回結(jié)果
Client error: `POST http://127.0.0.1/oauth/token` resulted in a `401 Unauthorized` response:
{"error":"invalid_credentials","message":"The user credentials were incorrect."}

我的解決方法:

1、首先,確定passport安裝完成并已成功執(zhí)行migratepassport:install,這時(shí),可以在數(shù)據(jù)庫(kù)中查看oauth_clients表,表中默認(rèn)有一個(gè)password客戶端,當(dāng)然,也可以通過(guò)passport:client --password來(lái)創(chuàng)建一個(gè)password客戶端,password客戶端在表中的password_client字段的值為1。

2、其次,確定在請(qǐng)求token的post請(qǐng)求中正確填寫(xiě)了參數(shù)值,尤其是client_id <oauth_clients表的主鍵id>client_secret,另外,scope盡量不為空,可設(shè)置為*,通常,client_idclient_secret填寫(xiě)錯(cuò)誤或不是對(duì)應(yīng)的password客戶端id的情況較多,另外scope為空也可能出現(xiàn)異常,關(guān)于scope只是別人反應(yīng)的情況,自己未遇到所以并未細(xì)尋。

3、通過(guò)post參數(shù)我們也會(huì)發(fā)現(xiàn),password grant會(huì)用到username和password兩個(gè)值,而這里也是passport在校驗(yàn)用戶名和密碼是否匹配,所以,我們需要保證passport能夠正確取到用戶并驗(yàn)證其密碼。我出問(wèn)題的地方也是在這里。

passport校驗(yàn)username和password部分過(guò)程,可直接跳過(guò)。

//在源碼中也可以看到,passwordGrant授權(quán)中會(huì)有這么一步:
//from vendor/league/oauth2-server/src/Grant/PasswordGrant.php -> function respondToAccessTokenRequest
$user = $this->validateUser($request, $client);
//在這一步會(huì)用到我們post參數(shù)中的username和password的值來(lái)驗(yàn)證用戶名密碼是否正確。
//在往后找,會(huì)看到具體的驗(yàn)證過(guò)程
//from vendor/laravel/passport/src/Bridge/UserRepository.php -> function getUserEntityByUserCredentials
//首先,要正確配置config/auth配置文件中的provider,把model配置成自己實(shí)際使用的userModel
$provider = config('auth.guards.api.provider');
if (is_null($model = config('auth.providers.'.$provider.'.model'))) {
    throw new RuntimeException('Unable to determine authentication model from configuration.');
}
//這里是passport通過(guò)username查找指定用戶的的地方,可以看到,passport默認(rèn)使用email字段當(dāng)做用戶賬號(hào)來(lái)尋找用戶,如果我們不是使用的email,需要添加findForPassport方法來(lái)自定義賬號(hào)。
if (method_exists($model, 'findForPassport')) {
    $user = (new $model)->findForPassport($username);
} else {
    $user = (new $model)->where('email', $username)->first();
}
if (! $user) {
    return;
//這里是passport驗(yàn)證用戶密碼的地方,默認(rèn)是通過(guò)hash check方法,如果自己使用的不是hash算法生成密碼,則需要添加validateForPassportPasswordGrant方法。
} elseif (method_exists($user, 'validateForPassportPasswordGrant')) {
    if (! $user->validateForPassportPasswordGrant($password)) {
        return;
    }
} elseif (! $this->hasher->check($password, $user->getAuthPassword())) {
    return;
}
//獲取用戶主鍵id
return new User($user->getAuthIdentifier());

解決方法:

//在自己實(shí)際使用的userModel中
//默認(rèn)主鍵字段為id
use Authenticatable,HasApiTokens;
//如果自己使用的賬號(hào)字段是email則無(wú)需添加此方法,否則添加此方法并手動(dòng)返回用戶模型。
public function findForPassport($username) {
    return $this->where('your username column name', $username)->first();
}
//如果你的密碼使用的是hash算法并且post傳遞的是原始密碼(即沒(méi)有手動(dòng)hash)則無(wú)需添加此方法,否則需自行驗(yàn)證密碼。
public function validateForPassportPasswordGrant($password) {
    //你自己的密碼驗(yàn)證邏輯
    return true or false;
}

到此為止,關(guān)于username和password引起的Unauthorized問(wèn)題就基本解決了,能夠正常獲取token。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容