You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Forge多域名单代码库:Laravel多租户数据隔离方案咨询

Nice one! I’ve built exactly this kind of multi-tenant SaaS setup with Laravel (API backend) + Vue frontend, so let me break down the actionable steps to make this work with a single codebase, single database, and custom domain support.

1. Domain Setup & Laravel Forge Configuration

First, let’s get the infrastructure sorted:

  • Customer Domain Setup: Have your clients point their domain’s A record (or CNAME if using a CDN) to your Laravel Forge server’s public IP.
  • Forge Site Configuration: In Forge, add each customer’s domain to your existing Laravel project site (not a new site!). Ensure the document root still points to your project’s public directory. Enable Let’s Encrypt for each domain to enforce HTTPS—Forge handles this automatically once the DNS propagates.
  • Nginx Tweaks: Forge will auto-configure Nginx to serve all domains, but double-check that the server block includes a wildcard or explicit list of allowed domains (or just let it catch all, since your backend will validate legitimacy).

2. Tenant Identification & Middleware

You need a way to map incoming domains to a tenant in your database. Here’s how to implement it:

Database Setup

Create two core tables:

  • tenants: Stores tenant metadata (id, name, slug, created_at, etc.)
  • tenant_domains: Maps domains to tenants (id, tenant_id, domain, primary flag for default domain)

Tenant Identification Middleware

Build a middleware to detect the tenant on every request and make it globally accessible:

// app/Http/Middleware/IdentifyTenant.php
namespace App\Http\Middleware;

use Closure;
use App\Models\Tenant;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class IdentifyTenant
{
    public function handle(Request $request, Closure $next)
    {
        $host = strtolower($request->getHost());
        
        // Skip your main app domain or local dev domains
        if (in_array($host, config('app.ignore_domains'))) {
            return $next($request);
        }

        // Cache tenant lookup to avoid repeated DB hits (1 hour TTL)
        $tenant = cache()->remember("tenant:{$host}", 3600, function () use ($host) {
            return Tenant::whereHas('domains', fn($q) => $q->where('domain', $host))->first();
        });

        if (!$tenant) {
            throw new NotFoundHttpException('Domain not registered');
        }

        // Bind tenant to the app container for global access
        app()->instance('currentTenant', $tenant);

        return $next($request);
    }
}

Register this middleware in app/Http/Kernel.php (add to web and api middleware groups).

3. Database Data Isolation

Ensure all tenant-specific data is filtered automatically:

Global Query Scope

Add a scope to all tenant-aware models to enforce tenant_id filtering:

// app/Scopes/TenantScope.php
namespace App\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        if (app()->has('currentTenant')) {
            $builder->where('tenant_id', app('currentTenant')->id);
        }
    }
}

Attach this scope to models like Car, Customer, User:

// app/Models/Car.php
protected static function booted()
{
    static::addGlobalScope(new TenantScope);
}

Migration Updates

Add a tenant_id foreign key to all tenant-specific tables:

Schema::table('cars', function (Blueprint $table) {
    $table->foreignId('tenant_id')->constrained()->onDelete('cascade');
});

4. Vue Frontend Adaptation

Since your backend handles tenant isolation, the frontend just needs to:

  • Fetch tenant-specific config (branding, settings) on initialization
  • Use that config to customize the UI

Example Tenant Config API

Add an endpoint in Laravel to return tenant settings:

// routes/api.php
Route::get('/tenant/config', function () {
    return response()->json([
        'name' => app('currentTenant')->name,
        'logo_url' => app('currentTenant')->logo_url,
        'primary_color' => app('currentTenant')->primary_color,
    ]);
});

Vue Store Integration

Use Pinia (or Vuex) to store tenant config and fetch it on app load:

// src/stores/tenant.js
import { defineStore } from 'pinia';

export const useTenantStore = defineStore('tenant', {
  state: () => ({
    config: null
  }),
  actions: {
    async fetchConfig() {
      try {
        const res = await fetch('/api/tenant/config');
        this.config = await res.json();
      } catch (err) {
        console.error('Failed to load tenant config:', err);
      }
    }
  }
});

Call fetchConfig() in your root App.vue component’s onMounted hook.

5. Critical Security & Optimization Tips

  • Never Trust Frontend: All data filtering must happen on the backend—never rely on frontend logic to restrict access.
  • User-Tenant Validation: Ensure authenticated users belong to the current tenant. Add a gate in AuthServiceProvider:
    Gate::define('access-tenant', fn($user) => $user->tenant_id === app('currentTenant')->id);
    
  • Cache Wisely: Use tenant-specific cache keys (e.g., cache()->get("tenant:{$tenantId}:cars")) to avoid cross-tenant cache leaks.
  • HSTS Headers: Enable HSTS in Forge to force HTTPS and prevent domain hijacking.

This setup is scalable, low-maintenance, and aligns perfectly with your Laravel+Vue stack. I’ve used it for 100+ tenants without issues!

内容的提问来源于stack exchange,提问作者Lovelock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:54:52