使用 Laravel 和 Mongodb 进行多 Jwt 身份验证

Posted

技术标签:

【中文标题】使用 Laravel 和 Mongodb 进行多 Jwt 身份验证【英文标题】:Multi Jwt Auth with Laravel and Mongodb 【发布时间】:2018-11-16 14:12:30 【问题描述】:

我有三种类型的 Authenticatable 模型,我需要为每种模型进行单独的 JWT 身份验证。让我详细解释一下我的问题。

我使用 MongoDB 作为我的数据库,Laravel MongoDB 是我使用的包。

UserAdminServiceProvider 是我的模型。

为了在 Laravel 中进行 JWT 身份验证,我使用 jwt-auth 包。用户模型(集合)没问题。当我想将 JWT 与任何其他模型一起使用时它不起作用并再次使用 user 完成所有操作。

我搜索了很多,发现要更改提供者用户模型,我可以使用Config::set(); 方法,如下所示,

Config::set('jwt.user', Admin::class);
Config::set('auth.providers.users.model', Admin::class);

但对 JWT 身份验证没有影响。 (我用Config::get()方法检查了'jwt.user''auth.providers.users.model'的值并返回,已改为'App\Admin')。

需要说,我的代码根据包的文档尽可能简单。 这是我的UserController 代码:

class UserController extends Controller

    public function login(Request $request)
    
        $validator = Validator::make($request->all(), [
            'email' => 'required|string|email|max:255',
            'password' => 'required|min:6'
        ]);

        if ($validator->fails()) 
            return response()->json($validator->errors());
        

        $credentials = $request->only('email', 'password');

        try 
            if (!$token = JWTAuth::attempt($credentials)) 
                return response()->json(['error' => 'invalid_credentials'], 401);
            
         catch (JWTException $e) 
            return response()->json(['error' => 'could_not_create_token'], 500);
        

        $user = User::where('email', $request->email)->first();
        return response()->json([
            'user' => $user,
            'token' => $token
        ]);
    

    public function register(Request $request)
    
        $validator = Validator::make($request->all(), [
            'email' => 'required|string|email|max:255|unique:users',
            'phone' => 'required|valid_phone|unique:users',
            'password' => 'required|min:6',
            'first_name' => 'required',
            'last_name' => 'required',
        ]);

        if ($validator->fails()) 
            return response()->json($validator->errors());
        

        User::create([
            'phone' => $request->get('phone'),
            'first_name' => $request->get('first_name'),
            'last_name' => $request->get('last_name'),
            'city_abbr' => $request->get('city_abbr'),
            'email' => $request->get('email'),
            'password' => bcrypt($request->get('password')),
        ]);
        $user = User::first();
        $token = JWTAuth::fromUser($user);

        return response()->json([
            'user' => $user,
            'token' => $token
        ]);

    


还有我的AdminController

class AdminController extends Controller


    public function login(Request $request)
    
        $validator = Validator::make($request->all(), [
            'email' => 'required|string|email|max:255',
            'password' => 'required|min:6'
        ]);

        if ($validator->fails()) 
            return response()->json($validator->errors());
        

        $credentials = $request->only('email', 'password');

        Config::set('jwt.user', Admin::class);
        Config::set('auth.providers.users.model', Admin::class);

        try 
            if (!$token = JWTAuth::attempt($credentials)) 
                return response()->json(['error' => 'invalid_credentials'], 401);
            
         catch (JWTException $e) 
            return response()->json(['error' => 'could_not_create_token'], 500);
        

        $admin = Admin::where('email', $request->email)->first();
        return response()->json([
            'admin' => $admin,
            'token' => $token
        ]);
    

    public function register(Request $request)
    
        $validator = Validator::make($request->all(), [
            'email' => 'required|string|email|max:255|unique:admins',
            'phone' => 'required|valid_phone|unique:admins',
            'password' => 'required|min:6',
            'name' => 'required',
        ]);

        if ($validator->fails()) 
            return response()->json($validator->errors());
        

        $admin = Admin::create([
            'phone' => $request->get('phone'),
            'name' => $request->get('name'),
            'access' => $request->get('access'),
            'email' => $request->get('email'),
            'password' => bcrypt($request->get('password')),
        ]);
        Config::set('jwt.user', Admin::class);
        Config::set('auth.providers.users.model', Admin::class);

        $token = JWTAuth::fromUser($admin);

        return response()->json([
            'admin' => $admin,
            'token' => $token
        ]);

    


我在某个地方错了吗? 有什么解决办法吗?

更新:

为了确定 MongoDB 的功能,我使用关系数据库(实际上是 mysql)测试了上述所有操作。没有任何改变!

JWTAuth 生成令牌,但是当我使用除 User 之外的任何模型运行 toUser 方法时,它返回 null!

我们将不胜感激任何解决方案。

【问题讨论】:

