Laravel passport grant password 401 Unauthorized

Laravel使用Laravel Passport轻松实现API身份验证,Passport建立在由Andy Millington和Simon Hamp维护的League OAuth2服务器之上。

关于passport的具体使用方法,可以参考Laravel官方文档,这里记录当使用grant_type为password时遇到401 Unauthorized的一种解决方法。

首先,重现案发现场:

//请求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);

//返回结果
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安装完成并已成功执行migratepassport:install,这时,可以在数据库中查看oauth_clients表,表中默认有一个password客户端,当然,也可以通过passport:client --password来创建一个password客户端,password客户端在表中的password_client字段的值为1。

2、其次,确定在请求token的post请求中正确填写了参数值,尤其是client_id client_secret,另外,scope尽量不为空,可设置为*,通常,client_idclient_secret填写错误或不是对应的password客户端id的情况较多,另外scope为空也可能出现异常,关于scope只是别人反应的情况,自己未遇到所以并未细寻。

3、通过post参数我们也会发现,password grant会用到username和password两个值,而这里也是passport在校验用户名和密码是否匹配,所以,我们需要保证passport能够正确取到用户并验证其密码。我出问题的地方也是在这里。

passport校验username和password部分过程,可直接跳过。

//在源码中也可以看到,passwordGrant授权中会有这么一步:
//from vendor/league/oauth2-server/src/Grant/PasswordGrant.php -> function respondToAccessTokenRequest
$user = $this->validateUser($request, $client);
//在这一步会用到我们post参数中的username和password的值来验证用户名密码是否正确。
//在往后找,会看到具体的验证过程
//from vendor/laravel/passport/src/Bridge/UserRepository.php -> function getUserEntityByUserCredentials
//首先,要正确配置config/auth配置文件中的provider,把model配置成自己实际使用的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通过username查找指定用户的的地方,可以看到,passport默认使用email字段当做用户账号来寻找用户,如果我们不是使用的email,需要添加findForPassport方法来自定义账号。
if (method_exists($model, 'findForPassport')) {
    $user = (new $model)->findForPassport($username);
} else {
    $user = (new $model)->where('email', $username)->first();
}
if (! $user) {
    return;
//这里是passport验证用户密码的地方,默认是通过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());

解决方法:

//在自己实际使用的userModel中
//默认主键字段为id
use Authenticatable,HasApiTokens;
//如果自己使用的账号字段是email则无需添加此方法,否则添加此方法并手动返回用户模型。
public function findForPassport($username) {
    return $this->where('your username column name', $username)->first();
}
//如果你的密码使用的是hash算法并且post传递的是原始密码(即没有手动hash)则无需添加此方法,否则需自行验证密码。
public function validateForPassportPasswordGrant($password) {
    //你自己的密码验证逻辑
    return true or false;
}

到此为止,关于username和password引起的Unauthorized问题就基本解决了,能够正常获取token。

你可能感兴趣的:(Laravel passport grant password 401 Unauthorized)