JWT 是 JSON Web Token 的缩写,它是一个规范,让用户和服务器之间传递安全可靠的信息。
JWT介绍
JWT 由头部(header)、载荷(payload)与签名(signature)组成
{
“typ”:“JWT”,
“alg”:“HS256”
}
{
“iss”: “http://package.test”,
“iat”: 1673495574,
“exp”: 1673499174,
“nbf”: 1673495574,
“jti”: “uuid”,
“sub”: 1,
“prv”: “87e0af1ef9fd15812fdec97153a14e0b047546aa”
}
signature
- 头部申明了加密算法;
- 载荷中中记录了一些关键数据:
iss: 签发者,也就是 package.test ;
iat 签发时间;
exp 过期时间;
nbf 在这个时间之前,该 JWT 都是不可用的,一般同签发时间 iat;
jti 唯一标识符,防止重放攻击。
sub 用户标识,这里是用户 ID
prv 扩展包自定义字段,模型名的哈希值,等于sha1(‘App\User’),用于区别不同的模型
- signature 是由服务器进行的签名,保证了 token 不被篡改
JWT 最后是通过 Base64 编码的,也就是说,它可以被翻译回原来的样子来的。所以不要在 JWT 中存放一些敏感信息。
3.signature 是由服务器进行的签名,保证了 token 不被篡改
这里使用的是tymon/jwt-auth,在 Laravel 中安装 tymon/jwt-auth 这个扩展包就可以很方便的使用 JWT 了
安装
安装tymon/jwt-auth
composer require tymon/jwt-auth
发布配置
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
生成secret
php artisan jwt:secret
这个命令会在 env 中增加一个 JWT_SECRET,secret 是十分重要的,用于给 Token 签名,更换这个 secret
会导致之前生成的所有 Token 无效,所以不要随意的更换这个secret
接入
创建 Token
需要修改一下 User 模型,在该模型中,需要实现扩展包提供的接口 Tymon\JWTAuth\Contracts\JWTSubject
实现两个方法
getJWTIdentifier 返回模型的 id
getJWTCustomClaims 存放自定义的数据用于放在 Token 中
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}
结合 Laravel Auth
修改一下配置:config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt', // 原来是 token 改成jwt
'provider' => 'users',
],
],
用到的接口
对于 API 来说一般需要以下几个接口:
login 用户登录,获取 JWT;
refresh 刷新 JWT;
logout 退出登录,注销 JWT;
user 获取当前 JWT 对应的用户。
以下调用需配置路由
在routes/api.php配置路由
//获取token
Route::post('/user/token','UserController@token')->name('user.token');
//刷新token
Route::post('/user/refresh','UserController@refresh')->name('user.refresh');
//退出登录
Route::post('/user/logout','UserController@logout')->name('user.logout');
//获取当前用户
Route::post('/user/user','UserController@user')->name('user.user');
值得注意的是,由于我在app/Http/Controllers里面新建了一个Api目录,所有的API控制器都放在里面,所以需要去app/Providers/RouteServiceProvider.php的mapApiRoutes修改一个默认命名空间,找到mapApiRoutes函数
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace,'\Api')//这里指明了命名空间
->group(base_path('routes/api.php'));
}
1.用户登录也就是获取token
/**
* 获取token
* @param $request
* @return JsonResponse
*/
public function getToken($request): JsonResponse
{
//使用email和密码验证
$credentials = $request->only('email', 'password');
if (!$token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Authentication failed!Please confirm the account password'], 401);
}
return $this->respondWithToken(['token' => $token]);
}
使用的统一规范响应
/**
* 获取token统一响应格式
* @param $token
* @return JsonResponse
*/
protected function respondWithToken($token): JsonResponse
{
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60
]);
}
调用一下api,成功了
2.refresh刷新token
/**
* 刷新token
* @return JsonResponse
*/
public function refreshToken(): JsonResponse
{
return $this->respondWithToken(auth('api')->refresh());
}
调用成功
引用
任何一个永久有效的 token 都是相当危险的,通过任意方式泄露了 token 之后,
用户的相关信息都有可能被利用。所以为了安全考虑,任何一种令牌的机制,都会有过期时间,
那么 token 过期以后,难道要用户重新登录吗?
像 OAuth 2.0 有 refresh_token 可以用来刷新一个过期的 access_token,jwt-auth 同样也为我们提供了刷新的机制,
只要在可刷新的时间范围内,即使 JWT 过期了,依然可以调用接口,换取一个新的 JWT。这对于客户端长期保持用户登录状态是十分重要的。
在生成的jwt配置中config/jwt.php,我们需要特别了解以下两个配置
3.logout 退出登录(注销 JWT)
- jwt.ttl (JWT_TTL) 多长时间以后 JWT 就过期了 (单位分钟);
- jwt.refresh_ttl (JWT_REFRESH_TTL) 多长时间以内, JWT 可以再次被刷新(单位分钟)。
一般情况下 refresh_ttl 应该大于 ttl,也就是 JWT 过期以后,依然可以刷新一个新的 JWT。
用户退出登录的时候,是需要将当前这个 JWT 注销的,但是 JWT 本身不用存储在服务端,因为本身已经包含了足够的信息以及签名,那如何来完成注销呢?其实是利用了黑名单,删除只是将 JWT 加入黑名单(Laravel 缓存)而已,加入黑名单的 JWT 都是无法继续使用的。
调用成功
4.user 获取登录用户
public function user(): JsonResponse
{
$user = auth('api')->user();
return response()->json($user);
}
调用