使用带有子域的“auth”中间件

Posted

技术标签:

【中文标题】使用带有子域的“auth”中间件【英文标题】:Using 'auth' middleware with subdomains 【发布时间】:2017-09-21 14:54:51 【问题描述】:

我正在尝试通过身份验证中间件在我的控制器中传递某些功能。但是,与这些功能关联的路由需要 URL 中的子域才能工作。当函数没有通过中间件或通过定制的中间件传递时,这些函数似乎可以工作,但是“auth”中间件会抛出错误:

UrlGenerationException.php 第 17 行中的 UrlGenerationException: [Route: login] [URI: login] 缺少必需的参数。

这与我尝试在参数列表中没有 $account 变量的情况下运行函数时引发的错误类似(因为它需要一个带有路由的子域)。

在深入研究“auth”中间件调用的 Authenticate 类后,我发现导致错误的行位于 public function authenticate(array $guards) 中,如下所示。

protected function authenticate(array $guards)


    if (empty($guards)) 
        /** ANYTHING BEFORE THIS RETURN STATEMENT RUNS WITH NO ERRORS **/
        return $this->auth->authenticate();
    

    foreach ($guards as $guard) 
        if ($this->auth->guard($guard)->check()) 
            return $this->auth->shouldUse($guard);
        
    

    throw new AuthenticationException('Unauthenticated.', $guards);

这是我的一些代码供参考(仅包含代码的相关sn-ps):

路由文件

路由\web.php

Route::group(['domain' => 'account.'.env('APP_URL')], function () 
  include('appRoutes/bcm.php');
);

路由\appRoutes\bcm.php

/********************************************/
/*********STATIC PAGE ROUTES******************/
/********************************************/

Route::get('/boards-commissions', 'BCM\PagesController@home');


/********************************************/
/*********BOARD ROUTES***********************/
/********************************************/

Route::get('/boards-commissions/boards', 'BCM\BoardsController@index');

Route::get('/boards-commissions/boards/archive/index', 
'BCM\BoardsController@archiveIndex');

控制器

app\Http\Controllers\BCM\BoardsController.php

namespace App\Http\Controllers\BCM;

use Auth;
use Session;
use Validator;

use Illuminate\Http\Request;
use Illuminate\Foundation\Validation\ValidatesRequests;

//Models
use App\Models\BCM\Board;

class BoardsController extends Controller

    public function __construct()
    
        $this->middleware('bcm_app_access');
        $this->middleware('auth', ['except' => array('index', 'show')]);
        $this->middleware('bcm_admin', ['only' => array('edit', 'update', 
'create', 'store', 'archive', 'showArchive', 'showArchivedBoard', 'restore')]);
    

    public function index($account, Request $request) 
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.index')->with('boards', $boards)->with('user', $user);
    

    public function archiveIndex($account, Request $request) 
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.archiveList')->with('boards', $boards)->with('user', $user);
    

目前看来只有 auth 中间件不起作用。 index 函数运行得很好(因为它在控制器的构造中被排除在 auth 中间件之外),即使它是通过 bcm_app_access 中间件传递的。但是archiveIndex在通过auth中间件的时候会抛出上面提到的UrlGenerationException错误。

我感觉需要在某处添加或包含 $account 参数,以便 auth 中间件知道它需要引用它,但我不确定在哪里添加它,我不知道还有什么$this->auth->authenticate(); 时正在引用文件正在运行,所以我不知道在哪里添加。有什么想法吗?

【问题讨论】:

有完全相同的问题。我什至尝试过使用forgetParameter。你弄明白了吗? 我仍然无法弄清楚如何使默认的身份验证中间件正常工作。我现在解决的工作包括制作一个新的中间件来代替 auth 中间件来执行相同的功能(即检查是否有有效用户登录。)我使用了 Auth: :查看();在其他一些代码中。如果可能,我仍然想知道如何将默认身份验证中间件调整到子域。 我也有同样的问题!标准的身份验证中间件不适用于子域!我得到了无限循环......@JakeKillpack 你能告诉我们你是如何为此创建自己的中间件的吗? 【参考方案1】:

在最初的问题中,我想知道如何让“auth”中间件与子域一起使用。我还没有想出这样做,但我已经通过创建一个基本的自定义中间件建立了一个功能解决方法。身份验证中间件的目的是检查站点的用户是否已登录或以访客身份运行。为了模仿这个功能,我通过运行创建了一个中间件

