You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

300 lines
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\ApiResponse;
use App\Helpers\ResponseCode;
use App\Helpers\StarterResponseCode;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\Config;
use App\Models\OperateLog;
use App\Models\Permission;
use App\Models\RoleHasPermission;
use App\Services\AdminSmsChallengeService;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
class AuthController extends Controller
{
use ApiResponse;
public $guard = "admin";
/**
* Create a new AuthController instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('sanctum.jwt:admin', ['except' => ['login', 'sendSms', 'smsLogin']]);
}
/**
* Create a new AuthController instance.
*
* @return void
*/
public function guard()
{
return auth()->guard($this->guard);
}
/**
* @OA\Post(
* path="/api/admin/auth/login",
* tags={"后台管理"},
* summary="用户名密码登录",
* description="",
* @OA\Parameter(name="username", in="query", @OA\Schema(type="string"), required=true, description="用户名"),
* @OA\Parameter(name="password", in="query", @OA\Schema(type="string"), required=true, description="密码"),
* @OA\Response(
* response="200",
* description="用户名密码登录"
* )
* )
*/
public function login()
{
$credentials = request(['username', 'password']);
if (!$token = $this->guard()->attempt($credentials)) {
return $this->fail(StarterResponseCode::LOGIN_FAIL);
}
$user = $this->guard()->user();
$token = $user->createToken("token")->plainTextToken;
// 加日志
OperateLog::addLogs($user, "用户[{$user->name}]登陆成功");
return $this->respondWithToken($token);
}
/**
* @OA\Post(
* path="/api/admin/auth/me",
* tags={"后台管理"},
* summary="获取用户基本信息",
* description="",
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"),
* @OA\Response(
* response="200",
* description="获取用户基本信息"
* )
* )
*/
public function me()
{
$user = Admin::with(['department', 'roles'])->find($this->guard()->user()->id);
return $this->success($user);
}
/**
* @OA\Post(
* path="/api/admin/auth/logout",
* tags={"后台管理"},
* summary="退出登录",
* description="",
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"),
* @OA\Response(
* response="200",
* description="退出登录"
* )
* )
*/
public function logout()
{
$user = $this->guard()->user();
$user->tokens()->delete();
$this->guard()->logout();
// 加日志
OperateLog::addLogs($user, "用户[$user->name]退出登陆");
return $this->success(["msg" => "登出成功"]);
}
/**
* @OA\Get(
* path="/api/admin/auth/permissions",
* tags={"后台管理"},
* summary="获取权限菜单",
* description="",
* @OA\Parameter(name="token", in="query", @OA\Schema(type="string"), required=true, description="token"),
* @OA\Response(
* response="200",
* description="获取权限菜单"
* )
* )
*/
public function getPermissions()
{
$admin = $this->guard()->user();
$admin_permissions = $admin->getAllPermissions();
$all_permissions = (new Permission())->get()->toArray();
$permission_has_url_ids = $admin_permissions->filter(function ($item) {
return $item->url && $item->visible ? true : false;
})->pluck("id")->toArray();
foreach ($admin_permissions as $admin_permission) {
$pids = get_pid($all_permissions, $admin_permission->id, "pid", $permission_has_url_ids);
foreach ($pids as $pid) {
if (in_array($pid, $permission_has_url_ids)) continue;
$permission_has_url_ids[] = $pid;
}
}
$admin_permissions = new Permission();
$admin_permissions = $admin_permissions->whereIn("id", $permission_has_url_ids);
$admin_permissions = $admin_permissions->orderBy("sortnumber")->get();
$admin_permissions = $admin_permissions->toArray();
$roleIds = $this->guard()->user()->roles->pluck('id')->toArray();
foreach ($admin_permissions as &$item) {
$item['has_auth_node_tags'] = [];
$has_auth_node_tags = RoleHasPermission::whereIn('role_id', $roleIds)
->where('permission_id', $item['id'])
->get()->implode('auth_node_tags', ',');
if ($has_auth_node_tags) {
$item['has_auth_node_tags'] = array_filter(array_unique(explode(',', $has_auth_node_tags)));
}
}
$admin_permissions = array2tree($admin_permissions);
return response()->json($admin_permissions);
}
/**
* @OA\Post(
* path="/api/admin/auth/sms-login",
* tags={"后台管理"},
* summary="验证码登陆",
* description="",
* @OA\RequestBody(required=true, @OA\JsonContent(
* required={"mobile", "code", "challenge_id"},
* @OA\Property(property="mobile", type="string", example="13800138000"),
* @OA\Property(property="code", type="string", example="123456"),
* @OA\Property(property="challenge_id", type="string", format="uuid")
* )),
* @OA\Response(
* response="200",
* description=""
* )
* )
*/
public function smsLogin()
{
$all = request()->all();
$messages = [
'mobile.required' => '手机号必填',
'mobile.digits' => '手机号格式错误',
'code.digits' => '验证码格式错误',
];
$validator = Validator::make($all, [
'mobile' => 'required|digits:11',
'code' => 'required|digits:6',
'challenge_id' => 'required|uuid',
], $messages);
if ($validator->fails()) {
return $this->fail([StarterResponseCode::START_ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$admin = Admin::where('mobile', $all['mobile'])->first();
$result = app(AdminSmsChallengeService::class)->verify(
$all['challenge_id'],
$all['mobile'],
$all['code'],
request()->ip(),
$admin,
(string) request()->userAgent()
);
if (!$result['ok']) {
return $this->fail([ResponseCode::ERROR_BUSINESS, $result['message']]);
}
$token = $admin->createToken("token")->plainTextToken;
// 加日志
OperateLog::addLogs($admin, "用户[{$admin->name}]短信登陆成功");
return $this->respondWithToken($token);
}
/**
* @OA\Post(
* path="/api/admin/auth/send-sms",
* tags={"后台管理"},
* summary="短信发送",
* description="",
* @OA\RequestBody(required=true, @OA\JsonContent(
* required={"mobile"},
* @OA\Property(property="mobile", type="string", example="13800138000")
* )),
* @OA\Response(
* response="200",
* description=""
* )
* )
*/
public function sendSms()
{
$all = request()->all();
$messages = [
'mobile.required' => '手机号必填',
'mobile.digits' => '手机号格式错误',
];
$validator = Validator::make($all, [
'mobile' => 'required|digits:11',
], $messages);
if ($validator->fails()) {
return $this->fail([StarterResponseCode::START_ERROR_PARAMETER, implode(',', $validator->errors()->all())]);
}
$service = app(AdminSmsChallengeService::class);
$canSend = $service->canSend($all['mobile'], request()->ip());
$admin = Admin::where('mobile', $all['mobile'])->first();
$challengeId = null;
if ($canSend && $admin) {
$smsSign = Config::getValueByKey('sms_sign');
$challengeId = $service->issueChallenge($all['mobile'], request()->ip(), function ($code) use ($all, $smsSign) {
$content = "{$smsSign}验证码{$code}您正在登陆苏州科技商学院信息化系统请在5分钟内完成验证。";
try {
return (bool) ymSms($all['mobile'], $content);
} catch (\Throwable $e) {
// Do not expose provider/network errors or the mobile number to the client.
Log::error('admin_sms_provider_error', [
'mobile_hash' => hash('sha256', (string) $all['mobile']),
'exception' => get_class($e),
]);
return false;
}
}, (string) request()->userAgent());
}
// Always return the same shape, including for unknown or throttled numbers.
return $this->success([
'message' => '如果手机号已登记,验证码将发送',
'challenge_id' => $challengeId ?: $service->issueDecoyChallenge($all['mobile'], request()->ip(), (string) request()->userAgent()),
]);
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
$ttl = config("sanctum.expiration");
if ($ttl) {
$expires_in = $ttl * 60;
} else {
$expires_in = null;
}
return $this->success([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => $expires_in
]);
}
}