laravel5.4框架jwt-auth 实现API用户认证

laravel5.4框架jwt-auth 实现API用户认证

本文主要介绍JWT([JSON Web Token])授权机制在前后端分离中的应用与实践,包括以下三部分:
- JWT原理介绍
- JWT的安全性
- JWT与Vue的使用

1、JWT原理介绍

1.1、cookie and session

用户授权往往还是采用Cookie+Session的方式,也就是需要原生应用中模拟浏览器对Cookie的操作
Cookie+Session的存在主要是为了解决HTTP这一无状态协议下服务器如何识别用户的问题,其原理就是在用户登录通过验证后,服务端将数据加密后保存到客户端浏览器的Cookie中,同时服务器保留相对应的Session(文件或DB)。用户之后发起的请求都会携带Cookie信息,服务端需要根据Cookie寻回对应的Session,从而完成验证,确认这是之前登陆过的用户。其工作原理如下图所示:

1.2、jwt

JWT是Auth0提出的通过对JSON进行加密签名来实现授权验证的方案,编码之后的JWT看起来是这样的一串字符:可以参考jwt.io
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
.分为三段,通过解码可以得到:

// 1. Headers
// 包括类别(typ)、加密算法(alg);
{
  "alg": "HS256",
  "typ": "JWT"
}
// 2. Claims
// 包括需要传递的用户信息;
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
// 3. Signature
// 根据alg算法与私有秘钥进行加密得到的签名字串;
// 这一段是最重要的敏感信息,只能在服务端解密;
HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
    SECREATE_KEY
)

在使用过程中,服务端通过用户登录验证之后,将Header+Claim信息加密后得到第三段签名,然后将签名返回给客户端,在后续请求中,服务端只需要对用户请求中包含的JWT进行解码,即可验证是否可以授权用户获取相应信息,其原理如下图所示:

通过比较可以看出,使用JWT可以省去服务端读取Session的步骤,这样更符合RESTful的规范。但是对于客户端(或App端)来说,为了保存用户授权信息,仍然需要通过Cookie或类似的机制进行本地保存。因此JWT是用来取代服务端的Session而非客户端Cookie的方案,当然对于客户端本地存储,HTML5提供了Cookie之外更多的解决方案(localStorage/sessionStorage),究竟采用哪种存储方式,其实从Js操作上来看没有本质上的差异,不同的选择更多是出于安全性的考虑。

2、JWT的安全性

用户授权这样敏感的信息,安全性当然是首先需要考虑的因素。这里主要讨论在使用JWT时如何防止XSS和XSRF两种攻击。

XSS是Web中最常见的一种漏洞(我们的**学报官网就存在这个漏洞这件事我就不说了=.=),其主要原因是对用户输入信息不加过滤,导致用户(被误导)恶意输入的Js代码在访问该网页时被执行,而Js可以读取当前网站域名下保存的Cookie信息。针对这种攻击,无论是Cookie还是localStorage中的信息都有可能被窃取,但防止XSS也相对简单一些,对用户输入的所有信息进行过滤即可。另外,现在越来越多的CDN服务,让我们可以节省服务器流量,但同时也有可能引入不安全的Js脚本,例如前段时间Github被Great Cannon轰击的案例,则需要提高对某度之类服务的警惕。

另外一种更加棘手的XSRF漏洞主要利用Cookie是按照域名存储,同时访问某域名时浏览器会自动携带该域名所保存的Cookie信息这一特征。如果执意要将JWT存储在Cookie中,服务端则需要额外验证请求来源,或者在提交表单中加入随机签名并在处理表单时进行验证。

我在后面的实例中采用将JWT保存在localStorage中的方案,请求时将JWT放入Request Header中的Authorization位。对JWT安全性问题想要了解更多可以参考下面几篇文章:
- Where to Store Your JWTs - Cookies vs HTML5 Web Storage
- Use JWT the Right Way!
- 10 Things You Should Know about Tokens
- Where to store JWT in browser? How to protect against CSRF?

3、JWT与Vue的使用

3.1安装

jwt_auth 当前使用版本已经2.0.*了,但是这里 使用的是1.0的最新版,同样的 如果你是 Laravel 5.4 以下版本,也推荐使用最新版本,RC.1 前的版本都存在多用户token认证的安全问题。

$ composer require tymon/jwt-auth 1.0.*@dev

3.2 配置

3.2.1添加服务提供商

将下面这行添加至 config/app.php 文件 providers 数组中:

'providers' => [
    ...
    Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
]

3.3发布配置文件

$ php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

3.4生成密钥

jwt-auth 已经预先定义好了一个 Artisan 命令方便你生成 Secret
$ php artisan jwt:secret

此命令会在你的 .env 文件中新增一行 JWT_SECRET=secret。

3.5配置 Auth guard

在 config/auth.php 文件中,你需要将 guards/driver 更新为 jwt:

