245 lines
6.6 KiB
PHP
245 lines
6.6 KiB
PHP
<?php
|
|
/**
|
|
* 认证服务
|
|
* 杰柏特半导体后台管理系统
|
|
*/
|
|
|
|
namespace app\service;
|
|
|
|
use app\model\Admin;
|
|
|
|
class AuthService
|
|
{
|
|
private $adminModel;
|
|
private $config;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->adminModel = new Admin();
|
|
$this->config = require __DIR__ . '/../config/app.php';
|
|
}
|
|
|
|
/**
|
|
* 用户登录
|
|
*/
|
|
public function login($username, $password)
|
|
{
|
|
if (empty($username) || empty($password)) {
|
|
return ['code' => 400, 'msg' => '用户名和密码不能为空'];
|
|
}
|
|
|
|
$admin = $this->adminModel->findByUsername($username);
|
|
|
|
if (!$admin) {
|
|
return ['code' => 400, 'msg' => '用户名或密码错误'];
|
|
}
|
|
|
|
if ($admin['status'] != 1) {
|
|
return ['code' => 403, 'msg' => '账号已被禁用,请联系管理员'];
|
|
}
|
|
|
|
if (!password_verify($password, $admin['password'])) {
|
|
return ['code' => 400, 'msg' => '用户名或密码错误'];
|
|
}
|
|
|
|
// 更新登录信息
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
|
|
$this->adminModel->updateLoginInfo($admin['id'], $ip);
|
|
|
|
// 生成Token
|
|
$token = $this->generateToken($admin);
|
|
|
|
return [
|
|
'code' => 200,
|
|
'msg' => '登录成功',
|
|
'data' => [
|
|
'token' => $token,
|
|
'userinfo' => [
|
|
'id' => $admin['id'],
|
|
'username' => $admin['username'],
|
|
'nickname' => $admin['nickname'] ?: $admin['username'],
|
|
'avatar' => $admin['avatar'] ?: '',
|
|
'category' => $admin['category'] ?? 'mes',
|
|
'role' => ($admin['category'] === 'all') ? 'super_admin' : 'admin',
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 生成JWT Token
|
|
*/
|
|
private function generateToken($admin)
|
|
{
|
|
$header = base64_encode(json_encode([
|
|
'alg' => 'HS256',
|
|
'typ' => 'JWT',
|
|
]));
|
|
|
|
$payload = base64_encode(json_encode([
|
|
'id' => $admin['id'],
|
|
'username' => $admin['username'],
|
|
'iat' => time(),
|
|
'exp' => time() + $this->config['token_expire'],
|
|
]));
|
|
|
|
$signature = base64_encode(hash_hmac(
|
|
'sha256',
|
|
"{$header}.{$payload}",
|
|
$this->config['token_secret'],
|
|
true
|
|
));
|
|
|
|
return "{$header}.{$payload}.{$signature}";
|
|
}
|
|
|
|
/**
|
|
* 验证Token
|
|
*/
|
|
public function verifyToken($token)
|
|
{
|
|
if (empty($token)) {
|
|
return false;
|
|
}
|
|
|
|
$parts = explode('.', $token);
|
|
if (count($parts) !== 3) {
|
|
return false;
|
|
}
|
|
|
|
[$header, $payload, $signature] = $parts;
|
|
|
|
// 验证签名
|
|
$expectedSig = base64_encode(hash_hmac(
|
|
'sha256',
|
|
"{$header}.{$payload}",
|
|
$this->config['token_secret'],
|
|
true
|
|
));
|
|
|
|
if (!hash_equals($expectedSig, $signature)) {
|
|
return false;
|
|
}
|
|
|
|
// 解析payload
|
|
$data = json_decode(base64_decode($payload), true);
|
|
if (!$data) {
|
|
return false;
|
|
}
|
|
|
|
// 检查过期
|
|
if (isset($data['exp']) && $data['exp'] < time()) {
|
|
return false;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* 获取当前登录用户
|
|
*/
|
|
public function getCurrentUser()
|
|
{
|
|
$token = $this->getBearerToken();
|
|
if (!$token) {
|
|
return null;
|
|
}
|
|
|
|
$payload = $this->verifyToken($token);
|
|
if (!$payload) {
|
|
return null;
|
|
}
|
|
|
|
return $this->adminModel->find($payload['id']);
|
|
}
|
|
|
|
/**
|
|
* 从请求头获取Token
|
|
* 优先级: Authorization > X-Auth-Token (Nginx兼容)
|
|
*/
|
|
public function getBearerToken()
|
|
{
|
|
// 1. 标准 Authorization header
|
|
$headers = $this->getAuthorizationHeader();
|
|
if ($headers && preg_match('/Bearer\s+(.+)$/i', $headers, $matches)) {
|
|
return $matches[1];
|
|
}
|
|
|
|
// 2. 自定义 header X-Auth-Token (Nginx 默认不丢弃自定义 header)
|
|
// 前端发送: X-Auth-Token: Bearer <token>
|
|
$xAuthToken = $_SERVER['HTTP_X_AUTH_TOKEN'] ?? null;
|
|
if ($xAuthToken) {
|
|
if (preg_match('/^Bearer\s+(.+)$/i', $xAuthToken, $m)) {
|
|
return $m[1];
|
|
}
|
|
// 也支持直接传 token 值
|
|
return $xAuthToken;
|
|
}
|
|
|
|
// 3. URL 参数 token(用于 window.open / <a href> 下载等场景)
|
|
$urlToken = $_GET['token'] ?? null;
|
|
if ($urlToken) {
|
|
return $urlToken;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 获取Authorization头
|
|
* 兼容 Apache / Nginx / IIS / PHP-FPM 等多种环境
|
|
*/
|
|
private function getAuthorizationHeader()
|
|
{
|
|
// 1. Apache / Nginx (with pass) 标准
|
|
if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
|
return $_SERVER['HTTP_AUTHORIZATION'];
|
|
}
|
|
|
|
// 2. Apache rewrite 后
|
|
if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
|
|
return $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
|
}
|
|
|
|
// 3. Nginx + PHP-FPM: Authorization 可能被放在其他变量中
|
|
if (isset($_SERVER['AUTHORIZATION'])) {
|
|
return $_SERVER['AUTHORIZATION'];
|
|
}
|
|
|
|
// 4. apache_request_headers (Apache/FastCGI)
|
|
if (function_exists('apache_request_headers')) {
|
|
$headers = apache_request_headers();
|
|
if (isset($headers['Authorization'])) {
|
|
return $headers['Authorization'];
|
|
}
|
|
// 有些环境下 header 名是小写的
|
|
if (isset($headers['authorization'])) {
|
|
return $headers['authorization'];
|
|
}
|
|
}
|
|
|
|
// 5. Nginx + PHP-FPM 终极兜底: 从原始 HTTP 头中提取
|
|
// 有些 Nginx 配置下,Authorization 可能根本不在 $_SERVER 中
|
|
// 尝试从 getallheaders() 获取 (PHP-FPM built-in)
|
|
if (function_exists('getallheaders')) {
|
|
$allHeaders = getallheaders();
|
|
if (isset($allHeaders['Authorization'])) {
|
|
return $allHeaders['Authorization'];
|
|
}
|
|
if (isset($allHeaders['authorization'])) {
|
|
return $allHeaders['authorization'];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 生成密码哈希
|
|
*/
|
|
public function hashPassword($password)
|
|
{
|
|
return password_hash($password, PASSWORD_BCRYPT);
|
|
}
|
|
}
|