php artisan make:middleware SubdomainAuth

在命令行中。然后我将自动生成的中间件文件配置为如下所示:

<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class SubdomainAuth

    /**
     * Handle an incoming request to make sure that a valid user is logged in.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next) 
        $user = Auth::user();
        if ($user == null) 
          return redirect('/login');
        
        return $next($request);
    

最后,我通过添加行配置我的内核以识别新的中间件

'sub_auth' => \App\Http\Middleware\SubdomainAuth::class,

到 app/Http/Kernel.php。从那里我可以用我创建的 sub_auth 中间件替换我的控制器中的 auth 中间件。我上面问题的例子现在看起来像这样:

namespace App\Http\Controllers\BCM;

use Auth;
use Session;
use Validator;

use Illuminate\Http\Request;
use Illuminate\Foundation\Validation\ValidatesRequests;

//Models
use App\Models\BCM\Board;

class BoardsController extends Controller

    public function __construct()
    
        $this->middleware('bcm_app_access');
        $this->middleware('sub_auth', ['except' => array('index', 'show')]);
        $this->middleware('bcm_admin', ['only' => array('edit', 'update', 
'create', 'store', 'archive', 'showArchive', 'showArchivedBoard', 'restore')]);
    

    public function index($account, Request $request) 
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.index')->with('boards', $boards)->with('user', $user);
    

    public function archiveIndex($account, Request $request) 
        $boards = Board::all();
        $user = $request->user();
        return view('bcm.boards.archiveList')->with('boards', $boards)->with('user', $user);
    

此解决方法成功筛选了未登录的用户并将他们重定向到登录页面。但是,我不太熟悉 Authenticate 中间件的工作原理,无法知道我创建的 SubdomainAuth 类中是否缺少我的自定义类的任何附加安全性。

【讨论】:

@mesqueeb 给你。【参考方案2】:

我也遇到过同样的问题。这是我的解决方案:

Laravel 的 Auth 中间件使用其默认路由('login')处理它,该路由位于 \Illuminate\Foundation\Exceptions\Handler.php

protected function unauthenticated($request, AuthenticationException $exception)

    return $request->expectsJson()
                ? response()->json(['message' => $exception->getMessage()], 401)
                : redirect()->guest(route('login'));

您可以在此处修改以更改 route('login', 'subdomain') 以使其正常工作。但就个人而言,我想保持框架不变。我修改了异常处理程序以覆盖“unauthenticated”函数。

在\app\Exceptions\Handler.php中添加一个新函数

protected function unauthenticated($request, AuthenticationException $exception)

    $subdomain = explode("//",explode('.', $request->url())[0])[1];
    return $request->expectsJson()
                ? response()->json(['message' => $exception->getMessage()], 401)
                : redirect()->guest(route('login',$subdomain));

我已经使用 $request 来获取 url 并提取它。我认为如果您可以更深入地研究 userResolver,您将能够提取在您的 routes/web.php 中定义的子域变量,但我现在对此很满意。

【讨论】:

【参考方案3】:

一个粗略的 laravel7 指南:

子域的通配符参数brand

Route::domain( 'brand.' . parse_url(config('app.url'), PHP_URL_HOST) )->group(function () 

    // login page
    Route::get('/login', 'Auth\LoginController@showLoginForm')->name('login')->middleware('guest');

    Route::middleware('auth')->group(function () 
        Route::get('/', 'BrandController@showDashboard')->name('dashboard');
        
        ...
    
);

可以在$request 对象中访问,所以在:

// app\Http\Middleware\Authenticate.php

protected function redirectTo($request)

    if (! $request->expectsJson()) 
        return route('login', $request->brand);
    

只需将通配符作为参数传递给路由函数

【讨论】:

以上是关于使用带有子域的“auth”中间件的主要内容,如果未能解决你的问题,请参考以下文章

带有访客和身份验证中间件的 Nuxt 身份验证将经过身份验证的用户在刷新时重定向到错误的页面

将 redux-devtools 传递给带有中间件的 redux 存储

Express.js 虚拟主机子域设置

Laravel 中间件“除外”规则不起作用

python Django子域中间件

使用 Laravel Auth 中间件