Json Web Token#


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 at http://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#
<?phpnamespace 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版本(什么鬼,what's the fuck!),so 继续撸之:

composer.json里修改为#
"require": {
   ...
   "tymon/jwt-auth": "^1.0@dev",  // 修改之前的,Or making a fresh start
   ...}
同样app.php里进行配置#
'providers' => [
   ....
   Tymon\JWTAuth\Providers\LaravelServiceProvider::class,  // 上文已经提到过,这里的provider已经不是JWTauthServiceProvider],'aliases' => [
   ....
   'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class],
发布配置文件#
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
生成密钥#

php artisan jwt:secret   // 发现没生成key的方法也变了,不是 php artisan jwt:generate了在.env文件中配置JWT_SECRET = 生成的的secret

接下来就是重点了,要设置好config/auth.php里面的配置项了,这里不能乱设置:

config/auth.php#
/**
* 默认使用web这个guard
*/'defaults' => [
       'guard' => 'web',
       'passwords' => 'users',
   ],'guards' => [
       'web' => [
           'driver' => 'session',
           'provider' => 'users',
       ],        'api' => [
           'driver' => 'token',
           'provider' => 'users',
       ],
       /**
        * 这里是我自定义的guard,这里我叫staffs,你也可以根据自己的业务需求设置admins等,并且我
        * 需要实现json web token认证
        */
       'staffs' => [
           'driver'   => 'jwt',   // 结合扩展这里定义即生效
           'provider' => 'staffs'
       ]    ],'providers' => [
   'users' => [
       'driver' => 'eloquent',
       'model' => App\User::class,   // 这里注意修改命名空间 通常是'model' => App\Models\User::class,
   ],    /**
    * 同样的这里定义自己的provider
    */
   'staffs' => [
       'driver' => 'eloquent',
       'model' => App\Models\Staff::class,
   ]    // 'users' => [
   //     'driver' => 'database',
   //     'table' => 'users',
   // ],],'passwords' => [
   'users' => [
       'provider' => 'users',
       'email' => 'auth.emails.password',
       'table' => 'password_resets',
       'expire' => 60,
   ],    /**
    * 这里我并没有设置如下,因为我的staff表并没有email字段,默认的重置密码功能暂时没考虑
    */
  <!-- 'staffs' => [
       'provider' => 'staffs',
       'email' => 'auth.emails.password',
       'table' => 'password_resets',
       'expire' => 60,                      
   ],-->]

下一步,创建我们的staff model

Models\staff.php#
<?phpnamespace App\Models;use Illuminate\Auth\Authenticatable;use Illuminate\Database\Eloquent\Model;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 Staff extends Model implements AuthenticatableContract, AuthorizableContract,  AuthenticatableUserContract{
   use Authenticatable, Authorizable, CanResetPassword;    protected $table = 'staffs';
   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 [];
   }}

好吧,接下来我们又要添加相关路由了

Route::post('/api/login', 'StaffAuthController@login');Route::post('/api/register', 'StaffAuthController@register');

控制器书写我们的业务逻辑

namespace App\Http\Controllers\Staff;use App\Models\Staff;use Illuminate\Http\Request;use App\Http\Controllers\Controller;use App\Http\Requests;use Illuminate\Foundation\Auth\ThrottlesLogins;use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;use Illuminate\Support\Facades\Validator;use Tymon\JWTAuth\Facades\JWTAuth;use Illuminate\Support\Facades\Auth;class StaffAuthController extends Controller{
   use AuthenticatesAndRegistersUsers, ThrottlesLogins;
   protected $guard = 'staffs';    /*注册*/
   public function register(Request $request)
   {
       $this->validate($request, [
           'phone' => 'required|max:16',
           'password' => 'required|min:6',
       ]);
       $credentials = [
           'phone' => $request->input('phone'),
           'password' => bcrypt($request->input('password')),
       ];        $id = Staff::create($credentials);
       if ($id) {
           $token = Auth::guard($this->getGuard())->attempt($credentials); // 也可以直接guard('staffs')
           return response()->json(['result' => $token]);
       }
   }    /*登录*/
   public function login(Request $request)
   {        $credentials = $request->only('phone','password');
       if ( $token = Auth::guard($this->getGuard())->attempt($credentials) ) {            return response()->json(['result' => $token]);
       } else {
           return response()->json(['result'=>false]);
       }
   }{

到现在,一个基于JWT的多用于认证系统雏形就建立就来了,这里面需要完善的东西很多,比如刷新token,退出登录,增加额外的中间件等,可以参考该扩展issue(Feature: Laravel 5.2 Custom Authentication Guard and Driver。markdown用的不多,排版不好请见谅,如有错误请指正,一起学习,谢谢。

Laravel 5.2 使用 JWT 完成多用户认证 | Laravel China 社区 - 高品质的 Laravel 开发者社区 - Powered by PHPHub的更多相关文章

  1. Entrust - Laravel 用户权限系统解决方案 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 - Powered by PHPHub

    说明# Zizaco/Entrust 是 Laravel 下 用户权限系统 的解决方案, 配合 用户身份认证 扩展包 Zizaco/confide 使用, 可以快速搭建出一套具备高扩展性的用户系统. ...

  2. 【扩展推荐】Laravel-ide-helper 高效的 IDE 智能提示插件 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 - Powered by PHPH

    说明# barryvdh/laravel-ide-helper 扩展包能让你的 IDE ( PHPStorm, Sublime ) 实现自动完成.代码智能提示和代码跟踪等功能,大大提高你的开发效率. ...

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

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

  4. laravel 5.3 多用户认证

    不知道对不对,乱来一下!!!! 1)laravel自带了一个用户认证系统,要使用的话,直接运行一下命令就可以用了 php artisan make:auth 会生成相应的控制器.路由和模版文件 2)数 ...

  5. Laravel 5.2 中多用户认证实现(前台和后台登录)

    Laravel 5.2中多用户认证支持,即同时允许不同数据表用户(如前台用户.后台用户.app用户等)登录认证.下面我们就来简单介绍多用户登录及注册功能实现. 1.生成认证脚手架 首先我们使用Lara ...

  6. 【laravel】基于jwt实现用户认证

    安装及基础配置 使用 composer 安装 # 建议使用1.0以上版本 composer require tymon/jwt-auth .*@rc 进行一些配置 有些文档会说要添加 Tymon\JW ...

  7. Node.js 使用JWT进行用户认证

    代码地址如下:http://www.demodashi.com/demo/13847.html 运行环境 该项目基于 node(v7.8.0版本以上) 和 mongodb 数据库,因此电脑上需要安装这 ...

  8. [转] Spring Boot实战之Filter实现使用JWT进行接口认证

    [From] http://blog.csdn.net/sun_t89/article/details/51923017 Spring Boot实战之Filter实现使用JWT进行接口认证 jwt(j ...

  9. Django-REST-Framework JWT 实现SSO认证(上)

    一.什么是Django-REST-Framework? Django-REST-framework 是基于Django框架的一个web RESTful风格开发的框架,它可以实现API接口的快速开发,但 ...

随机推荐

  1. SPSS分析过程可自动化,你知道吗

    SPSS分析过程可自动化,你知道吗 在使用SPSS的过程中,有时候会遇到重复进行相同分析操作的情况,或者分析过程很复杂的情况. 这时候我们多么希望SPSS能够记住上一次的分析步骤,不要让我们重复的去点 ...

  2. codeforces 1136E-Nastya Hasn't Written a Legend

    传送门:QAQQAQ 题意:有一个数组a和一个数组k,数组a一直保持一个性质:a[i + 1] >= a[i] + k[i].有两种操作:1,给某个元素加上x,但是加上之后要保持数组a的性质.比 ...

  3. pycharm优化

    一.版本选择 建议安装5.0版本,因为好注册,这个你懂得. 下载地址: https://confluence.jetbrains.com/display/PYH/Previous+PyCharm+Re ...

  4. <每日一题>题目21:简单的python练习题(21-30)

    #21.cookie和session的区别 ''' 1.cookie数据存放在客户的浏览器上,session数据存放在服务器上 2.cookie不是很安全,可以通过分析本地cookie组成伪造cook ...

  5. Apache Pig学习笔记(二)

     主要整理了一下,pig里面的一些关键词的含义和用法,pig虽然是一种以数据流处理为核心的框架,但数据库的大部分关键词和操作,在pig里面基本上都能找到对应的函数,非常灵活与简洁,春节前的最后一篇文章 ...

  6. Bash 常用快捷方式

    从历史中执行命令 ctrl +r 搜索历史命令记录 !$ 重复上一个命令参数 文本编辑的快捷方式 c    分别更改这些配对标点符号中的文本内容 di   分别删除这些配对标点符号中的文本内容 do ...

  7. 关于Canvas的坐标系

    注意Canvas的坐标系应该是这样子的: 看下面的例子: 最后的显示效果是:

  8. C++对C的扩展、增强

    C++对C的扩展 1. 双冒号::作用域运算符 代码中对同一个变量多次声明,在代码块中使用时,局部变量会将全局变量隐藏.若在代码块使用变量前添加::,表示为全局变量. ::表示作用域运算符,如常见的s ...

  9. SpringData _day02_JPQL和SQL的方式查询

    1.Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询 JPQ ...

  10. 博客的页面定制CSS

    我目前的博客CSS其实也是借用了别家的,来源:https://www.cnblogs.com/Penn000/p/6947472.html 注意使用的模板是:darkgreentrip 复制粘贴使用就 ...