
本教程将指导您如何在laravel应用中验证来自外部身份提供商的rs256签名jwt。我们将利用tymondesigns/jwt-auth包,通过配置jwks获取的公钥、实现自定义认证守卫(guard)来处理令牌解析与签名验证,并将其无缝集成到laravel的认证体系中,从而确保api请求的安全性。
在现代微服务架构和单页应用(SPA)中,使用外部身份提供商(IdP)进行用户认证已成为常见模式。用户通过IdP认证后会获得一个Access Token(通常是JWT),后端API服务需要验证这个JWT以授权访问。本教程将详细介绍如何在Laravel应用中,利用tymondesigns/jwt-auth包以及自定义认证守卫,高效且安全地验证来自第三方IdP的RS256签名的JWT。
验证RS256签名的JWT需要对应的公钥。外部身份提供商通常会通过一个JWKS(JSON Web Key Set)URL(例如 https://{domain}/.well-known/jwks.json)公开其公钥。您需要从这个JWKS URL获取相应的公钥,并将其保存为PEM格式的文件。
步骤:
从JWKS URL获取公钥: 访问您的IdP提供的JWKS URL。通常,JWKS是一个JSON数组,包含一个或多个JSON Web Key (JWK) 对象。您需要根据JWT头部的kid(Key ID)或其他标识符找到匹配的公钥。将JWK中的n(modulus)和e(exponent)字段转换为PEM格式的RSA公钥。市面上有一些工具或库可以帮助您完成这个转换,例如PHP的phpseclib或在线JWK转PEM工具。
保存公钥文件: 将转换后的PEM格式公钥保存到Laravel项目的安全位置,例如 storage/jwt/public.pem。
配置tymondesigns/jwt-auth: 修改config/jwt.php配置文件,指定公钥文件的路径和签名算法。
// config/jwt.php
return [
// ...
'keys' => [
'public' => 'file://' . storage_path('jwt/public.pem'),
'private' => null, // 如果只验证,不需要私钥
'passphrase' => null,
],
'algo' => 'RS256', // 确保与您的IdP使用的算法一致
// ...
];请确保storage/jwt目录存在且可读。
Laravel的认证系统允许您定义自定义的认证守卫。我们将创建一个JWTGuard来封装JWT的验证逻辑,包括解析令牌、检查签名和提取用户信息。
创建守卫文件: 在app/Guard目录下创建JWTGuard.php文件(如果目录不存在,请先创建)。
<?php
namespace App\Guard;
use App\Models\User; // 您自定义的用户模型
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Tymon\JWTAuth\JWT;
class JWTGuard implements Guard
{
use GuardHelpers;
/**
* @var JWT $jwt
*/
protected JWT $jwt;
/**
* @var Request $request
*/
protected Request $request;
/**
* JWTGuard constructor.
* @param JWT $jwt
* @param Request $request
*/
public function __construct(JWT $jwt, Request $request)
{
$this->jwt = $jwt;
$this->request = $request;
}
/**
* 获取当前认证用户。
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if (!is_null($this->user)) {
return $this->user;
}
// 尝试从请求中获取JWT,并检查其有效性
if ($this->jwt->setRequest($this->request)->getToken() && $this->jwt->check()) {
// 从JWT payload中获取用户标识(通常是'sub' claim)
$id = $this->jwt->payload()->get('sub');
// 根据IdP返回的用户信息创建或查找本地用户实例
// 这里我们创建一个简单的User实例,您可以根据需要从数据库查找或设置更多自定义属性
$this->user = new User();
$this->user->id = $id;
// 如果需要,可以从JWT payload中提取更多自定义声明并设置到用户模型
// $this->user->email = $this->jwt->payload()->get('email');
return $this->user;
}
return null;
}
/**
* 验证用户凭据(在本场景中不使用,因为我们通过JWT验证)。
*
* @param array $credentials
* @return bool
*/
public function validate(array $credentials = [])
{
// 对于JWT验证,此方法通常留空或返回false
return false;
}
}自定义用户模型: 为了让JWTGuard能够返回一个Authenticatable的用户实例,您可能需要创建一个简单的User模型,或者确保您现有的User模型实现了Illuminate\Contracts\Auth\Authenticatable接口。
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Database\Eloquent\Model;
class User extends Model implements AuthenticatableContract
{
// 这里可以添加您需要的自定义属性和方法
// 例如,如果JWT中包含用户角色或权限信息,可以在这里处理
/**
* Get the name of the unique identifier for the user.
*
* @return string
*/
public function getAuthIdentifierName()
{
return 'id'; // 假设您的用户模型使用'id'作为主键
}
/**
* Get the unique identifier for the user.
*
* @return mixed
*/
public function getAuthIdentifier()
{
return $this->id;
}
/**
* Get the password for the user.
*
* @return string
*/
public function getAuthPassword()
{
return ''; // 对于JWT认证,密码通常不直接存储或使用
}
/**
* Get the "remember me" token value.
*
* @return string
*/
public function getRememberToken()
{
return null; // 对于无状态JWT认证,不需要remember token
}
/**
* Set the "remember me" token value.
*
* @param string $value
* @return void
*/
public function setRememberToken($value)
{
//
}
/**
* Get the column name for the "remember me" token.
*
* @return string
*/
public function getRememberTokenName()
{
return '';
}
}完成JWTGuard的创建后,需要将其注册到Laravel的认证系统中,并在config/auth.php中配置使用。
在AuthServiceProvider中注册: 修改app/Providers/AuthServiceProvider.php的boot方法。
<?php
namespace App\Providers;
use App\Guard\JWTGuard; // 引入您的自定义守卫
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
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();
// 扩展Laravel的认证系统,注册自定义的'jwt-auth'守卫
$this->app['auth']->extend(
'jwt-auth', // 守卫的驱动名称
function ($app, $name, array $config) {
$guard = new JWTGuard(
$app['tymon.jwt'], // 获取tymon.jwt实例
$app['request'] // 获取当前请求实例
);
// 确保每次请求时,守卫都能获取到最新的请求实例
$app->refresh('request', $guard, 'setRequest');
return $guard;
}
);
}
}在config/auth.php中配置: 将新的jwt守卫添加到guards配置项中。
<?php
// config/auth.php
return [
'defaults' => [
'guard' => 'web', // 默认守卫,可以根据需要调整
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
// ... 其他守卫
'jwt' => [ // 定义您的JWT守卫
'driver' => 'jwt-auth', // 对应AuthServiceProvider中extend的驱动名称
'provider' => 'users' // 可以是任何有效的provider,这里我们使用默认的users
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// ...
],
// ...
];现在,您可以通过Laravel的路由中间件来保护您的API端点,确保只有携带有效JWT的请求才能访问。
<?php
// routes/api.php 或其他路由文件
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Auth;
Route::middleware('auth:jwt')->get('/user', function () {
// 只有携带有效JWT并成功通过'jwt'守卫认证的请求才能到达这里
return Auth::user(); // 返回当前认证用户实例
});
// 您也可以在控制器构造函数中使用
// public function __construct()
// {
// $this->middleware('auth:jwt');
// }当请求到达/user路由时,auth:jwt中间件会激活JWTGuard。如果请求头中包含有效的Bearer Token,JWTGuard将对其进行解析、签名验证,并根据sub声明设置认证用户。
通过以上步骤,您已经成功地在Laravel应用中构建了一个健壮的机制,用于验证来自外部身份提供商的RS256签名的JWT。这不仅增强了API的安全性,也使其能够与复杂的身份认证生态系统无缝集成。
以上就是在Laravel中验证第三方JWT(RS256 & JWKS)的教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号