JWT代表Json Web Token.JWT能有效地进行身份验证并连接前后端。

  • 降地耦合性,取代session,进一步实现前后端分离
  • 减少服务器的压力
  • 可以很简单的实现单点登录

我在实现这个功能的时候查到了这个扩展“tymon/jwt-auth”,最新稳定版是0.5.9。OK照着wiki撸起来,第一步我们先实现API

安装扩展#
composer require tymon/jwt-auth

之后打开config/app.php文件添加service provider 和 aliase

config/app.php#
'providers' => [
    ....
    Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,  // 注意这里的名字,下文会提到
],
'aliases' => [
    ....
    'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class
],

OK,现在来发布JWT的配置文件,比如令牌到期时间配置等

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

最后一步需要生成JWT Key

php artisan jwt:generate
创建API路由#

我在创建Api路由的时候会用到一个“cors”中间件,虽然它不是强制性的,但是后面你会发现报类似这样的错

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource athttp://xxx.com/api/register. (Reason: CORS header 'Access-Control-Allow-Origin' missing)

大致翻译下,“跨源请求阻塞:同源策略不允许读取http://kylesean.com/api/register远程资源。(原因:CORS 头“Access-Control-Allow-Origin” 没有)。” 这就是跨域请求导致的错误消息,当然你可以自定义Header,Origin, Method来解决跨域问题,不过我这边推荐一个package:barryvdh/laravel-cors(最新稳定版是0.8.2),这里安装过程省略。

创建中间件#
php artisan make:middleware CORS

进入app/Http/Middleware,编辑CORS.php

app/Http/Middleware/CORS.php#
namespace App\Http\Middleware;
use Closure;
class CORS
{
    public function handle($request, Closure $next)
    {
        header('Access-Control-Allow-Origin: *');

        $headers = [
            'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
            'Access-Control-Allow-Headers'=> 'Content-Type, X-Auth-Token, Origin'
        ];
        if($request->getMethod() == "OPTIONS") {
            return Response::make('OK', 200, $headers);
        }

        $response = $next($request);
        foreach($headers as $key => $value)
            $response->header($key, $value);
        return $response;
    }
}

Ok,在app/Http/Kernel.php注册中间件

app/Http/Kernel.php#
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
    ...
    ...
    protected $routeMiddleware = [
        ...
        'cors' => \App\Http\Middleware\CORS::class,
    ];
}

有了这个中间件我们就解决了跨域问题。接下来回到路由

app/Http/routes.php#
Route::group(['middleware' => ['api','cors'],'prefix' => 'api'], function () {
    Route::post('register', 'ApiController@register');     // 注册
    Route::post('login', 'ApiController@login');           // 登陆
    Route::group(['middleware' => 'jwt.auth'], function () {
        Route::post('get_user_details', 'APIController@get_user_details');  // 获取用户详情
    });
});
建议:过滤掉路由api/*下的csrf_token,方便测试开发#

上面的jwt-auth中间件现在还是无效的,接着创建这个middleware

php artisan make:middleware authJWT

同样的我们需要编辑下这个authJWT.php

app/Http/Middleware/authJWT.php#
namespace App\Http\Middleware;
use Closure;
use Tymon\JWTAuth\Facades\JWTAuth;
use Exception;
class authJWT
{
    public function handle($request, Closure $next)
    {
        try {
            // 如果用户登陆后的所有请求没有jwt的token抛出异常
            $user = JWTAuth::toUser($request->input('token')); 
        } catch (Exception $e) {
            if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
                return response()->json(['error'=>'Token 无效']);
            }else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
                return response()->json(['error'=>'Token 已过期']);
            }else{
                return response()->json(['error'=>'出错了']);
            }
        }
        return $next($request);
    }
}

OK,接着注册该中间件

app/Http/Kernel.php#
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
    ...
    ...
    protected $routeMiddleware = [
        ...
        'jwt.auth' => \App\Http\Middleware\authJWT::class,
    ];
}

然后,我们创建控制器管理所有的请求

app/Http/Controllers/ApiController.php#
<?php

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\User;
use Illuminate\Support\Facades\Hash;
use Tymon\JWTAuth\Facades\JWTAuth;

class ApiController extends Controller
{
    /*注册*/
    public function register(Request $request)
    {
        $input = $request->all();
        $input['password'] = Hash::make($input['password']);
        User::create($input);
        return response()->json(['result'=>true]);
    }

    /*登陆*/
    public function login(Request $request)
    {
        $input = $request->all();
        if (!$token = JWTAuth::attempt($input)) {
            return response()->json(['result' => '邮箱或密码错误.']);
        }
        return response()->json(['result' => $token]);
    }

    /*获取用户信息*/
    public function get_user_details(Request $request)
    {
        $input = $request->all();
        $user = JWTAuth::toUser($input['token']);
        return response()->json(['result' => $user]);
    }

}

最后一步我们就来模拟一个请求来测试这个api,为了模拟本地跨域请求,我们简单的新建一个静态页面test.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>

</body>
<script>
    $.ajax({
    url: "http://localhost/api/login",
    dataType: "json",
    type: "POST",
    data: {"email":kylesean@qq.com","password":"123456"},
    success: function (data) {
        alert(data.result)
    }
    // 这里我们用ajax请求测试,当然你也可以用Angular.js  Vue.js
});
</script>
</html>

这里我们要注意一下,以上测试我们仍是基于User table的,我们来模拟一下login过程,如果账号密码匹配成功,不出意外将会出现类似:

{
  "result": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlzcyI6Imh0dHA6XC9cL2xvY2FsaG9zdFwvYXBpXC9sb2dpbiIsImlhdCI6MTQ3MzQ1MjUyNSwiZXhwIjoxNDczNDU2MTI1LCJuYmYiOjE0NzM0NTI1MjUsImp0aSI6IjA1M2IzNjliYzYyZjJiZjJmMGMxNjFiNzIxNzY4Y2MzIn0.4WeezpSgEKjNmDFxv1nMU9HxqJgBE7bPyaJDRK4iLeA"
}

至此,我们已经实现了jwt的认证功能,那么我们接着完成下一半工作,实现jwt的多用户认证,即Jwt for Multi Auth.
如果你的业务场景是的确需要多用户认证,比如为管理员admin单独生成一张表,恰好字段也是laravel auth user里面默认的name email password remember_token等,那么实现起来就方便的多,官方文档和网上的demo示例已经很多了,但是若结合这个laravel/jwt-auth扩展进行多用户认证,其实坑还是蛮多的,由于该扩展0.5.9似乎不支持多用户认证(反正不会帮我们自定义好guard,当然我们可以自己在AuthServiceProvider里用boot方法实现) 我在其github issue里面看到好多人踩过此坑,结合我遇到的
总结一下,里面一个哥们说,得用^0.1@dev版本


Composer.json文件

内容如下:

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "type": "project",
    "require": {
        "php": ">=5.6.4",
        "barryvdh/laravel-ide-helper": "^2.4",
        "laravel/framework": "5.4.*",
        "laravel/tinker": "~1.0",
        "predis/predis": "^1.1"
    },
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~5.7",
        "dingo/api": "1.0.*@dev",
        "tymon/jwt-auth": "^1.0@dev"
    },
    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "scripts": {
        "post-root-package-install": [
            "php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "php artisan key:generate"
        ],
        "post-install-cmd": [
            "Illuminate\\Foundation\\ComposerScripts::postInstall",
            "php artisan optimize"
        ],
        "post-update-cmd": [
            "Illuminate\\Foundation\\ComposerScripts::postUpdate",
            "php artisan optimize"
        ]
    },
    "config": {
        "preferred-install": "dist",
        "sort-packages": true,
        "optimize-autoloader": true
    
}
}

 

新增内容如下:

"tymon/jwt-auth": "^1.0@dev"

 

App.php配置文件里:

'providers' => [

    ....

Tymon\JWTAuth\Providers\LaravelServiceProvider::class,

  // 上文已经提到过,这里的provider已经不是JWTauthServiceProvider

],

'aliases' => [

    ....


// 注册JWT门面
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,

],

 

更新包

Composer update -vvv

 

发布配置文件#(生成文件前先删除jwt.php文件)

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

 

生成密钥#

php artisan jwt:secret

 

Auth.php文件:

'guards' => [

.....

'admins' => [
    'driver' => 'jwt',
    'provider' => 'admins',
],

 

 ],

 

 

 

'providers' => [

'admins' => [
    'driver' => 'jwt',
    'provider' => 'admins',
],

 

 

],

 

 

建立模型admins

PHP artisan make:mode Admins

修改内页的内容:

<?php

namespace
App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Tymon\JWTAuth\Contracts\JWTSubject as AuthenticatableUserContract;

class Admins extends Model implements AuthenticatableContract, AuthorizableContract,  AuthenticatableUserContract
{
    //
    use Authenticatable, Authorizable, CanResetPassword;

    protected $table = 'admins';
    //protected $fillable = ['name', 'phone', 'password'];
    //protected $hidden = ['password', 'remember_token'];

    public function getJWTIdentifier()
    {
        return $this->getKey(); // Eloquent model method
    }

    /**
     *
@return array
     */
    
public function getJWTCustomClaims()
    {
        return [];
    }
}

 

在控制器中引用

use Illuminate\Http\Request;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use App\Admins;
use App\Http\Controllers\Controller;
use App\Http\Requests;
use Illuminate\Support\Facades\Validator;
use Tymon\JWTAuth\Facades\JWTAuth;
use Illuminate\Support\Facades\Auth;

 

控制器中加入变量,用于指定生成和解析token

protected $guard = 'managers';

 

生成token

$token = Auth::guard($this->guard)->attempt($input);

 

解析token

需要传token

$list = Auth::guard($this->guard)->user();

 

 

 

 

 

 

 

 

 


Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