'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],
...
'guards' => [
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

只有在使用 Laravel 5.2 及以上版本的情况下才能使用。

3.6 更改 Model

如果需要使用 jwt-auth 作为用户认证,我们需要对我们的 User 模型进行一点小小的改变,实现一个接口,变更后的 User 模型如下:



namespace App;

use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User 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 [];
    }
}

3.7 配置项详解(jwt.php)



return [

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Secret
    |--------------------------------------------------------------------------
    |
    | 用于加密生成 token 的 secret
    |
    */

    'secret' => env('JWT_SECRET'),

    /*
    |--------------------------------------------------------------------------
    | JWT Authentication Keys
    |--------------------------------------------------------------------------
    |
    | 如果你在 .env 文件中定义了 JWT_SECRET 的随机字符串
    | 那么 jwt 将会使用 对称算法 来生成 token
    | 如果你没有定有,那么jwt 将会使用如下配置的公钥和私钥来生成 token
    |
    */

    'keys' => [

        /*
        |--------------------------------------------------------------------------
        | Public Key
        |--------------------------------------------------------------------------
        |
        | 公钥
        |
        */

        'public' => env('JWT_PUBLIC_KEY'),

        /*
        |--------------------------------------------------------------------------
        | Private Key
        |--------------------------------------------------------------------------
        |
        | 私钥
        |
        */

        'private' => env('JWT_PRIVATE_KEY'),

        /*
        |--------------------------------------------------------------------------
        | Passphrase
        |--------------------------------------------------------------------------
        |
        | 私钥的密码。 如果没有设置,可以为 null。
        |
        */

        'passphrase' => env('JWT_PASSPHRASE'),

    ],

    /*
    |--------------------------------------------------------------------------
    | JWT time to live
    |--------------------------------------------------------------------------
    |
    | 指定 access_token 有效的时间长度(以分钟为单位),默认为1小时,您也可以将其设置为空,以产生永不过期的标记
    |
    */

    'ttl' => env('JWT_TTL', 60),

    /*
    |--------------------------------------------------------------------------
    | Refresh time to live
    |--------------------------------------------------------------------------
    |
    | 指定 access_token 可刷新的时间长度(以分钟为单位)。默认的时间为 2 周。
    | 大概意思就是如果用户有一个 access_token,那么他可以带着他的 access_token 
    | 过来领取新的 access_token,直到 2 周的时间后,他便无法继续刷新了,需要重新登录。
    |
    */

    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),

    /*
    |--------------------------------------------------------------------------
    | JWT hashing algorithm
    |--------------------------------------------------------------------------
    |
    | 指定将用于对令牌进行签名的散列算法。
    |
    */

    'algo' => env('JWT_ALGO', 'HS256'),

    /*
    |--------------------------------------------------------------------------
    | Required Claims
    |--------------------------------------------------------------------------
    |
    | 指定必须存在于任何令牌中的声明。
    | 
    |
    */

    'required_claims' => [
        'iss',
        'iat',
        'exp',
        'nbf',
        'sub',
        'jti',
    ],

    /*
    |--------------------------------------------------------------------------
    | Persistent Claims
    |--------------------------------------------------------------------------
    |
    | 指定在刷新令牌时要保留的声明密钥。
    |
    */

    'persistent_claims' => [
        // 'foo',
        // 'bar',
    ],

    /*
    |--------------------------------------------------------------------------
    | Blacklist Enabled
    |--------------------------------------------------------------------------
    |
    | 为了使令牌无效,您必须启用黑名单。
    | 如果您不想或不需要此功能,请将其设置为 false。
    |
    */

    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),

    /*
    | -------------------------------------------------------------------------
    | Blacklist Grace Period
    | -------------------------------------------------------------------------
    |
    | 当多个并发请求使用相同的JWT进行时,
    | 由于 access_token 的刷新 ,其中一些可能会失败
    | 以秒为单位设置请求时间以防止并发的请求失败。
    |
    */

    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),

    /*
    |--------------------------------------------------------------------------
    | Providers
    |--------------------------------------------------------------------------
    |
    | 指定整个包中使用的各种提供程序。
    |
    */

    'providers' => [

        /*
        |--------------------------------------------------------------------------
        | JWT Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于创建和解码令牌的提供程序。
        |
        */

        'jwt' => Tymon\JWTAuth\Providers\JWT\Namshi::class,

        /*
        |--------------------------------------------------------------------------
        | Authentication Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于对用户进行身份验证的提供程序。
        |
        */

        'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,

        /*
        |--------------------------------------------------------------------------
        | Storage Provider
        |--------------------------------------------------------------------------
        |
        | 指定用于在黑名单中存储标记的提供程序。
        |
        */

        'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,

    ],

];

参考链接:
Laravel 5.5 使用 Jwt-Auth 实现 API 用户认证以及无痛刷新访问令牌
jwt.io
JWT 在前后端分离中的应用与实践

你可能感兴趣的:(PHP/Laravel大杂烩)