Laravel 5 中使用 JWT(Json Web Token) 实现基于API的用户认证

php中文网
发布: 2016-06-20 12:34:14
原创
3084人浏览过

在JavaScript前端技术大行其道的今天,我们通常只需在后台构建API提供给前端调用,并且后端仅仅设计为给前端移动App调用。用户认证是Web应用的重要组成部分,基于API的用户认证有两个最佳解决方案 —— OAuth 2.0 和JWT(JSON Web Token)。

1、JWT定义及其组成

JWT(JSON Web Token)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。

一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

载荷(Payload)

我们先将用户认证的操作描述成一个JSON对象。其中添加了一些其他的信息,帮助今后收到这个JWT的服务器理解这个JWT。

{    "sub": "1",    "iss": "http://localhost:8000/auth/login",    "iat": 1451888119,    "exp": 1454516119,    "nbf": 1451888119,    "jti": "37c107e4609ddbcc9c096ea5ee76c667"}
登录后复制

这里面的前6个字段都是由JWT的标准所定义的。

  • sub: 该JWT所面向的用户
  • iss: 该JWT的签发者
  • iat(issued at): 在什么时候签发的token
  • exp(expires): token什么时候过期
  • nbf(not before):token在此时间之前不能被接收处理
  • jti:JWT ID为web token提供唯一标识

这些定义都可以在 标准 中找到。

将上面的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是一种编码,也就是说,它是可以被翻译回原来的样子来的。它并不是一种加密过程。

头部(Header)

JWT还需要一个头部,头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象:

{  "typ": "JWT",  "alg": "HS256"}
登录后复制

在这里,我们说明了这是一个JWT,并且我们所用的签名算法(后面会提到)是HS256算法。

对它也要进行Base64编码,之后的字符串就成了JWT的Header(头部):

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
登录后复制

签名(签名)

将上面的两个编码后的字符串都用句号.连接在一起(头部在前),就形成了:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4ODExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ
登录后复制

最后,我们将上面拼接完的字符串用HS256算法进行加密。在加密的时候,我们还需要提供一个密钥(secret):

HMACSHA256(    base64UrlEncode(header) + "." +    base64UrlEncode(payload),    secret)
登录后复制

这样就可以得到我们加密后的内容:

wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4
登录后复制

这一部分又叫做签名。

最后将这一部分签名也拼接在被签名的字符串后面,我们就得到了完整的JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDUxODg4MTE5LCJleHAiOjE0NTQ1MTYxMTksIm5iZiI6MTQ1MTg4ODExOSwianRpIjoiMzdjMTA3ZTQ2MDlkZGJjYzljMDk2ZWE1ZWU3NmM2NjcifQ.wyoQ95RjAyQ2FF3aj8EvCSaUmeP0KUqcCJDENNfnaT4
登录后复制

2、集成JWT到Laravel 5

安装

我们使用Composer安装jwt扩展包:

Find JSON Path Online
Find JSON Path Online

Easily find JSON paths within JSON objects using our intuitive Json Path Finder

Find JSON Path Online 30
查看详情 Find JSON Path Online
composer require tymon/jwt-auth 0.5.*
登录后复制

配置

安装完成后,需要在 config/app.php 中注册相应的服务提供者:

Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class
登录后复制

然后注册需要用到的对应门面:

'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class
登录后复制

然后发布相应配置文件:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
登录后复制

最后生成密钥:

php artisan jwt:generate
登录后复制

如果你想要将其添加到 .env 文件中,在 .env 中创建 JWT_SECRET 字段并再次执行生成密钥的命令。

在config/jwt.php中,你可以配置以下选项:

  • ttl:token有效期(分钟)
  • refresh_ttl:刷新token时间(分钟)
  • algo:token签名算法
  • user:指向User模型的命名空间路径
  • identifier:用于从token的sub中获取用户
  • require_claims:必须出现在token的payload中的选项,否则会抛出 TokenInvalidException 异常
  • blacklist_enabled:如果该选项被设置为false,那么我们将不能废止token,即使我们刷新了token,前一个token仍然有效
  • providers:完成各种任务的具体实现,如果需要的话你可以重写他们
    • User —— providers.user:基于sub获取用户的实现
    • JWT —— providers.jwt:加密/解密token
    • Authentication —— providers.auth:通过证书/ID获取认证用户
    • Storage —— providers.storage:存储token直到它们失效

创建Token

创建用户token最常用的方式就是通过登录实现用户认证,如果成功则返回相应用户的token。这里假设我们有一个 AuthenticateController :

use JWTAuth;use Tymon\JWTAuth\Exceptions\JWTException;class AuthenticateController extends Controller{    public function authenticate(Request $request)    {        // grab credentials from the request        $credentials = $request->only('email', 'password');        try {            // attempt to verify the credentials and create a token for the user            if (! $token = JWTAuth::attempt($credentials)) {                return response()->json(['error' => 'invalid_credentials'], 401);            }        } catch (JWTException $e) {            // something went wrong whilst attempting to encode the token            return response()->json(['error' => 'could_not_create_token'], 500);        }        // all good so return the token        return response()->json(compact('token'));    }}
登录后复制

有时候我们还可以直接通过用户对象实例创建token:

// grab some user$user = User::first();$token = JWTAuth::fromUser($user);
登录后复制

此外,还可以使用 Tymon\JWTAuth\PayloadFactory 实例(或者 JWTFactory 门面)基于任意数据创建token:

$customClaims = ['foo' => 'bar', 'baz' => 'bob'];$payload = JWTFactory::make($customClaims);$token = JWTAuth::encode($payload);
登录后复制

还可以使用方法链的方式:

// add a custom claim with a key of `foo` and a value of ['bar' => 'baz']$payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make();$token = JWTAuth::encode($payload);
登录后复制

用户认证

用户登录成功之后,下一步就是发送一个包含token的请求来获取用户信息。

要通过http发送一个需要认证通过的请求,需要设置Authorization头:

Authorization: Bearer {yourtokenhere}
登录后复制

如果用户名/密码没有进行base64编码那么Apache似乎会摒弃Authorization头,要修复这一问题你可以添加如下代码到Apache配置文件:

RewriteEngine OnRewriteCond %{HTTP:Authorization} ^(.*)RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
登录后复制

或者将token信息包含到URL中:

http://api.mysite.com/me?token={yourtokenhere}
登录后复制

要从请求中获取token,你可以这么做:

// this will set the token on the objectJWTAuth::parseToken();// and you can continue to chain methods$user = JWTAuth::parseToken()->authenticate();
登录后复制

要获取该token值,你可以这么调用:

$token = JWTAuth::getToken();
登录后复制

如果token被设置则会返回,否则会尝试使用方法从请求中解析token,如果token未被设置或不能解析最终返回false。

当然如果需要的话你还可以手动设置token:

JWTAuth::setToken('foo.bar.baz');
登录后复制

从Token中获取认证用户:

// somewhere in your controllerpublic function getAuthenticatedUser(){    try {        if (! $user = JWTAuth::parseToken()->authenticate()) {            return response()->json(['user_not_found'], 404);        }    } catch (Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {        return response()->json(['token_expired'], $e->getStatusCode());    } catch (Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {        return response()->json(['token_invalid'], $e->getStatusCode());    } catch (Tymon\JWTAuth\Exceptions\JWTException $e) {        return response()->json(['token_absent'], $e->getStatusCode());    }    // the token is valid and we have found the user via the sub claim    return response()->json(compact('user'));}
登录后复制

jwt-auth扩展还提供了两个中间件 GetUserFromToken 和 RefreshToken ,前者用于在请求头和参数中检查是否包含token,并尝试对其解码,后者会再次从请求中解析token,并顺序刷新token(同时废弃老的token)并将其作为下一个响应的一部分。要使用这两个中间件,需要到 app/Http/Kernel.php 下的 $routeMiddleware 属性中注册它们:

protected $routeMiddleware = [    ...    'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',    'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',];
登录后复制

JWT让用户认证变得简单和安全,token会被保存到本地的 storage/web 或Cookie中,使用JWT,基于API的用户认证将不再困难。

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号