320 lines
9.8 KiB
PHP
320 lines
9.8 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Admin;
|
||
|
||
use App\Http\Controllers\BaseController;
|
||
use App\Models\User;
|
||
use App\Services\Auth\TokenAuthService;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Hash;
|
||
use Illuminate\Support\Facades\Validator;
|
||
use Illuminate\Http\JsonResponse;
|
||
|
||
/**
|
||
* 后台认证控制器 (支持Token认证)
|
||
* @package App\Http\Controllers\Admin
|
||
*/
|
||
class AuthController extends BaseController
|
||
{
|
||
/**
|
||
* Token认证服务
|
||
*/
|
||
protected TokenAuthService $tokenAuthService;
|
||
|
||
/**
|
||
* 构造函数
|
||
*/
|
||
public function __construct(TokenAuthService $tokenAuthService)
|
||
{
|
||
$this->tokenAuthService = $tokenAuthService;
|
||
}
|
||
/**
|
||
* 用户登录 (返回Token)
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function login(Request $request) : JsonResponse
|
||
{
|
||
// 验证请求数据
|
||
$validator = Validator::make($request->all(), [
|
||
'username' => ['required', 'string'],
|
||
'password' => ['required', 'string'],
|
||
'device_name' => ['string', 'nullable'] // 设备名称,用于Token标识
|
||
], [
|
||
'username.required' => '请输入用户名',
|
||
'password.required' => '请输入密码',
|
||
]);
|
||
|
||
if ($validator->fails()) {
|
||
return $this->Field($validator->errors()->first(), 422);
|
||
}
|
||
|
||
$credentials = $validator->validated();
|
||
|
||
// 手动验证用户
|
||
$user = User::where('username', $credentials['username'])
|
||
->where('status', 0) // 0表示正常状态,1表示停用
|
||
->where('deleted', 0) // 确保用户未被删除
|
||
->first();
|
||
|
||
if (!$user || !Hash::check($credentials['password'], $user->password)) {
|
||
return $this->Field('用户名或密码错误,或账户已被停用', 401);
|
||
}
|
||
|
||
// 设备名称,默认为APP
|
||
$deviceName = $credentials['device_name'] ?? 'APP-' . now()->format('Y-m-d H:i:s');
|
||
|
||
// 创建Token (删除该设备的旧Token,避免重复)
|
||
$user->tokens()->where('name', $deviceName)->delete();
|
||
$token = $user->createToken($deviceName);
|
||
|
||
// 更新最后登录信息
|
||
$user->update([
|
||
'login_ip' => $request->ip(),
|
||
'login_date' => now(),
|
||
]);
|
||
|
||
return $this->SuccessObject([
|
||
'user' => [
|
||
'id' => $user->id,
|
||
'username' => $user->username,
|
||
'nickname' => $user->nickname,
|
||
'email' => $user->email,
|
||
'mobile' => $user->mobile,
|
||
'avatar' => $user->avatar,
|
||
'dept_id' => $user->dept_id,
|
||
],
|
||
'token' => [
|
||
'access_token' => $token->plainTextToken,
|
||
'token_type' => 'Bearer',
|
||
'expires_in' => null, // Sanctum默认不过期,可以在config/sanctum.php配置
|
||
],
|
||
'message' => '登录成功'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* APP登出 (删除当前Token)
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function logout(Request $request) : JsonResponse
|
||
{
|
||
$user = $request->user();
|
||
$currentToken = $request->bearerToken();
|
||
|
||
// 清空当前token的缓存
|
||
if ($currentToken) {
|
||
$this->tokenAuthService->clearTokenCache($currentToken);
|
||
}
|
||
|
||
// 删除当前使用的token
|
||
$user->currentAccessToken()->delete();
|
||
|
||
return $this->Success(['message' => '登出成功']);
|
||
}
|
||
|
||
/**
|
||
* 登出所有设备 (删除用户所有Token)
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function logoutAll(Request $request) : JsonResponse
|
||
{
|
||
$user = $request->user();
|
||
|
||
// 清空用户所有token的缓存
|
||
$this->tokenAuthService->clearCacheOnLogout($user);
|
||
|
||
// 删除用户的所有token
|
||
$user->tokens()->delete();
|
||
|
||
return $this->Success(['message' => '已登出所有设备']);
|
||
}
|
||
|
||
/**
|
||
* 清空认证缓存
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function clearAuthCache(Request $request) : JsonResponse
|
||
{
|
||
$type = $request->input('type', 'current'); // current, user, all
|
||
|
||
switch ($type) {
|
||
case 'current':
|
||
// 清空当前token缓存
|
||
$currentToken = $request->bearerToken();
|
||
if ($currentToken) {
|
||
$result = $this->tokenAuthService->clearTokenCache($currentToken);
|
||
return $this->Success([
|
||
'message' => '当前token缓存清空成功',
|
||
'cleared' => $result
|
||
]);
|
||
}
|
||
return $this->Field('未找到当前token');
|
||
|
||
case 'user':
|
||
// 清空当前用户所有token缓存
|
||
$user = $request->user();
|
||
$clearedCount = $this->tokenAuthService->clearCacheOnLogout($user);
|
||
return $this->Success([
|
||
'message' => '用户所有token缓存清空成功',
|
||
'cleared_count' => $clearedCount
|
||
]);
|
||
|
||
case 'all':
|
||
// 清空所有认证缓存(管理员功能)
|
||
$result = $this->tokenAuthService->clearAllAuthCache();
|
||
return $this->Success([
|
||
'message' => '所有认证缓存清空成功',
|
||
'success' => $result
|
||
]);
|
||
|
||
default:
|
||
return $this->Field('无效的缓存类型');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取缓存统计信息
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function getCacheStats(Request $request) : JsonResponse
|
||
{
|
||
$stats = $this->tokenAuthService->getCacheStats();
|
||
|
||
return $this->Success([
|
||
'message' => '缓存统计信息',
|
||
'stats' => $stats
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取当前用户信息
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function me(Request $request) : JsonResponse
|
||
{
|
||
$user = $request->user();
|
||
|
||
return $this->Success([
|
||
'id' => $user->id,
|
||
'username' => $user->username,
|
||
'nickname' => $user->nickname,
|
||
'email' => $user->email,
|
||
'mobile' => $user->mobile,
|
||
'avatar' => $user->avatar,
|
||
'dept_id' => $user->dept_id,
|
||
'login_date' => $user->login_date?->format('Y-m-d H:i:s'),
|
||
'login_ip' => $user->login_ip,
|
||
'current_token' => [
|
||
'name' => $request->user()->currentAccessToken()->name,
|
||
'created_at' => $request->user()->currentAccessToken()->created_at->format('Y-m-d H:i:s'),
|
||
'last_used_at' => $request->user()->currentAccessToken()->last_used_at?->format('Y-m-d H:i:s'),
|
||
]
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 刷新Token (删除旧Token,创建新Token)
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function refresh(Request $request) : JsonResponse
|
||
{
|
||
$user = $request->user();
|
||
$currentToken = $request->user()->currentAccessToken();
|
||
|
||
// 获取当前token的设备名
|
||
$deviceName = $currentToken->name;
|
||
|
||
// 删除当前token
|
||
$currentToken->delete();
|
||
|
||
// 创建新token
|
||
$newToken = $user->createToken($deviceName);
|
||
|
||
return $this->Success([
|
||
'token' => [
|
||
'access_token' => $newToken->plainTextToken,
|
||
'token_type' => 'Bearer',
|
||
'expires_in' => null,
|
||
],
|
||
'message' => 'Token刷新成功'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取用户所有设备/Token列表
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function devices(Request $request) : JsonResponse
|
||
{
|
||
$tokens = $request->user()->tokens;
|
||
|
||
$devices = $tokens->map(function ($token) {
|
||
return [
|
||
'id' => $token->id,
|
||
'name' => $token->name,
|
||
'created_at' => $token->created_at->format('Y-m-d H:i:s'),
|
||
'last_used_at' => $token->last_used_at?->format('Y-m-d H:i:s'),
|
||
'is_current' => $token->id === request()->user()->currentAccessToken()->id,
|
||
];
|
||
});
|
||
|
||
return $this->Success([
|
||
'devices' => $devices,
|
||
'total' => $devices->count()
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 删除指定设备/Token
|
||
*
|
||
* @param Request $request
|
||
* @return \Illuminate\Http\JsonResponse
|
||
*/
|
||
public function deleteDevice(Request $request) : JsonResponse
|
||
{
|
||
$validator = Validator::make($request->all(), [
|
||
'token_id' => ['required', 'integer', 'exists:personal_access_tokens,id']
|
||
], [
|
||
'token_id.required' => '请指定要删除的Token ID',
|
||
'token_id.exists' => 'Token不存在'
|
||
]);
|
||
|
||
if ($validator->fails()) {
|
||
return $this->Field($validator->errors()->first(), 422);
|
||
}
|
||
|
||
$tokenId = $request->token_id;
|
||
$currentTokenId = $request->user()->currentAccessToken()->id;
|
||
|
||
// 不能删除当前正在使用的token
|
||
if ($tokenId == $currentTokenId) {
|
||
return $this->Field('不能删除当前正在使用的设备', 400);
|
||
}
|
||
|
||
// 只能删除自己的token
|
||
$deleted = $request->user()->tokens()->where('id', $tokenId)->delete();
|
||
|
||
if ($deleted) {
|
||
return $this->Success(['message' => '设备删除成功']);
|
||
} else {
|
||
return $this->Field('设备删除失败', 500);
|
||
}
|
||
}
|
||
}
|