laravel 验证jwt_使用JWT在Laravel中基于角色的身份验证

段干靖
2023-12-01

laravel 验证jwt

From personal experience, no JWT (JSON Web Token) library incorporates a feature for role-based authentication, at least for my core languages which are Node, PHP, C# and Java. In as much as the trend is building stateless API applications, only session authentication libraries come with role authorization helpers.

根据个人经验,至少没有针对我的核心语言(Node,PHP,C#和Java)的JWT(JSON Web令牌)库集成基于角色的身份验证功能。 在构建无状态API应用程序的趋势中,只有会话身份验证库才带有角色授权助手。

I am going to show you a simple hack for adding role based authentication to JWT powered applications, using Laravel as case study.

我将向您展示一个简单的技巧,以Laravel为例,为基于JWT的应用程序添加基于角色的身份验证。

背景 ( Background )

You can skip this section if you know everything about JWT and role-based authentication.

如果您了解有关JWT和基于角色的身份验证的所有知识,则可以跳过本节。

JWT is the best option at this time to create authentication for stateless applications, mostly Web API. It has a lot of advantages including flexibility, enables scaling of HTTP applications, self contained and available in all standard programming languages.

目前,JWT是为无状态应用程序(主要是Web API)创建身份验证的最佳选择。 它有很多优点,包括灵活性,能使缩放HTTP应用的,自包含的和可用的所有标准编程语言。

In Laravel, we are going to use Tymon's jwt-auth as demonstrated in this tutorial.

在Laravel中,我们将使用教程中演示的Tymon的jwt-auth

Role based authentication on the other hand is authorization mechanisms for applications. It does not just end at collecting username/email or password but figuring out identity and assigning roles to these identities while restricting permissions too.

另一方面,基于角色的身份验证是应用程序的授权机制。 它不仅结束于收集用户名/电子邮件或密码,而且还弄清楚身份并为这些身份分配角色,同时也限制了权限。

A user is an entity and has different characteristics from another. She can create another entity but might not be allowed to delete the entity. She might be allowed to view some entity but not allowed to edit it.

用户是一个实体,并且具有彼此不同的特征。 她可以创建另一个实体,但可能不允许删除该实体。 可能允许她查看某些实体,但不允许对其进行编辑。

Determining these restrictions and permissions is where the headache is because JWT already handles authenticating the user. We will make use of a common library, Entrust, to achieve authorization.

确定这些限制和权限是令人头疼的地方,因为JWT已经处理了对用户的身份验证。 我们将使用公共库Entrust来实现授权。

Where the problem lies as already described in the introduction is working with JWT and authorization. This might not be a problem to professionals but I am 90% certain that beginners are battling with this judging from questions thrown around in the community. Let us try and tackle this problem.

正如引言中已描述的那样,问题所在在使用JWT和授权。 对于专业人士而言,这可能不是问题,但我可以肯定90%的初学者正在与社区中提出的问题作斗争。 让我们尝试解决这个问题。

Laravel的HTTP中间件 ( Laravel's HTTP Middleware )

Middleware seem to scare beginners a lot and trust me I had that experience when I was new to Node, specifically Express JS. When I finally could wrap my mind around the concept, it felt so easy to do all over again and again. However, explaining middleware is not in the scope of this article.

中间件似乎吓到了很多初学者,请相信我,当我刚接触Node时,尤其是Express JS时,我就有过这种经验。 当我终于对这个概念付诸行动时,一次又一次地做起来很容易。 但是,解释中间件不在本文讨论范围之内。

For now, just see authentication middleware as the automatic security gateways in places like the airport but in this case way smarter.

现在,仅将身份验证中间件视为机场等地方的自动安全网关,但在这种情况下更智能。

We will dig into the source of jwt-auth and Entrust to see how their middlwares were developed and how we can blend them to work together. That sounds scary, I know, but it is no big deal.

我们将深入研究jwt-authEntrust的源代码,以了解它们的中间件软件是如何开发的以及如何将它们融合在一起一起工作。 我知道这听起来很吓人,但这没什么大不了的。

样品申请 ( Sample Application )

To get a clearer picture we will make a sample app to see how we can create custom middleware that will tie the functionalities of Entrust and JWT. What we need to do first is setup a Laravel application.

为了更清楚地了解情况,我们将制作一个示例应用程序,以了解如何创建将Entrust和JWT的功能联系在一起的自定义中间件。 我们首先需要设置一个Laravel应用程序。

设置和依赖项 (Setup and Dependencies)

After we install and create a Laravel application we can pull in the the following dependencies in composer.json:

我们经过安装和创建一个Laravel应用程序,我们可以在下面的依赖关系拉composer.json

"tymon/jwt-auth": "0.5.*",
"zizaco/entrust": "dev-laravel-5"

We need to install the new dependencies by running:

我们需要通过运行以下命令安装新的依赖项:

composer update

Next, let us include the new installed dependencies to our config/app.php. In the providers array, add:

接下来,让我们将新安装的依赖项包含到config/app.php 。 在providers数组中,添加:

$providers = [
// Other providers
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
Zizaco\Entrust\EntrustServiceProvider::class
]

and in the aliases array:

并在aliases数组中:

$aliases = [
// Other aliases
'JWTAuth'   => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
'Entrust' => Zizaco\Entrust\EntrustServiceProvider::class
]

Then publish the JWT dependency with the following commands:

然后使用以下命令发布JWT依赖项:

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

We need to generate a secret key for the JWT. Run the following command to do so:

我们需要为JWT生成一个密钥。 运行以下命令来这样做:

php artisan jwt:generate

Another important thing to do is generate the migrations for the roles and permissions with there relationships:

要做的另一件事是为具有关系的角色和权限生成迁移:

php artisan entrust:migration

We can go ahead and create a seed for the users table. We will not create seeds for roles and permission entities so we can demonstrate how to do that from our controller logic. In database/seeds/DatabaseSeeder.php, update the run() method with:

我们可以继续为users表创建种子。 我们不会为rolespermission实体创建种子,因此我们可以从控制器逻辑中演示如何做到这一点。 在database/seeds/DatabaseSeeder.php ,使用以下命令更新run()方法:

public function run()
    {
        Model::unguard();

        // $this->call(UserTableSeeder::class);
        DB::table('users')->delete();

        $users = array(
            ['name' => 'Ryan Chenkie', 'email' => 'ryanchenkie@gmail.com', 'password' => Hash::make('secret')],
            ['name' => 'Chris Sevilleja', 'email' => 'chris@scotch.io', 'password' => Hash::make('secret')],
            ['name' => 'Holly Lloyd', 'email' => 'holly@scotch.io', 'password' => Hash::make('secret')],
            ['name' => 'Adnan Kukic', 'email' => 'adnan@scotch.io', 'password' => Hash::make('secret')],
        );

        // Loop through each user above and create the record for them in the database
        foreach ($users as $user)
        {
            User::create($user);
        }

        Model::reguard();
    }

The final step in the setup section is to run migration hoping that you have configured your database in .env:

设置部分的最后一步是运行迁移,希望您已在.env配置了数据库:

php artisan migrate

所需的路线,控制器和口才模型 (The Needed Routes, Controller and Eloquent Models)

The following routes will be needed for us to accomplish our task in this tutorial:

我们需要以下路线来完成本教程中的任务:

// Route to create a new role
Route::post('role', 'JwtAuthenticateController@createRole');
// Route to create a new permission
Route::post('permission', 'JwtAuthenticateController@createPermission');
// Route to assign role to user
Route::post('assign-role', 'JwtAuthenticateController@assignRole');
// Route to attache permission to a role
Route::post('attach-permission', 'JwtAuthenticateController@attachPermission');


// API route group that we need to protect
Route::group(['prefix' => 'api', 'middleware' => ['ability:admin,create-users']], function()
{
    // Protected route
    Route::get('users', 'JwtAuthenticateController@index');
});

// Authentication route
Route::post('authenticate', 'JwtAuthenticateController@authenticate');

Each route points to respective action methods under the JwtAuthenticateController so it is time to create that:

每个路由都指向JwtAuthenticateController下的相应操作方法,因此现在该创建它了:

<?php

namespace App\Http\Controllers;

use App\Permission;
use App\Role;
use App\User;
use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Log;

class JwtAuthenticateController extends Controller
{

    public function index()
    {
        return response()->json(['auth'=>Auth::user(), 'users'=>User::all()]);
    }


    public function authenticate(Request $request)
    {
        $credentials = $request->only('email', 'password');

        try {
            // verify the credentials and create a token for the user
            if (! $token = JWTAuth::attempt($credentials)) {
                return response()->json(['error' => 'invalid_credentials'], 401);
            }
        } catch (JWTException $e) {
            // something went wrong
            return response()->json(['error' => 'could_not_create_token'], 500);
        }

        // if no errors are encountered we can return a JWT
        return response()->json(compact('token'));
    }

    public function createRole(Request $request){
        // Todo       
    }

    public function createPermission(Request $request){
        // Todo       
    }

    public function assignRole(Request $request){
         // Todo
    }

    public function attachPermission(Request $request){
        // Todo       
    }

}

We have only implemented two of the few methods. The index() method is for our protected route, which just lists all users. The authenticate() method uses the JWTAuth's attempt() method to create a token for the user.

我们仅实现了少数几种方法中的两种。 index()方法用于我们的受保护路由,该路由仅列出所有usersauthenticate()方法使用JWTAuth attempt()方法为用户创建令牌。

use App\Permission;
use App\Role;
use App\User;

You are obviously wondering when we created the Permission and Role classes as seen above. We have yet to do that, so in your app directory, create a Permission.php file:

您显然想知道我们何时创建如上所示的PermissionRole类。 我们尚未这样做,因此在您的app目录中,创建一个Permission.php文件:

<?php namespace App;

use Zizaco\Entrust\EntrustPermission;

class Permission extends EntrustPermission
{
}

and Roles.php:

Roles.php

<?php namespace App;

use Zizaco\Entrust\EntrustRole;

class Role extends EntrustRole
{
}

Those are just the eloquent models needed for the permissions and roles table.

这些只是permissionsroles表所需的雄辩模型。

We need a way to tell the User model that it has some kind of relationship with roles and permissions:

我们需要一种方法来告诉User模型它与rolespermissions之间存在某种关系:

<?php

use Zizaco\Entrust\Traits\EntrustUserTrait;

class User extends Eloquent
{
    use Authenticatable, CanResetPassword, EntrustUserTrait;
    ...
}

Notice we have replaced the Authorizable with EntrustUserTrait trait in the model as we are not going to use Laravel's built in authorization as it will cause conflicts with Entrust.

注意,由于我们将不使用Laravel的内置授权,因为它会导致与Entrust发生冲突,因此我们在模型中将Authorizable替换为EntrustUserTrait特性。

委托人的行动方法 (Controller Action Methods for Entrust)

Remember we left some of the controller action methods empty, now is the time to flesh them out. The actions are expected to create roles, permissions, attach permissions to roles and assign roles to users

记住,我们将一些控制器动作方法留空了,现在是充实它们的时候了。 这些操作应可创建角色,权限,将权限附加到角色以及将角色分配给用户

The createRole method only requires a name input which is the going to be set as the name of the role:

createRole方法只需要一个name输入将被设置为角色的名称会:

public function createRole(Request $request){

        $role = new Role();
        $role->name = $request->input('name');
        $role->save();

        return response()->json("created");

    }

NB: This is just a demo. In a real application, kindly do some protective checks before saving. Also visit the Entrust documentation for more available options.

注意:这只是一个演示。 在实际应用中,请在保存之前进行一些保护性检查。 另请访问Entrust文档以获取更多可用选项。

createPermission is just like the createRole method, but creates a permission:

createPermission类似于createRole方法,但是创建一个权限:

public function createPermission(Request $request){

        $viewUsers = new Permission();
        $viewUsers->name = $request->input('name');
        $viewUsers->save();

        return response()->json("created");

    }

Next, the assignRole is responsible for assigning a given role to a user. Which means it needs a role id and a user object:

接下来, assignRole负责将给定角色分配给用户。 这意味着它需要一个角色ID和一个用户对象:

public function assignRole(Request $request){
        $user = User::where('email', '=', $request->input('email'))->first();

        $role = Role::where('name', '=', $request->input('role'))->first();
        //$user->attachRole($request->input('role'));
        $user->roles()->attach($role->id);

        return response()->json("created");
    }

Finally, the attachPermission adds the permissions for a role:

最后, attachPermission添加角色的权限:

public function attachPermission(Request $request){
        $role = Role::where('name', '=', $request->input('role'))->first();
        $permission = Permission::where('name', '=', $request->input('name'))->first();
        $role->attachPermission($permission);

        return response()->json("created");
    }

骇客 ( The Hack )

Take another look at the routes. You will see that there is middleware called ability on the protected routes:

再看看路线。 您将看到在受保护的路由上有一个称为“能力”的中间件:

Route::group(['prefix' => 'api', 'middleware' => ['ability:admin,create-users']], function()
{
        Route::get('users', 'JwtAuthenticateController@index');

});

We are just saying that we need the user to be an admin or have the create-users permissions before they can access the routes in this group.

我们只是说我们需要用户成为管理员具有create-users权限,然后他们才能访问该组中的路由。

Use Postman to create an admin role and create-users permission.

使用Postman创建admin角色和create-users权限。

Doing that will not protect the routes but rather throw errors, so we need to define this middleware. Entrust already has a EntrutAbility that can be seen here but the problem is that it works with sessions and not tokens.

这样做不会保护路由,而是会引发错误,因此我们需要定义此中间件。 Entrust已经具有一个EntrutAbility ,可以在此处看到但问题是它与会话一起使用,而不与令牌一起使用。

What we can do is extend the JWT's middleware to include Entrust's and work with a token, not session.

我们可以做的是扩展JWT的中间件,使其包含Entrust的中间件 ,并使用令牌(而不是会话)工作。

Run:

跑:

php artisan make:middleware TokenEntrustAbility

TokenEntrustAbility will be created in app/Http/Middleware. Replace the content with:

TokenEntrustAbility将在app/Http/Middleware创建。 将内容替换为:

<?php

namespace App\Http\Middleware;

use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Middleware\BaseMiddleware;

class TokenEntrustAbility extends BaseMiddleware
{
       public function handle($request, Closure $next, $roles, $permissions, $validateAll = false)
    {

        if (! $token = $this->auth->setRequest($request)->getToken()) {
            return $this->respond('tymon.jwt.absent', 'token_not_provided', 400);
        }

        try {
            $user = $this->auth->authenticate($token);
        } catch (TokenExpiredException $e) {
            return $this->respond('tymon.jwt.expired', 'token_expired', $e->getStatusCode(), [$e]);
        } catch (JWTException $e) {
            return $this->respond('tymon.jwt.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
        }

        if (! $user) {
            return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404);
        }

       if (!$request->user()->ability(explode('|', $roles), explode('|', $permissions), array('validate_all' => $validateAll))) {
            return $this->respond('tymon.jwt.invalid', 'token_invalid', 401, 'Unauthorized');
        }



        $this->events->fire('tymon.jwt.valid', $user);

        return $next($request);
    }
}

Now the JWT middleware has been extended to include the Entrust's ability middleware by including this tiny block:

现在,JWT中间件通过包含以下小块扩展为包括Entrust的功能中间件:

if (!$request->user()->ability(explode('|', $roles), explode('|', $permissions), array('validate_all' => $validateAll))) {

            return $this->respond('tymon.jwt.invalid', 'token_invalid', 401, 'Unauthorized');

        }

Finally, register the middleware in app/Http/Kernel.php:

最后,在app/Http/Kernel.php注册中间件:

protected $routeMiddleware = [
// ... Other middlewares
'ability' => 'App\Http\Middleware\TokenEntrustAbility'
]

Now you won't get an exception again if you tried accessing the protected routes. Instead you will get a 401 or the data you seek if you are authenticated as the required role.

现在,如果您尝试访问受保护的路由,则不会再出现异常。 相反,如果您已通过身份验证为必需角色,则将获得401或所需的数据。

结论 ( Conclusion )

As a recap, we just allowed jwt-auth and Entrust to work independently. Then we went an extra mile to create a custom middleware other than what the libraries provide to better meet our needs.

作为回顾,我们只允许jwt-authEntrust独立工作。 然后,除了库提供的内容可以更好地满足我们的需求之外,我们还付出了更多努力来创建自定义中间件。

Use the provided demo as an example if you got lost anywhere in the middle of this tutorial. Feel free to use the comments as a mini forum for your confusions and we will be glad to fight your code battles with you.

如果您在本教程中间的任何地方迷路,请以提供的演示为例。 随意将注释用作迷你论坛,以解决您的困惑,我们将很高兴与您一起进行代码之战。

翻译自: https://scotch.io/tutorials/role-based-authentication-in-laravel-with-jwt

laravel 验证jwt

 类似资料: