初始化
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class Admin
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (! $request->user()) {
|
||||
return redirect()->route('admin.login');
|
||||
}
|
||||
|
||||
if (! $request->user()->isAdmin()) {
|
||||
abort(403, '需要管理员权限');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* 权限点门禁:can:articles.moderate
|
||||
* 传入多个权限点时,拥有任一即通过(OR 语义)。
|
||||
*/
|
||||
class Can
|
||||
{
|
||||
public function handle(Request $request, Closure $next, string ...$permissions): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if (! $user) {
|
||||
abort(403, '需要登录');
|
||||
}
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
if ($user->canPerm($permission)) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
abort(403, '无访问权限');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* 角色门禁:role:super_admin,admin,forum_admin,staff
|
||||
* 用于「能否进入后台」这类按角色层级的判断。
|
||||
*/
|
||||
class Role
|
||||
{
|
||||
public function handle(Request $request, Closure $next, string ...$roles): Response
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if (! $user || ! in_array($user->role, $roles, true)) {
|
||||
abort(403, '无访问权限');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* 统一下发 HTTP 安全响应头(纵深防御)。
|
||||
*
|
||||
* 设计说明(零构建架构约束):
|
||||
* - 本站前台使用 Tailwind Play CDN(cdn.tailwindcss.com)与内联 tailwind.config,
|
||||
* 因此 script-src / style-src 必须放行该 CDN 与 'unsafe-inline'。
|
||||
* - 若日后将 Tailwind 改为构建产物并为内联脚本加 nonce,可移除 'unsafe-inline' 收紧 CSP。
|
||||
* - HSTS 仅在「生产环境 + 已启用 HTTPS」时下发,避免本地 http 下浏览器记忆错误。
|
||||
*/
|
||||
class SecurityHeaders
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
// 禁止 MIME 嗅探(防止被当作可执行内容)
|
||||
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||
|
||||
// 防点击劫持(现代浏览器看 frame-ancestors,旧浏览器看 X-Frame-Options)
|
||||
$response->headers->set('X-Frame-Options', 'DENY');
|
||||
|
||||
// Referrer 泄露控制
|
||||
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
|
||||
// 关闭不必要的浏览器敏感能力
|
||||
$response->headers->set(
|
||||
'Permissions-Policy',
|
||||
"geolocation=(), camera=(), microphone=(), payment=(), usb=(), interest-cohort=()"
|
||||
);
|
||||
|
||||
// 内容安全策略:限制脚本/样式/连接/表单来源,缩小 XSS 影响面
|
||||
$csp = implode('; ', [
|
||||
"default-src 'self'",
|
||||
"script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com",
|
||||
"style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com",
|
||||
"img-src 'self' data: https:",
|
||||
"font-src 'self' data:",
|
||||
"connect-src 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
]);
|
||||
$response->headers->set('Content-Security-Policy', $csp);
|
||||
|
||||
// HSTS:仅生产 + HTTPS 时下发
|
||||
if ($request->isSecure() && app()->environment('production')) {
|
||||
$response->headers->set(
|
||||
'Strict-Transport-Security',
|
||||
'max-age=31536000; includeSubDomains; preload'
|
||||
);
|
||||
}
|
||||
|
||||
// 禁止代理/浏览器缓存动态页面(含 CSRF token 的表单页)。
|
||||
// 否则 nginx/浏览器缓存的旧 HTML 会携带过期 _token,提交即触发 419 Page Expired。
|
||||
// 静态资源由 web 服务器直接服务、不经过本中间件,不受影响。
|
||||
$response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
|
||||
$response->headers->set('Pragma', 'no-cache');
|
||||
$response->headers->set('Expires', '0');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user