多个模型如何认证共用 REST API?

1. 运行环境

1). 当前使用的 Laravel 版本?

Laravel Framework 8.83.27

2). 当前使用的 php/php-fpm 版本?

使用 docker 运行环境
PHP 版本:
PHP 7.3.33 (cli) (built: Mar 18 2022 04:33:02) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.33, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.33, Copyright (c) 1999-2018, by Zend Technologies
with Zend OPcache v7.3.33, Copyright (c) 1999-2018, by Zend Technologies

php-fpm 版本:
PHP 7.3.33 (fpm-fcgi) (built: Mar 18 2022 04:33:13)
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.33, Copyright (c) 1998-2018 Zend Technologies
with Zend OPcache v7.3.33, Copyright (c) 1999-2018, by Zend Technologies

3). 当前系统

macOS 12.7.6

4). 业务环境

开发环境

5). 相关软件版本

nginx version: nginx/1.27.0
mysql Ver 8.3.0 for macos12.6 on x86_64 (Homebrew)

2. 问题描述?

在一个系统中,有2个角色:学生和老师,分别对应 student 表和 teacher 表。有一个文章模块接口分别供学生和老师使用。
路由配置:

Route::middleware('auth:student')->prefix('v1')->group(function() {
    Route::get('student/article/list', 'ArticleController@list');
    Route::post('student/article/upsert', 'ArticleController@upsert');
    Route::get('student/article/detail/{id}', 'ArticleController@detail');
    Route::post('student/article/delete', 'ArticleController@delete');
});
Route::middleware('auth:teacher')->prefix('v1')->group(function() {
    Route::get('teacher/article/list', 'ArticleController@list');
    Route::post('teacher/article/upsert', 'ArticleController@upsert');
    Route::get('teacher/article/detail/{id}', 'ArticleController@detail');
    Route::post('teacher/article/delete', 'ArticleController@delete');
});

auth.php 配置

return [
    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],
    'guards' => [
        'student' => [
            'driver' => 'jwt',
            'provider' => 'student'
        ],
        'teacher' => [
            'driver' => 'jwt',
            'provider' => 'teacher',
            'hash' => true,
        ],
    ],
    'providers' => [
        'student' => [
            'driver' => 'eloquent',
            'model' => App\Student::class,
        ],
        'teacher' => [
            'driver' => 'eloquent',
            'model' => App\Teacher::class,
        ],
    ],
];

ArticleController 通过 Auth::user()可以获取当前用户的对象 App\StudentApp\Teacher

3. 您期望得到的结果?

文章接口是重复的,现在想合并 2 个分组的接口如下。 用户认证如何配置?

 Route::middleware('auth:xxxx')->prefix('v1')->group(function() {
    Route::get('/article/list', 'ArticleController@list');
    Route::post('/article/upsert', 'ArticleController@upsert');
    Route::get('/article/detail/{id}', 'ArticleController@detail');
    Route::post('/article/delete', 'ArticleController@delete');
});

4. 您实际得到的结果?

一种实现方法:
routes.php

 Route::middleware('auth:common_user')->prefix('v1')->group(function() {
    Route::get('/article/list', 'ArticleController@list');
    Route::post('/article/upsert', 'ArticleController@upsert');
    Route::get('/article/detail/{id}', 'ArticleController@detail');
    Route::post('/article/delete', 'ArticleController@delete');
});

auth.php

 return [
 'guards' => [
        'student' => [
            'driver' => 'jwt',
            'provider' => 'student'
        ],
        'teacher' => [
            'driver' => 'jwt',
            'provider' => 'teacher',
            'hash' => true,
        ],
        'common_user' => [
            'driver' => 'common_user',
            'provider' => 'common_user',
            'hash' => true
        ],
    ],
    'providers' => [
        'student' => [
            'driver' => 'eloquent',
            'model' => App\Student::class,
        ],
        'teacher' => [
            'driver' => 'eloquent',
            'model' => App\Teacher::class,
        ],
    ];
 ];

AppServiceProvider.php

 use Illuminate\Support\ServiceProvider;
 use Illuminate\Support\Facades\Auth;
 class AppServiceProvider extends ServiceProvider
 {
     public function boot()
    {
        // driver common_user 实现
        Auth::extend('common_user', function($app, $name, $config){
            // 解析 token,判断当前用户类型
            $configProvider = $config['provider'];
            swtich(用户类型) {
                case 'student':
                    $configProvider = 'student';
                    break;
                case 'teacher':
                    $configProvider = 'teacher';
                    break;
            }
            // 使用了 tymon/jwt-auth 库,下面的代码可以在源码中找到,这里主要是替换了 configProvider
            $provider = $app['auth']->createUserProvider($configProvider);
            $guard = new JWTGuard($app['tymon.jwt'], $provider, $app['request']);
            $app->refresh('request', $guard, 'setRequest');
            return $guard;
        });
    }
 }
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5

大概思路也是这样,根据路由,teacher可以前置,判断是学生还是教师,更改默认的配置项即可。

3周前 评论

你这设计有点乱啊 感觉应该 都是用户 然后身份不一样

3周前 评论

你这个就像 Student Teacher 都属于common_user

那和 UserModel id 32 type teacher, UserModel id 33 type student 有啥区别…

2周前 评论

直接写一个中间件就好了
对于老师和学生,应该都实现了Authenticatable接口,中间件参数使用这个。
中间件参数写起来麻烦,直接放在枚举中,反正就是3个值,老师,学生和所有

class AuthenticateUser
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     *
     * @throws AuthenticationException
     */
    public function handle(Request $request, Closure $next, string $userModelClass = User::class): Response
    {
        $user = $request->user('sanctum');

        if ($user instanceof $userModelClass) {
            return $next($request);
        } else {
            throw new AuthenticationException;
        }
    }
}
1周前 评论

多用户体系共存?还用是同一套JWT,涉及到业务鉴权,处理起来比较麻烦。

1周前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!