在JavaScript前端技术大行其道的今天,我们通常只需在后台构建API提供给前端调用,并且后端仅仅设计为给前端移动App调用。用户认证是Web应用的重要组成部分,基于API的用户认证有两个最佳解决方案 —— OAuth 2.0 和 JWT(JSON Web Token)。
JWT(JSON Web Token)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
我们先将用户认证的操作描述成一个JSON对象。其中添加了一些其他的信息,帮助今后收到这个JWT的服务器理解这个JWT。
{"sub": "1","iss": "localhost:8000/auth/login","iat": 1451888119,"exp": 1454516119,"nbf": 1451888119,"jti": "37c107e4609ddbcc9c096ea5ee76c667"
}
这里面的前6个字段都是由JWT的标准所定义的。
将上面的JSON对象进行base64编码可以得到下面的字符串:
eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4ODExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ
这个字符串我们将它称作JWT的Payload(载荷)。
如果你使用Node.js,可以用Node.js的包base64url来得到这个字符串:
var base64url = require('base64url')var header = {"from_user": "B","target_user": "A"
}
console.log(base64url(JSON.stringify(header)))
注:Base64是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。
JWT还需要一个头部,头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象:
{"typ": "JWT","alg": "HS256"
}
在这里,我们说明了这是一个JWT,并且我们所用的签名算法(后面会提到)是HS256算法。
对它也要进行Base64编码,之后的字符串就成了JWT的Header(头部):
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
将上面的两个编码后的字符串都用句号.连接在一起(头部在前),就形成了:
最后,我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret):
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret
)
这样就可以得到我们加密后的内容:
wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4
这一部分又叫做签名。
最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWT:
JWT 是一个令牌(Token),客户端得到这个服务器返回的令牌后,可以将其存储到 Cookie 或 localStorage 中,此后,每次与服务器通信都要带上这个令牌,你可以把它放到 Cookie 中自动发送,但这样做不能跨域,所以更好的做法是将其放到 HTTP 请求头 Authorization 字段里面:
Authorization: Bearer
服务端收到这个 JWT 令牌后,就可以根据令牌值认定用户身份。
composer require tymon/jwt-auth
安装完成后可以在项目根目录中的 composer.json 中看到如下内容
..."require": {"php": "^7.1.3","fideloper/proxy": "^4.0","laravel/framework": "5.8.*","laravel/tinker": "^1.0","tymon/jwt-auth": "^1.0" /* 刚下载的依赖*/},
...
本文环境使用的是laravel 5.8 无需注册服务提供者,Laravel 5.4 及以下版本的用户需要手动配置一下
在项目的控制台中输入
php artisan vendor:publish
然后根据提示回应对应的配置项编号即可。
成功之后可以看到项目目录/config/jwt.php
的这个配置文件,你可以配置以下选项:
通过如下命令
php artisan jwt:secret
成功后可以在.env
中看到如下字段
JWT_SECRET=Wkgeflos6dLXPzt1f6fSB9qI4dmMpNgpoKezIsHxpzzKLSZjlXHSIvoKSsyCpL9H
首先,模型类需要在 User
模型实现TymonJWTAuthContractsJWTSubject
接口和他的 2 个方法getJWTIdentifier()
和getJWTCustomClaims()
.
下面的示例应该让您了解它的外观。显然,您应该根据自己的需要进行任何更改。
<?phpnamespace App;use TymonJWTAuthContractsJWTSubject;
use IlluminateNotificationsNotifiable;
use IlluminateFoundationAuthUser as Authenticatable;class User extends Authenticatable implements JWTSubject
{use Notifiable;// Rest omitted for brevity/*** Get the identifier that will be stored in the subject claim of the JWT.** @return mixed*/public function getJWTIdentifier(){return $this->getKey();}/*** Return a key value array, containing any custom claims to be added to the JWT.** @return array*/public function getJWTCustomClaims(){return [];}
}
以下是根据上面的模板构建我的模型类
<?phpnamespace AppModels;use IlluminateDatabaseEloquentModel;
use IlluminateNotificationsNotifiable;
use IlluminateFoundationAuthUser as Authenticatable;
use TymonJWTAuthContractsJWTSubject;class UserModel extends Authenticatable implements JWTSubject
{protected $table = 't_users';protected $fillable = ['username','password'];public $timestamps = false;use Notifiable;// Rest omitted for brevity/*** Get the identifier that will be stored in the subject claim of the JWT.** @return mixed*/public function getJWTIdentifier(){return $this->getKey();}/*** Return a key value array, containing any custom claims to be added to the JWT.** @return array*/public function getJWTCustomClaims(){return [];}
}
在config/auth.php
文件中,您需要进行一些更改以配置 Laravel 以使用jwt
防护来支持您的应用程序身份验证。
对文件进行以下更改:
'defaults' => ['guard' => 'api','passwords' => 'users',
],...'guards' => ['api' => ['driver' => 'jwt','provider' => 'users',],
],...
// 由于我没使用系统自带的User类,所以下侧也要更改
'providers' => ['users' => ['driver' => 'eloquent',// 更改为我定义的模型的位置'model' => AppModelsUserModel::class,],// 'users' => [// 'driver' => 'database',// 'table' => 'users',// ],],
在routes/api.php
中配置一些带有权限认证的路由
Route::group(['middleware' => 'api','prefix' => 'auth'], function ($router) {Route::post('login', 'AuthController@login');Route::post('logout', 'AuthController@logout');Route::post('refresh', 'AuthController@refresh');Route::post('me', 'AuthController@me');});
通过手动或者使用artisan
工具命令时创建:
php artisan make:controller AuthController
然后添加如下内容
<?phpnamespace AppHttpControllers;use IlluminateSupportFacadesAuth;
use AppHttpControllersController;class AuthController extends Controller
{/*** Create a new AuthController instance.** @return void*/public function __construct(){$this->middleware('auth:api', ['except' => ['login']]);}/*** Get a JWT via given credentials.** @return IlluminateHttpJsonResponse*/public function login(){// 这里修改需要验证的字段$credentials = request(['email', 'password']);if (! $token = auth()->attempt($credentials)) {return response()->json(['error' => 'Unauthorized'], 401);}return $this->respondWithToken($token);}/*** Get the authenticated User.** @return IlluminateHttpJsonResponse*/public function me(){return response()->json(auth()->user());}/*** Log the user out (Invalidate the token).** @return IlluminateHttpJsonResponse*/public function logout(){auth()->logout();return response()->json(['message' => 'Successfully logged out']);}/*** Refresh a token.** @return IlluminateHttpJsonResponse*/public function refresh(){return $this->respondWithToken(auth()->refresh());}/*** Get the token array structure.** @param string $token** @return IlluminateHttpJsonResponse*/protected function respondWithToken($token){return response()->json(['access_token' => $token,'token_type' => 'bearer','expires_in' => auth()->factory()->getTTL() * 60]);}
}
{"access_token": Jpc3MiOiJodHRwOlwvXC90ZXN0MS5jb21cL2FwaVwvYXV0aFwvbG9naW4iLCJpYXQiOjE2MzMwMDc4MjIsImV4cCI6MTYzMzAxMTQyMiwibmJmIjoxNjMzMDA3ODIyLCJqdGkiOiIyMG8zczNHQkd4NnlnR2tuIiwic3ViIjoxLCJwcnYiOiI0MWRmODgzNGYxYjk4ZjcwZWZhNjBhYWVkZWY0MjM0MTM3MDA2OTBjIn0.josgI6pz8VLEjw2-etbjDL49Ju7xCnEMnJfd-zPAauc","token_type": "bearer","expires_in": 3600
}
成功拿到了。
有以下需要注意:
config/api.php
中定义的路由访问时 不能直接访问auth/**
,必须添加api
前缀,如api/auth/login
,是由app/Providers/RouteServiceProvider.php
决定的123456
,原因是采用laravel的auth
机制来实现jwt
在登录的时候会默认将密码进行Bcrypt
加密,所以在注册时请直接将密码保存为该种加密方式之后可以使用此令牌向您的应用程序发出经过身份验证的请求。
之前在api.php中定义了
auth/me
现在来携带Token进行测试
成功拿到了响应!~
本文发布于:2024-02-03 08:16:11,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170691937349783.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |