加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 创业 > C语言 > 正文

Laravel 5.2 API使用JWT完成多用户认证

发布时间:2020-12-15 00:29:10 所属栏目:C语言 来源:网络整理
导读:Json Web Token JWT代表Json Web Token.JWT能有效地进行身份验证并连接前后端。 降地耦合性,取代session,进一步实现前后端分离 减少服务器的压力 可以很简单的实现单点登录 我在实现这个功能的时候查到了这个扩展“”,最新稳定版是0.5.9。OK照着wiki撸起来

Json Web Token


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

  • 降地耦合性,取代session,进一步实现前后端分离

  • 减少服务器的压力

  • 可以很简单的实现单点登录

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

安装扩展
composer require tymon/jwt-auth

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

config/app.php
'providers' => [
    ....
    TymonJWTAuthProvidersJWTAuthServiceProvider::class,// 注意这里的名字,下文会提到
],'aliases' => [
    ....
    'JWTAuth' => TymonJWTAuthFacadesJWTAuth::class
],

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

php artisan vendor:publish --provider="TymonJWTAuthProvidersJWTAuthServiceProvider"

最后一步需要生成JWT Key

php artisan jwt:generate
创建API路由

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

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at (Reason: CORS header 'Access-Control-Allow-Origin' missing)

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

创建中间件
php artisan make:middleware CORS

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

app/Http/Middleware/CORS.php
namespace AppHttpMiddleware;
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 AppHttp;
use IlluminateFoundationHttpKernel as HttpKernel;
class Kernel extends HttpKernel
{
    ...
    ...
    protected $routeMiddleware = [
        ...
        'cors' => AppHttpMiddlewareCORS::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 AppHttpMiddleware;
use Closure;
use TymonJWTAuthFacadesJWTAuth;
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 TymonJWTAuthExceptionsTokenInvalidException){
                return response()->json(['error'=>'Token 无效']);
            }else if ($e instanceof TymonJWTAuthExceptionsTokenExpiredException){
                return response()->json(['error'=>'Token 已过期']);
            }else{
                return response()->json(['error'=>'出错了']);
            }
        }
        return $next($request);
    }
}

OK,接着注册该中间件

app/Http/Kernel.php
namespace AppHttp;
use IlluminateFoundationHttpKernel as HttpKernel;
class Kernel extends HttpKernel
{
    ...
    ...
    protected $routeMiddleware = [
        ...
        'jwt.auth' => AppHttpMiddlewareauthJWT::class,];
}

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

app/Http/Controllers/ApiController.php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use AppUser;
use IlluminateSupportFacadesHash;
use TymonJWTAuthFacadesJWTAuth;

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



    Document
    

这里我们要注意一下,以上测试我们仍是基于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' => [
    ....
    TymonJWTAuthProvidersLaravelServiceProvider::class,// 上文已经提到过,这里的provider已经不是JWTauthServiceProvider
],
发布配置文件
php artisan vendor:publish --provider="TymonJWTAuthProvidersLaravelServiceProvider"
生成密钥
php artisan jwt:secret   // 发现没生成key的方法也变了,不是 php artisan jwt:generate了

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

config/auth.php
/**
 * 默认使用web这个guard
 */
'defaults' => [
        'guard' => 'web','passwords' => 'users',],'guards' => [
        'web' => [
            'driver' => 'session','provider' => 'users','api' => [
            'driver' => 'token',/**
         * 这里是我自定义的guard,这里我叫staffs,你也可以根据自己的业务需求设置admins等,并且我
         * 需要实现json web token认证
         */
        'staffs' => [
            'driver'   => 'jwt',// 结合扩展这里定义即生效
            'provider' => 'staffs'
        ]
],'providers' => [
'users' => [
    'driver' => 'eloquent','model' => AppUser::class,// 这里注意修改命名空间 通常是'model' => AppModelsUser::class,/**
 * 同样的这里定义自己的provider
 */
'staffs' => [
    'driver' => 'eloquent','model' => AppModelsStaff::class,]

// 'users' => [
//     'driver' => 'database',//     'table' => 'users',// ],'passwords' => [
'users' => [
    'provider' => 'users','email' => 'auth.emails.password','table' => 'password_resets','expire' => 60,/**
 * 这里我并没有设置如下,因为我的staff表并没有email字段,默认的重置密码功能暂时没考虑
 */

]

下一步,创建我们的staff model

Modelsstaff.php
namespace AppModels;

use IlluminateAuthAuthenticatable;
use IlluminateDatabaseEloquentModel;
use IlluminateAuthPasswordsCanResetPassword;
use IlluminateFoundationAuthAccessAuthorizable;
use IlluminateContractsAuthAuthenticatable as AuthenticatableContract;
use IlluminateContractsAuthAccessAuthorizable as AuthorizableContract;
use TymonJWTAuthContractsJWTSubject 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 AppHttpControllersStaff;

use AppModelsStaff;
use IlluminateHttpRequest;
use AppHttpControllersController;
use AppHttpRequests;
use IlluminateFoundationAuthThrottlesLogins;
use IlluminateFoundationAuthAuthenticatesAndRegistersUsers;
use IlluminateSupportFacadesValidator;
use TymonJWTAuthFacadesJWTAuth;
use IlluminateSupportFacadesAuth;

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(。markdown用的不多,排版不好请见谅,如有错误请指正,一起学习,谢谢。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读