初始化项目
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class AuthCheckMiddleware
|
||||
{
|
||||
/**
|
||||
* 处理传入请求
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @param string|null $guard 认证守卫名称(默认为 api)
|
||||
* @param string|null $permission 需要检查的权限编码(可选)
|
||||
* @return Response
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, ?string $guard = 'api', ?string $permission = null): Response
|
||||
{
|
||||
// 检查是否已认证
|
||||
if (!Auth::guard($guard)->check()) {
|
||||
return response()->json([
|
||||
'code' => 401,
|
||||
'message' => '未登录或token已过期',
|
||||
'data' => null,
|
||||
], 401);
|
||||
}
|
||||
|
||||
// 获取当前用户
|
||||
$user = Auth::guard($guard)->user();
|
||||
|
||||
// 检查用户状态
|
||||
if (isset($user->status) && $user->status !== 1) {
|
||||
return response()->json([
|
||||
'code' => 403,
|
||||
'message' => '账号已被禁用',
|
||||
'data' => null,
|
||||
], 403);
|
||||
}
|
||||
|
||||
// 如果需要检查权限
|
||||
if ($permission !== null) {
|
||||
if (!$this->checkPermission($user, $permission, $guard)) {
|
||||
return response()->json([
|
||||
'code' => 403,
|
||||
'message' => '无权限访问',
|
||||
'data' => null,
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
|
||||
// 将用户信息添加到请求中
|
||||
$request->merge(['auth_user' => $user]);
|
||||
|
||||
// 更新用户最后活跃时间
|
||||
if (method_exists($user, 'updateLastActiveAt')) {
|
||||
$user->updateLastActiveAt();
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户权限
|
||||
*
|
||||
* @param mixed $user
|
||||
* @param string $permission 权限编码
|
||||
* @param string $guard 认证守卫
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkPermission($user, string $permission, string $guard): bool
|
||||
{
|
||||
// 如果用户有所有权限标识
|
||||
if (method_exists($user, 'hasAllPermissions') && $user->hasAllPermissions()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查用户是否有指定权限
|
||||
if (method_exists($user, 'hasPermission')) {
|
||||
return $user->hasPermission($permission);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查多个权限(满足任意一个即可)
|
||||
*
|
||||
* @param array $permissions 权限编码数组
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkAnyPermission($user, array $permissions): bool
|
||||
{
|
||||
foreach ($permissions as $permission) {
|
||||
if ($this->checkPermission($user, $permission, 'api')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查多个权限(必须全部满足)
|
||||
*
|
||||
* @param array $permissions 权限编码数组
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkAllPermissions($user, array $permissions): bool
|
||||
{
|
||||
foreach ($permissions as $permission) {
|
||||
if (!$this->checkPermission($user, $permission, 'api')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log as LaravelLog;
|
||||
use App\Services\System\LogService;
|
||||
use Throwable;
|
||||
|
||||
class LogRequestMiddleware
|
||||
{
|
||||
protected $logService;
|
||||
|
||||
public function __construct(LogService $logService)
|
||||
{
|
||||
$this->logService = $logService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$startTime = microtime(true);
|
||||
|
||||
$response = $next($request);
|
||||
|
||||
$endTime = microtime(true);
|
||||
$executionTime = round(($endTime - $startTime) * 1000, 2); // 转换为毫秒
|
||||
|
||||
// 异步记录日志,不影响响应速度
|
||||
$this->logRequest($request, $response, $executionTime);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录请求日志
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Http\Response $response
|
||||
* @param float $executionTime
|
||||
* @return void
|
||||
*/
|
||||
protected function logRequest(Request $request, $response, float $executionTime): void
|
||||
{
|
||||
try {
|
||||
// 获取当前用户信息
|
||||
$user = Auth::guard('admin')->user();
|
||||
$userId = $user ? $user->id : null;
|
||||
$username = $user ? $user->username : 'guest';
|
||||
|
||||
// 解析模块和操作
|
||||
$module = $this->parseModule($request->path());
|
||||
$action = $this->parseAction($request->method(), $request->path());
|
||||
|
||||
// 获取请求参数(排除敏感信息)
|
||||
$params = $this->sanitizeParams($request->all());
|
||||
|
||||
// 获取响应数据
|
||||
$result = null;
|
||||
if ($response->getStatusCode() >= 400) {
|
||||
$result = $response->getContent();
|
||||
}
|
||||
|
||||
// 确定日志状态
|
||||
$status = $response->getStatusCode() < 400 ? 'success' : 'error';
|
||||
$errorMessage = null;
|
||||
if ($status === 'error') {
|
||||
$errorMessage = $this->extractErrorMessage($result);
|
||||
}
|
||||
|
||||
// 构建日志数据
|
||||
$logData = [
|
||||
'user_id' => $userId,
|
||||
'username' => $username,
|
||||
'module' => $module,
|
||||
'action' => $action,
|
||||
'method' => $request->method(),
|
||||
'url' => $request->fullUrl(),
|
||||
'ip' => $this->getClientIp($request),
|
||||
'user_agent' => $request->userAgent(),
|
||||
'params' => $params,
|
||||
'result' => $result,
|
||||
'status_code' => $response->getStatusCode(),
|
||||
'status' => $status,
|
||||
'error_message' => $errorMessage,
|
||||
'execution_time' => $executionTime,
|
||||
];
|
||||
|
||||
// 记录到数据库
|
||||
$this->logService->create($logData);
|
||||
|
||||
// 同时记录到 Laravel 日志(用于错误)
|
||||
if ($status === 'error') {
|
||||
LaravelLog::error('API Request Error', [
|
||||
'url' => $request->fullUrl(),
|
||||
'method' => $request->method(),
|
||||
'user_id' => $userId,
|
||||
'error' => $errorMessage,
|
||||
]);
|
||||
}
|
||||
|
||||
} catch (Throwable $e) {
|
||||
// 记录日志失败不影响业务流程
|
||||
LaravelLog::error('Log request failed', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析模块名称
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
protected function parseModule(string $path): string
|
||||
{
|
||||
$segments = explode('/', trim($path, '/'));
|
||||
|
||||
// 移除前缀(如 admin, api)
|
||||
$prefixes = ['admin', 'api'];
|
||||
if (in_array($segments[0], $prefixes)) {
|
||||
array_shift($segments);
|
||||
}
|
||||
|
||||
return $segments[0] ?? 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析操作名称
|
||||
*
|
||||
* @param string $method
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
protected function parseAction(string $method, string $path): string
|
||||
{
|
||||
$segments = explode('/', trim($path, '/'));
|
||||
|
||||
// 获取资源名称
|
||||
$resource = end($segments);
|
||||
|
||||
// 如果资源是 ID,则取前一个作为资源名
|
||||
if (is_numeric($resource)) {
|
||||
$resource = prev($segments);
|
||||
}
|
||||
|
||||
// 根据方法映射操作
|
||||
$actionMap = [
|
||||
'GET' => '查询',
|
||||
'POST' => '创建',
|
||||
'PUT' => '更新',
|
||||
'PATCH' => '更新',
|
||||
'DELETE' => '删除',
|
||||
];
|
||||
|
||||
$action = $actionMap[$method] ?? '操作';
|
||||
|
||||
return $action . ' ' . $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理敏感参数
|
||||
*
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
protected function sanitizeParams(array $params): array
|
||||
{
|
||||
$sensitiveKeys = ['password', 'password_confirmation', 'token', 'secret', 'key'];
|
||||
|
||||
array_walk_recursive($params, function (&$value, $key) use ($sensitiveKeys) {
|
||||
if (in_array(strtolower($key), $sensitiveKeys)) {
|
||||
$value = '******';
|
||||
}
|
||||
});
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取错误信息
|
||||
*
|
||||
* @param string|null $content
|
||||
* @return string|null
|
||||
*/
|
||||
protected function extractErrorMessage(?string $content): ?string
|
||||
{
|
||||
if (empty($content)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = json_decode($content, true);
|
||||
if (isset($data['message'])) {
|
||||
return $data['message'];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// JSON 解析失败,返回原始内容
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端 IP
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientIp(Request $request): string
|
||||
{
|
||||
$ip = $request->ip();
|
||||
|
||||
// 检查代理头
|
||||
$headers = [
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_CLIENT_IP',
|
||||
'HTTP_X_REAL_IP',
|
||||
'HTTP_CF_CONNECTING_IP',
|
||||
];
|
||||
|
||||
foreach ($headers as $header) {
|
||||
if ($request->hasHeader($header)) {
|
||||
$forwardedIps = explode(',', $request->header($header));
|
||||
$ip = trim($forwardedIps[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user