你试过$admin = Admin::create(...吗?恐怕Admin::first() 返回正确的用户。 糟糕!这只是一个测试。我首先使用的正确代码是$admin = Admin::creat...。我会编辑这个。感谢您的通知! @revo 好吧,顺便说一句,因为我不确定jwt,我鼓励您为Admin 模型使用完全限定名称。请将这两行 Config::set('jwt.user' , "App\Admin"); Config::set('auth.providers.users.model', \App\Admin::class); 替换为旧的并重新检查。 旧的是什么意思? @revo 我的意思是 old 我的意思是 current 【参考方案1】:

您应该只使用一个模型(实际上是表)进行身份验证。当您保存用户和管理员时,您可以处理它。但是当用户使用 jwt 令牌请求时,您不知道会返回哪个模型(Admin 或 User)?

仅使用 User 模型进行身份验证,Admin 模型从 User 扩展而来。

像这样重新设计数据库:

users 表:id、email、密码、is_admin

user_details 表:id、user_id、first_name、last_name、city_abbr、phone

admin_details 表:id、user_id、姓名、电话

将此作为您的 Admin 模型来覆盖所有查询:

protected $table = "users";

public function newQuery()

    return parent::newQuery()
        ->where("is_admin", true);

【讨论】:

你说我使用角色,但它是正确的方式吗?我已经了解角色,但在处理大数据的大型项目(如我的)中,它会导致一些滞后,而且正如我在问题顶部提到的那样,我有 3 种可验证的模型,每种模型至少有两种访问类型。我想更多地了解这种类型的身份验证处理(角色)的优点和缺点。这是唯一的方法吗?并注意可以在关系数据库中使用多模型处理多身份验证。 而且在你说的这种类型中,令牌对每个模型都有效! 我要说明的是,您需要使用单个模型(表)来验证 jwt 会话。例如,用户想要列出产品并使用 JWT 发出请求。您将为“auth.providers.users.model”管理员或用户或其他设置使用哪个设置?除了 jwt 令牌,您什么都不知道,您应该可以验证它。您可以通过单个模型(表)使 JWT 有效,然后从电子邮件等基本信息中获取模型。 我把正确答案写在下面,你可以阅读一下。但我的问题奖将是你的。最好的,arkadaş ;)【参考方案2】:

这是您必须为我的项目添加 JWT 的多身份验证功能。

    在tymon JWT auth 包中。在JWTAuthServiceProvider 中,在bootBindings 方法中将Tymon\JWTAuth\JWTAuthTymon\JWTAuth\Providers\User\UserInterface 定义类型从singleton 更改为bind

    定义了一个新的中间件,下面的代码是它的handle方法:

    public function handle($request, Closure $next)
            if (!$request->header('Auth-Type')) 
                return response()->json([
                    'success' => 0,
                    'result' => 'auth type could not found!'
                ]);
            
        switch ($request->header('Auth-Type')) 
        case 'user':
            $auth_class = 'App\User';
            break;
    
        case 'admin':
            $auth_class = 'App\Admin';
            break;
    
        case 'provider':
            $auth_class = 'App\ServiceProvider';
            break;
    
        default:
            $auth_class = 'App\User';
    
    
    
    if (!Helpers::modifyJWTAuthUser($auth_class))
        return response()->json([
            'status' => 0,
            'error' => 'provider not found!'
        ]);
    
    return $next($request);    
    

    Helpers 中定义了一个名为modifyJWTAuthUser 的函数,这是它的内部:

    public static function modifyJWTAuthUser($user_class)
    if (!$user_class ||
        (
            $user_class != 'App\User' &&
            $user_class != 'App\Admin' &&
            $user_class != 'App\ServiceProvider'
        ))
        return false;
    
    try 
        Config::set('jwt.user', $user_class);
        Config::set('auth.providers.users.model', $user_class);
    
        app()->make('tymon.jwt.provider.user');
        return true;
     catch (\Exception $e) 
        return false;
        
    

    Kernel.php 中引入了另一个$routeMiddleware,如下所示:

    ... 'modify.jwt.auth.user' => ChangeJWTAuthUser::class,

    最后一步,将'modify.jwt.auth.user' 中间件添加到您想要的路由中。

但即使有了这些步骤,您一定遇到了新问题。它是关于通过登录凭据获取 auth 令牌 并从令牌中获取 auth 用户。 (似乎更改配置值不会影响JWTAuth::attempt($credentials)$this->auth->authenticate($token)

解决从令牌获取授权用户的问题:

创建一个新的中间件CustomGetUserFromTokenwhich extends of Tymon'sjwt.authmiddleware, I meanGetUserFromTokenand in line 35, and **replace**$user = $this->auth->authenticate($token);with$user = JWTAuth::toUser ($token);`

并解决在登录问题中通过凭据获取身份验证令牌

首先,找到auth用户,然后用Hash::check()方法检查用户是否存在并验证密码,如果这些条件返回true,则从用户那里生成一个令牌。这是登录代码:

$admin = Admin::where('email', $request->email)->first();
if (!$admin || !Hash::check($request->get('password'), $admin->password)) 
    return response()->json([
        'success' => '0',
        'error' => 'invalid_credentials'
    ], 401);

我不确定这种方式,但我认为在找到正确的方法之前它是正确的!

结论:

在 Laravel 中拥有多 JWT 身份验证功能可能还有很多其他方法可以做,但我确实喜欢这个并分享它以提供帮助。

我认为这个问题唯一重要的一点是app()->make('tymon.jwt.provider.user');,能够在配置值更改后重新制作用户提供程序。

任何其他解决方案将不胜感激。

【讨论】:

以上是关于使用 Laravel 和 Mongodb 进行多 Jwt 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 和 MongoDB 以及高级角色/权限系统

使用 Laravel 播种 MongoDB 时的 authenticationDatabase 错误

laravel 测试中认证失败

laravel中mysql和mongodb的关系

如何在 laravel 5.8 或 6 中制作通用自定义包进行身份验证。*

使用 select2 和 ajax laravel 资源进行多选