• 技术文章 >后端开发 >php教程

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

    2016-06-20 12:34:14原创2111

    在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的标准所定义的。

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

    将上面的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扩展包:

    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中,你可以配置以下选项:

    创建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的用户认证将不再困难。

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:php中的数组函数学习记录1 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • PHP反序列化入门总结(小白必看)• 一文详解PHP用流方式实现下载文件(附代码示例)• 聊聊PHP escapeshellarg函数使用的中文问题• PHP原生类的总结分享• 分享PHP函数使用小工具(附代码示例)
    1/1

    PHP中文网