1. 實作多租戶專屬自訂登入頁面:支援 `/c/{company_code}/login` 特殊入口,自動載入租戶專屬 Logo 網址、名稱與歡迎詞。
2. 支援客製化品牌視覺設定:於客戶管理清單中,提供 SaaS 管理者可自訂租戶 Logo 圖片與歡迎副標題,且系統後台 Header/Sidebar 與登入頁能動態依該配置載入租戶專屬商標。
3. 擴充多語系字典並完成三檔對齊:新增 "Upload Time" 欄位與翻譯,執行 `ksort` 以維持 zh_TW、en 與 ja 語系檔對齊。
4. APK 版本清單增加上傳時間欄位:於 APK 版本表格中呈現上傳時間(created_at),使用 font-mono 奢華風小尺寸樣式呈現。
98 lines
3.4 KiB
PHP
98 lines
3.4 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Auth;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Models\System\Company;
|
||
use App\Models\System\User;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Auth;
|
||
use Illuminate\Support\Facades\Hash;
|
||
use Illuminate\Support\Facades\Log;
|
||
|
||
class TenantLoginController extends Controller
|
||
{
|
||
/**
|
||
* 顯示專屬客戶的客製化登入頁面
|
||
*/
|
||
public function showLoginForm($company_code)
|
||
{
|
||
// 1. 查詢啟用的公司
|
||
$company = Company::active()->where('code', $company_code)->first();
|
||
|
||
// 2. 安全防護:若公司不存在,或沒有啟用品牌客製化功能授權,一律自動重導向至通用登入頁
|
||
if (!$company) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$settings = $company->settings ?? [];
|
||
$enableCustomBranding = filter_var($settings['enable_custom_branding'] ?? false, FILTER_VALIDATE_BOOLEAN);
|
||
|
||
if (!$enableCustomBranding) {
|
||
Log::warning("Tenant login attempted for unauthorized company [{$company_code}]. Redirecting to main login.");
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
// 3. 渲染共用的 login Blade,傳入當前公司的上下文
|
||
return view('auth.login', compact('company'));
|
||
}
|
||
|
||
/**
|
||
* 處理專屬客戶的登入請求 (安全性加強版)
|
||
*/
|
||
public function login($company_code, Request $request)
|
||
{
|
||
// 1. 查詢並確認該公司
|
||
$company = Company::active()->where('code', $company_code)->first();
|
||
if (!$company) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
$settings = $company->settings ?? [];
|
||
$enableCustomBranding = filter_var($settings['enable_custom_branding'] ?? false, FILTER_VALIDATE_BOOLEAN);
|
||
if (!$enableCustomBranding) {
|
||
return redirect()->route('login');
|
||
}
|
||
|
||
// 2. 驗證輸入欄位
|
||
$request->validate([
|
||
'username' => 'required|string',
|
||
'password' => 'required|string',
|
||
]);
|
||
|
||
// 3. 查詢使用者
|
||
$user = User::where('username', $request->username)->first();
|
||
|
||
// 4. 【關鍵安全防錯】驗證此使用者是否確實屬於該公司
|
||
if (!$user || $user->company_id !== $company->id) {
|
||
Log::warning("Tenant login security violation: User [{$request->username}] attempted login at company [{$company_code}] portal.");
|
||
|
||
return back()->withErrors([
|
||
'username' => __('This account does not belong to this company.'),
|
||
])->withInput($request->only('username', 'remember'));
|
||
}
|
||
|
||
// 5. 驗證帳號狀態
|
||
if ($user->status !== 1) {
|
||
return back()->withErrors([
|
||
'username' => __('Your account is disabled.'),
|
||
])->withInput($request->only('username', 'remember'));
|
||
}
|
||
|
||
// 6. 驗證密碼
|
||
if (!Hash::check($request->password, $user->password)) {
|
||
return back()->withErrors([
|
||
'password' => __('auth.failed'),
|
||
])->withInput($request->only('username', 'remember'));
|
||
}
|
||
|
||
// 7. 執行登入與安全 Session 重新生成
|
||
Auth::login($user, $request->boolean('remember'));
|
||
$request->session()->regenerate();
|
||
|
||
Log::info("User [{$user->username}] successfully logged in via company portal [{$company_code}].");
|
||
|
||
return redirect()->intended(route('admin.dashboard'));
|
||
}
|
||
}
|