jwt笔记


相关链接

https://github.com/tymondesigns/jwt-auth/wiki

基于JWT(Json Web Token)的授权方式

JWT 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;

从客户端请求服务器获取token, 用该token 去访问实现了jwt认证的web服务器。

token 可保存自定义信息,如用户基本信息, web服务器用key去解析token,就获取到请求用户的信息了;

很方便解决跨域授权的问题,因为跨域无法共享cookie,用jwt完美解决了。

JWT缺点

  • 一旦拿到token, 可用它访问服务器,直到过期,中间服务器无法控制它, 如是它失效(有解决方案: 在 token 中保存信息,可添加额外的验证,如加一个 flag, 把数据库对应的flag失效,来控制token有效性)。
  • token 的过期时间设置很关键,一般把它设到凌晨少人访问时失效,以免用户使用过程中失效而丢失数据。
  • token保存的信息有限,且都是字符串。

JWT字符串解析

JWT是Auth0提出的通过对JSON进行加密签名来实现授权验证的方案,编码之后的JWT看起来是这样的一串字符:

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
)

JWT安装

  • 安装JWT
composer require tymon/jwt-auth
  • 注册服务提供者 config/app.php
'providers' => [
    Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class
]
  • 注册门面 config/app.php
'aliases' => [
        'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
        'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
]
  • 写入配置文件 config/jwt.php
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
在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直到它们失效

-- 生成密钥

php artisan jwt:generate

jwt测试用例

-- 生成控制器

php artisan make:controller JWTController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use JWTAuth; // use Facade
use Tymon\JWTAuth\Exceptions\JWTException;
use App\User;

class JWTController extends Controller
{
    public function index(Request $request)
    {
        // 根据用户信息生成一个token
        $token = JWTAuth::attempt(['email' => 'test@qq.com', 'password' => '123456']);
        var_dump($token);

        return 'JWTController';
    }

    /**
     * 获取用户相关信息
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function getUserToken(Request $request)
    {
        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());

        }

        // 如果token验证通过,则返回用户相关信息
        return response()->json(compact('user'));
    }

    /**
     * 用户验证
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function authenticate(Request $request)
    {
        // 获取表单信息
        $email = $request->get('email');
        $password = $request->get('password');

        // 生成表单信息
        $userInfo = [
            'email'    => $email,
            'password' => $password,
        ];

        try {
            // 根据用户信息试着生成一个token
            if (!$token = JWTAuth::attempt($userInfo)) {
                return response()->json(['error' => 'invalid_credentials'], 401);
            }
        } catch (JWTException $e) {
            // 无法生成token
            return response()->json(['error' => 'could_not_create_token'], 500);
        }

        // 验证成功后返回token
        return response()->json(compact('token'));
    }

    /**
     * 用户注册
     * @param Request $request
     * @return string 成功后返回token
     */
    public function register(Request $request)
    {
        // 获取表单信息
        $name = $request->get('name');
        $email = $request->get('email');
        $password = bcrypt($request->get('password'));

        // 生成表单信息
        $userInfo = [
            'name'     => $name,
            'email'    => $email,
            'password' => $password,
        ];

        $result = User::where('email', '=', $email)->first();

        if ($result) {
            return ['此用户已经注册过'];
        }

        // 根据用户生成token
        $user = User::create($userInfo);
        $token = JWTAuth::fromUser($user);

        // 将token存储在数据库中
        if ($token) {
            User::where('email', '=', $request->get('email'))->update(['remember_token' => $token]);
        }

        // 返回token
        return response()->json(compact('token'));
    }
}

-- 添加路由 routes/api.php

// JWT路由
$api->version(['v1', 'v2'], function($api){
    $api->get('jwt', 'App\Http\Controllers\JWTController@index');
    $api->get('jwt/register', 'App\Http\Controllers\JWTController@register'); // 注册
    $api->get('jwt/authenticate', 'App\Http\Controllers\JWTController@authenticate'); // 登录
    $api->get('jwt/getusertoken', 'App\Http\Controllers\JWTController@getusertoken'); // 登录
});

使用http://html.dev/api/jwt进行访问

-- 配置数据库

首先要配置.env里数据库的连接信息

// 修改文件
`database/migrations/2014_10_12_000000_create_users_table.php`
$table->string('email', 500)->unique();
`database/migrations/2014_10_12_100000_create_password_resets_table.php`
$table->string('email', 500)->index();
php artisan migrate

-- 测试链接

http://html.dev/api/jwt/getusertoken
?name=test
&email=test@qq.com
&password=123456
&token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJzdWIiOjcsImlzcyI6Imh0dHA6Ly9odG1sLmRldi9hcGkvand0L3JlZ2lzdGVyIiwiaWF0IjoxNDk5NTk0MDQwLCJleHAiOjE0OTk1OTc2NDAsIm5iZiI6MTQ5OTU5NDA0MCwianRpIjoibUlVRHg5b3NxTmJLZVlLNyJ9
.eij1EPrV2wmOA83aAal2qQiDGdKEJ5qbUAR0g_StO8M