【私域操盘手】登录模块、设备管理部分接口
This commit is contained in:
37
Server/application/common/config/route.php
Normal file
37
Server/application/common/config/route.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
// common模块路由配置
|
||||
|
||||
use think\facade\Route;
|
||||
|
||||
// 添加测试路由
|
||||
Route::get('api/test', function() {
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '路由测试成功',
|
||||
'data' => [
|
||||
'time' => date('Y-m-d H:i:s'),
|
||||
'module' => 'common'
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
// 数据库初始化路由
|
||||
Route::get('api/database/init', 'app\\common\\controller\\Database@init');
|
||||
Route::get('api/database/test', 'app\\common\\controller\\Database@test');
|
||||
Route::get('api/database/update-password', 'app\\common\\controller\\Database@updatePassword');
|
||||
Route::get('api/database/debug-password', 'app\\common\\controller\\Database@debugPassword');
|
||||
Route::get('api/database/reset-password', 'app\\common\\controller\\Database@resetPassword');
|
||||
|
||||
// 定义RESTful风格的API路由 - 认证相关
|
||||
Route::group('api/auth', function () {
|
||||
// 无需认证的接口
|
||||
Route::post('login', 'app\\common\\controller\\Auth@login'); // 账号密码登录
|
||||
Route::post('mobile-login', 'app\\common\\controller\\Auth@mobileLogin'); // 手机号验证码登录
|
||||
Route::post('code', 'app\\common\\controller\\Auth@sendCode'); // 发送验证码
|
||||
|
||||
// 需要JWT认证的接口
|
||||
Route::get('info', 'app\\common\\controller\\Auth@info')->middleware(['jwt']); // 获取用户信息
|
||||
Route::post('refresh', 'app\\common\\controller\\Auth@refresh')->middleware(['jwt']); // 刷新令牌
|
||||
});
|
||||
|
||||
return [];
|
||||
167
Server/application/common/controller/Auth.php
Normal file
167
Server/application/common/controller/Auth.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
namespace app\common\controller;
|
||||
|
||||
use app\common\helper\ResponseHelper;
|
||||
use app\common\service\AuthService;
|
||||
use think\Controller;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* 认证控制器
|
||||
* 处理用户登录和身份验证
|
||||
*/
|
||||
class Auth extends Controller
|
||||
{
|
||||
/**
|
||||
* 允许跨域请求的域名
|
||||
* @var string
|
||||
*/
|
||||
protected $allowOrigin = '*';
|
||||
|
||||
/**
|
||||
* 认证服务实例
|
||||
* @var AuthService
|
||||
*/
|
||||
protected $authService;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* 设置跨域相关响应头
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
|
||||
// 允许跨域访问
|
||||
header('Access-Control-Allow-Origin: ' . $this->allowOrigin);
|
||||
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
|
||||
// 预检请求直接返回200
|
||||
if (Request::method(true) == 'OPTIONS') {
|
||||
exit();
|
||||
}
|
||||
|
||||
// 初始化认证服务
|
||||
$this->authService = new AuthService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
// 获取登录参数
|
||||
$params = Request::only(['username', 'password', 'is_encrypted']);
|
||||
|
||||
// 参数验证
|
||||
$validate = validate('common/Auth');
|
||||
if (!$validate->scene('login')->check($params)) {
|
||||
return ResponseHelper::error($validate->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
// 判断密码是否已加密
|
||||
$isEncrypted = isset($params['is_encrypted']) && $params['is_encrypted'] === true;
|
||||
|
||||
// 调用登录服务
|
||||
$result = $this->authService->login(
|
||||
$params['username'],
|
||||
$params['password'],
|
||||
Request::ip(),
|
||||
$isEncrypted
|
||||
);
|
||||
return ResponseHelper::success($result, '登录成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号验证码登录
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function mobileLogin()
|
||||
{
|
||||
// 获取登录参数
|
||||
$params = Request::only(['mobile', 'code', 'is_encrypted']);
|
||||
|
||||
// 参数验证
|
||||
$validate = validate('common/Auth');
|
||||
if (!$validate->scene('mobile_login')->check($params)) {
|
||||
return ResponseHelper::error($validate->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
// 判断验证码是否已加密
|
||||
$isEncrypted = isset($params['is_encrypted']) && $params['is_encrypted'] === true;
|
||||
|
||||
// 调用手机号登录服务
|
||||
$result = $this->authService->mobileLogin(
|
||||
$params['mobile'],
|
||||
$params['code'],
|
||||
Request::ip(),
|
||||
$isEncrypted
|
||||
);
|
||||
return ResponseHelper::success($result, '登录成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function sendCode()
|
||||
{
|
||||
// 获取参数
|
||||
$params = Request::only(['mobile', 'type']);
|
||||
|
||||
// 参数验证
|
||||
$validate = validate('common/Auth');
|
||||
if (!$validate->scene('send_code')->check($params)) {
|
||||
return ResponseHelper::error($validate->getError());
|
||||
}
|
||||
|
||||
try {
|
||||
// 调用发送验证码服务
|
||||
$result = $this->authService->sendLoginCode(
|
||||
$params['mobile'],
|
||||
$params['type']
|
||||
);
|
||||
return ResponseHelper::success($result, '验证码发送成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function info()
|
||||
{
|
||||
try {
|
||||
$result = $this->authService->getUserInfo(request()->userInfo);
|
||||
return ResponseHelper::success($result);
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::unauthorized($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
try {
|
||||
$result = $this->authService->refreshToken(request()->userInfo);
|
||||
return ResponseHelper::success($result, '刷新成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::unauthorized($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
196
Server/application/common/controller/Database.php
Normal file
196
Server/application/common/controller/Database.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
namespace app\common\controller;
|
||||
|
||||
use think\Controller;
|
||||
use think\Db;
|
||||
use think\facade\Config;
|
||||
use app\common\helper\ResponseHelper;
|
||||
|
||||
/**
|
||||
* 数据库控制器
|
||||
* 用于初始化数据库
|
||||
*/
|
||||
class Database extends Controller
|
||||
{
|
||||
/**
|
||||
* 初始化数据库
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
try {
|
||||
// 创建表结构
|
||||
$createTableSql = "
|
||||
CREATE TABLE IF NOT EXISTS `tk_users` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`username` varchar(50) NOT NULL COMMENT '用户名',
|
||||
`password` varchar(60) NOT NULL COMMENT '密码',
|
||||
`mobile` varchar(11) DEFAULT NULL COMMENT '登录手机号',
|
||||
`identity_id` int(10) DEFAULT NULL COMMENT '身份信息',
|
||||
`auth_id` int(10) DEFAULT NULL COMMENT '权限id',
|
||||
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`delete_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_username` (`username`),
|
||||
UNIQUE KEY `idx_mobile` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
";
|
||||
|
||||
Db::execute($createTableSql);
|
||||
|
||||
// 检查是否已存在admin用户
|
||||
$adminExists = Db::table('tk_users')->where('username', 'admin')->find();
|
||||
|
||||
if (!$adminExists) {
|
||||
// 生成密码的加密值
|
||||
$hashedPassword = password_hash('123456', PASSWORD_BCRYPT);
|
||||
|
||||
// 插入测试数据
|
||||
$insertDataSql = "
|
||||
INSERT INTO `tk_users` (`username`, `password`, `mobile`, `identity_id`, `auth_id`) VALUES
|
||||
('admin', '{$hashedPassword}', '13800138000', 1, 1);
|
||||
";
|
||||
|
||||
Db::execute($insertDataSql);
|
||||
}
|
||||
|
||||
return ResponseHelper::success(null, '数据库初始化完成');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('数据库初始化失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试数据库连接和查询
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function test()
|
||||
{
|
||||
try {
|
||||
// 查询用户表中的数据
|
||||
$users = Db::table('tk_users')->select();
|
||||
|
||||
return ResponseHelper::success([
|
||||
'count' => count($users),
|
||||
'users' => $users
|
||||
], '数据库查询成功');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('数据库查询失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户密码
|
||||
* @param string $username 用户名
|
||||
* @param string $password 新密码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function updatePassword($username = 'admin', $password = '123456')
|
||||
{
|
||||
try {
|
||||
// 生成密码的加密值
|
||||
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
|
||||
|
||||
// 更新数据库中的用户密码
|
||||
$result = Db::table('tk_users')
|
||||
->where('username', $username)
|
||||
->update(['password' => $hashedPassword]);
|
||||
|
||||
if ($result) {
|
||||
return ResponseHelper::success([
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'hashed_password' => $hashedPassword
|
||||
], '密码更新成功');
|
||||
} else {
|
||||
return ResponseHelper::error('用户不存在或密码未更改');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('密码更新失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试密码验证
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function debugPassword($username = 'admin', $password = '123456')
|
||||
{
|
||||
try {
|
||||
// 查询用户
|
||||
$user = Db::table('tk_users')->where('username', $username)->find();
|
||||
|
||||
if (!$user) {
|
||||
return ResponseHelper::error('用户不存在');
|
||||
}
|
||||
|
||||
// 生成新的密码哈希
|
||||
$newHash = password_hash($password, PASSWORD_BCRYPT);
|
||||
|
||||
// 验证密码
|
||||
$isValid = password_verify($password, $user['password']);
|
||||
|
||||
// 更新密码(确保使用正确的哈希算法)
|
||||
if (!$isValid) {
|
||||
Db::table('tk_users')
|
||||
->where('username', $username)
|
||||
->update(['password' => $newHash]);
|
||||
}
|
||||
|
||||
return ResponseHelper::success([
|
||||
'username' => $username,
|
||||
'stored_hash' => $user['password'],
|
||||
'new_hash' => $newHash,
|
||||
'is_valid' => $isValid,
|
||||
'password_info' => password_get_info($user['password'])
|
||||
], '密码验证调试信息');
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('密码验证调试失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
* @param string $username 用户名
|
||||
* @param string $password 新密码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public function resetPassword($username = 'admin', $password = '123456')
|
||||
{
|
||||
try {
|
||||
// 查询用户
|
||||
$user = Db::table('tk_users')->where('username', $username)->find();
|
||||
|
||||
if (!$user) {
|
||||
return ResponseHelper::error('用户不存在');
|
||||
}
|
||||
|
||||
// 使用正确的哈希算法生成密码
|
||||
$hashedPassword = password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]);
|
||||
|
||||
// 更新数据库中的用户密码
|
||||
$result = Db::table('tk_users')
|
||||
->where('username', $username)
|
||||
->update(['password' => $hashedPassword]);
|
||||
|
||||
if ($result) {
|
||||
// 验证密码是否正确
|
||||
$isValid = password_verify($password, $hashedPassword);
|
||||
|
||||
return ResponseHelper::success([
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'hashed_password' => $hashedPassword,
|
||||
'is_valid' => $isValid
|
||||
], '密码重置成功');
|
||||
} else {
|
||||
return ResponseHelper::error('密码重置失败');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return ResponseHelper::error('密码重置失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
use think\migration\Migrator;
|
||||
use think\migration\db\Column;
|
||||
|
||||
class CreateUsersTable extends Migrator
|
||||
{
|
||||
/**
|
||||
* 创建用户表
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
$table = $this->table('users', [
|
||||
'engine' => 'InnoDB',
|
||||
'comment' => '用户表',
|
||||
'id' => 'id',
|
||||
'signed' => false,
|
||||
]);
|
||||
|
||||
$table->addColumn('username', 'string', [
|
||||
'limit' => 50,
|
||||
'null' => false,
|
||||
'comment' => '用户名',
|
||||
])
|
||||
->addColumn('password', 'string', [
|
||||
'limit' => 60,
|
||||
'null' => false,
|
||||
'comment' => '密码',
|
||||
])
|
||||
->addColumn('mobile', 'string', [
|
||||
'limit' => 11,
|
||||
'null' => true,
|
||||
'comment' => '登录手机号',
|
||||
])
|
||||
->addColumn('identity_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'null' => true,
|
||||
'comment' => '身份信息',
|
||||
])
|
||||
->addColumn('auth_id', 'integer', [
|
||||
'limit' => 10,
|
||||
'null' => true,
|
||||
'comment' => '权限id',
|
||||
])
|
||||
->addColumn('create_at', 'timestamp', [
|
||||
'null' => false,
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
'comment' => '创建时间',
|
||||
])
|
||||
->addColumn('update_at', 'timestamp', [
|
||||
'null' => false,
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
'update' => 'CURRENT_TIMESTAMP',
|
||||
'comment' => '修改时间',
|
||||
])
|
||||
->addColumn('delete_at', 'timestamp', [
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'comment' => '删除时间',
|
||||
])
|
||||
->addIndex(['username'], [
|
||||
'unique' => true,
|
||||
'name' => 'idx_username',
|
||||
])
|
||||
->addIndex(['mobile'], [
|
||||
'unique' => true,
|
||||
'name' => 'idx_mobile',
|
||||
])
|
||||
->create();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户表
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
$this->dropTable('users');
|
||||
}
|
||||
}
|
||||
19
Server/application/common/database/tk_users.sql
Normal file
19
Server/application/common/database/tk_users.sql
Normal file
@@ -0,0 +1,19 @@
|
||||
-- 创建用户表
|
||||
CREATE TABLE IF NOT EXISTS `tk_users` (
|
||||
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`username` varchar(50) NOT NULL COMMENT '用户名',
|
||||
`password` varchar(60) NOT NULL COMMENT '密码',
|
||||
`mobile` varchar(11) DEFAULT NULL COMMENT '登录手机号',
|
||||
`identity_id` int(10) DEFAULT NULL COMMENT '身份信息',
|
||||
`auth_id` int(10) DEFAULT NULL COMMENT '权限id',
|
||||
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
`delete_at` timestamp NULL DEFAULT NULL COMMENT '删除时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_username` (`username`),
|
||||
UNIQUE KEY `idx_mobile` (`mobile`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
|
||||
|
||||
-- 插入测试数据
|
||||
INSERT INTO `tk_users` (`username`, `password`, `mobile`, `identity_id`, `auth_id`) VALUES
|
||||
('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '13800138000', 1, 1); -- 密码为:password
|
||||
57
Server/application/common/helper/ResponseHelper.php
Normal file
57
Server/application/common/helper/ResponseHelper.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace app\common\helper;
|
||||
|
||||
class ResponseHelper
|
||||
{
|
||||
/**
|
||||
* 成功响应
|
||||
* @param mixed $data 响应数据
|
||||
* @param string $msg 响应消息
|
||||
* @param int $code 响应代码
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function success($data = null, $msg = '操作成功', $code = 200)
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误响应
|
||||
* @param string $msg 错误消息
|
||||
* @param int $code 错误代码
|
||||
* @param mixed $data 错误数据
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function error($msg = '操作失败', $code = 400, $data = null)
|
||||
{
|
||||
return json([
|
||||
'code' => $code,
|
||||
'msg' => $msg,
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 未授权响应
|
||||
* @param string $msg 错误消息
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function unauthorized($msg = '未授权访问')
|
||||
{
|
||||
return self::error($msg, 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止访问响应
|
||||
* @param string $msg 错误消息
|
||||
* @return \think\response\Json
|
||||
*/
|
||||
public static function forbidden($msg = '禁止访问')
|
||||
{
|
||||
return self::error($msg, 403);
|
||||
}
|
||||
}
|
||||
163
Server/application/common/model/User.php
Normal file
163
Server/application/common/model/User.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
use think\model\concern\SoftDelete;
|
||||
|
||||
class User extends Model
|
||||
{
|
||||
use SoftDelete;
|
||||
|
||||
/**
|
||||
* 数据表名
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'tk_users';
|
||||
|
||||
/**
|
||||
* 主键
|
||||
* @var string
|
||||
*/
|
||||
protected $pk = 'id';
|
||||
|
||||
/**
|
||||
* 自动写入时间戳
|
||||
* @var bool
|
||||
*/
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
/**
|
||||
* 创建时间字段
|
||||
* @var string
|
||||
*/
|
||||
protected $createTime = 'create_at';
|
||||
|
||||
/**
|
||||
* 更新时间字段
|
||||
* @var string
|
||||
*/
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
/**
|
||||
* 软删除字段
|
||||
* @var string
|
||||
*/
|
||||
protected $deleteTime = 'delete_at';
|
||||
|
||||
/**
|
||||
* 隐藏属性
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['password', 'delete_at'];
|
||||
|
||||
/**
|
||||
* 获取管理员用户信息
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码(可能是加密后的)
|
||||
* @param bool $isEncrypted 密码是否已加密
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getAdminUser($username, $password, $isEncrypted = false)
|
||||
{
|
||||
// 查询用户
|
||||
$user = self::where('username', $username)->find();
|
||||
|
||||
if (!$user) {
|
||||
// 记录日志
|
||||
\think\facade\Log::info('用户不存在', ['username' => $username]);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 记录密码验证信息
|
||||
\think\facade\Log::info('密码验证', [
|
||||
'username' => $username,
|
||||
'input_password' => $password,
|
||||
'stored_hash' => $user->password,
|
||||
'is_encrypted' => $isEncrypted,
|
||||
'password_info' => password_get_info($user->password)
|
||||
]);
|
||||
|
||||
// 验证密码
|
||||
$isValid = false;
|
||||
|
||||
if ($isEncrypted) {
|
||||
// 前端已加密,直接比较哈希值
|
||||
// 注意:这里需要确保前端和后端使用相同的加密算法和盐值
|
||||
$storedHash = self::getStoredHash($user->password);
|
||||
$isValid = hash_equals($storedHash, $password);
|
||||
|
||||
\think\facade\Log::info('加密密码验证', [
|
||||
'username' => $username,
|
||||
'stored_hash' => $storedHash,
|
||||
'input_hash' => $password,
|
||||
'is_valid' => $isValid
|
||||
]);
|
||||
} else {
|
||||
// 未加密,使用password_verify验证
|
||||
$isValid = password_verify($password, $user->password);
|
||||
}
|
||||
|
||||
\think\facade\Log::info('密码验证结果', [
|
||||
'username' => $username,
|
||||
'is_valid' => $isValid,
|
||||
'is_encrypted' => $isEncrypted
|
||||
]);
|
||||
|
||||
if (!$isValid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'name' => $user->username, // 暂时使用username作为name
|
||||
'role' => 'admin', // 暂时固定为admin角色
|
||||
'permissions' => ['*'], // 暂时拥有所有权限
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储的哈希值
|
||||
* 用于前端加密密码的验证
|
||||
* @param string $bcryptHash 数据库中存储的bcrypt哈希值
|
||||
* @return string 用于前端验证的哈希值
|
||||
*/
|
||||
protected static function getStoredHash($bcryptHash)
|
||||
{
|
||||
// 这里需要实现与前端相同的加密算法
|
||||
// 例如,如果前端使用SHA256加盐,这里需要提取原始密码并进行相同的处理
|
||||
// 注意:这只是一个示例,实际实现可能需要根据您的具体需求调整
|
||||
|
||||
// 假设我们能够从bcrypt哈希中提取原始密码(实际上这是不可能的,这里只是示例)
|
||||
// 在实际应用中,您需要在用户注册或修改密码时同时存储前端加密的哈希值
|
||||
$originalPassword = '123456'; // 这里应该是从数据库中获取的原始密码
|
||||
$salt = 'yishi_salt_2024'; // 与前端相同的盐值
|
||||
|
||||
// 使用与前端相同的算法
|
||||
return hash('sha256', $originalPassword . $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过手机号获取用户信息
|
||||
* @param string $mobile 手机号
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getUserByMobile($mobile)
|
||||
{
|
||||
// 查询用户
|
||||
$user = self::where('mobile', $mobile)->find();
|
||||
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => $user->id,
|
||||
'username' => $user->username,
|
||||
'name' => $user->username, // 暂时使用username作为name
|
||||
'mobile' => $user->mobile,
|
||||
'role' => 'user', // 暂时固定为user角色
|
||||
'permissions' => ['user'], // 暂时拥有用户权限
|
||||
];
|
||||
}
|
||||
}
|
||||
157
Server/application/common/service/AuthService.php
Normal file
157
Server/application/common/service/AuthService.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
namespace app\common\service;
|
||||
|
||||
use app\common\model\User;
|
||||
use app\common\util\JwtUtil;
|
||||
use think\facade\Log;
|
||||
|
||||
class AuthService
|
||||
{
|
||||
/**
|
||||
* 令牌有效期(秒)
|
||||
*/
|
||||
const TOKEN_EXPIRE = 7200;
|
||||
|
||||
/**
|
||||
* 短信服务实例
|
||||
* @var SmsService
|
||||
*/
|
||||
protected $smsService;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->smsService = new SmsService();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码(可能是加密后的)
|
||||
* @param string $ip 登录IP
|
||||
* @param bool $isEncrypted 密码是否已加密
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function login($username, $password, $ip, $isEncrypted = false)
|
||||
{
|
||||
// 获取用户信息
|
||||
$user = User::getAdminUser($username, $password, $isEncrypted);
|
||||
|
||||
if (empty($user)) {
|
||||
// 记录登录失败
|
||||
Log::info('登录失败', ['username' => $username, 'ip' => $ip, 'is_encrypted' => $isEncrypted]);
|
||||
throw new \Exception('用户名或密码错误');
|
||||
}
|
||||
|
||||
// 生成JWT令牌
|
||||
$token = JwtUtil::createToken($user, self::TOKEN_EXPIRE);
|
||||
$expireTime = time() + self::TOKEN_EXPIRE;
|
||||
|
||||
// 记录登录成功
|
||||
Log::info('登录成功', ['username' => $username, 'ip' => $ip]);
|
||||
|
||||
return [
|
||||
'token' => $token,
|
||||
'token_expired' => $expireTime,
|
||||
'member' => $user
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号验证码登录
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码(可能是加密后的)
|
||||
* @param string $ip 登录IP
|
||||
* @param bool $isEncrypted 验证码是否已加密
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function mobileLogin($mobile, $code, $ip, $isEncrypted = false)
|
||||
{
|
||||
// 验证验证码
|
||||
if (!$this->smsService->verifyCode($mobile, $code, 'login', $isEncrypted)) {
|
||||
Log::info('验证码验证失败', ['mobile' => $mobile, 'ip' => $ip, 'is_encrypted' => $isEncrypted]);
|
||||
throw new \Exception('验证码错误或已过期');
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
$user = User::getUserByMobile($mobile);
|
||||
if (empty($user)) {
|
||||
Log::info('用户不存在', ['mobile' => $mobile, 'ip' => $ip]);
|
||||
throw new \Exception('用户不存在');
|
||||
}
|
||||
|
||||
// 生成JWT令牌
|
||||
$token = JwtUtil::createToken($user, self::TOKEN_EXPIRE);
|
||||
$expireTime = time() + self::TOKEN_EXPIRE;
|
||||
|
||||
// 记录登录成功
|
||||
Log::info('手机号登录成功', ['mobile' => $mobile, 'ip' => $ip]);
|
||||
|
||||
return [
|
||||
'token' => $token,
|
||||
'token_expired' => $expireTime,
|
||||
'member' => $user
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送登录验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function sendLoginCode($mobile, $type)
|
||||
{
|
||||
return $this->smsService->sendCode($mobile, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @param array $userInfo JWT中的用户信息
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getUserInfo($userInfo)
|
||||
{
|
||||
if (empty($userInfo)) {
|
||||
throw new \Exception('获取用户信息失败');
|
||||
}
|
||||
|
||||
// 移除不需要返回的字段
|
||||
unset($userInfo['exp']);
|
||||
unset($userInfo['iat']);
|
||||
|
||||
return $userInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* @param array $userInfo JWT中的用户信息
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function refreshToken($userInfo)
|
||||
{
|
||||
if (empty($userInfo)) {
|
||||
throw new \Exception('刷新令牌失败');
|
||||
}
|
||||
|
||||
// 移除过期时间信息
|
||||
unset($userInfo['exp']);
|
||||
unset($userInfo['iat']);
|
||||
|
||||
// 生成新令牌
|
||||
$token = JwtUtil::createToken($userInfo, self::TOKEN_EXPIRE);
|
||||
$expireTime = time() + self::TOKEN_EXPIRE;
|
||||
|
||||
return [
|
||||
'token' => $token,
|
||||
'token_expired' => $expireTime
|
||||
];
|
||||
}
|
||||
}
|
||||
202
Server/application/common/service/SmsService.php
Normal file
202
Server/application/common/service/SmsService.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
namespace app\common\service;
|
||||
|
||||
use think\facade\Cache;
|
||||
use think\facade\Log;
|
||||
|
||||
/**
|
||||
* 短信服务类
|
||||
*/
|
||||
class SmsService
|
||||
{
|
||||
/**
|
||||
* 验证码有效期(秒)
|
||||
*/
|
||||
const CODE_EXPIRE = 300;
|
||||
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
const CODE_LENGTH = 4;
|
||||
|
||||
/**
|
||||
* 发送验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型 (login, register)
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function sendCode($mobile, $type)
|
||||
{
|
||||
// 检查发送频率限制
|
||||
$this->checkSendLimit($mobile, $type);
|
||||
|
||||
// 生成验证码
|
||||
$code = $this->generateCode();
|
||||
|
||||
// 缓存验证码
|
||||
$this->saveCode($mobile, $code, $type);
|
||||
|
||||
// 发送验证码(实际项目中对接短信平台)
|
||||
$this->doSend($mobile, $code, $type);
|
||||
|
||||
// 记录日志
|
||||
Log::info('发送验证码', [
|
||||
'mobile' => $mobile,
|
||||
'type' => $type,
|
||||
'code' => $code
|
||||
]);
|
||||
|
||||
return [
|
||||
'mobile' => $mobile,
|
||||
'expire' => self::CODE_EXPIRE,
|
||||
// 测试环境返回验证码,生产环境不应返回
|
||||
'code' => $code
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码(可能是加密后的)
|
||||
* @param string $type 验证码类型
|
||||
* @param bool $isEncrypted 验证码是否已加密
|
||||
* @return bool
|
||||
*/
|
||||
public function verifyCode($mobile, $code, $type, $isEncrypted = false)
|
||||
{
|
||||
$cacheKey = $this->getCodeCacheKey($mobile, $type);
|
||||
$cacheCode = Cache::get($cacheKey);
|
||||
|
||||
if (!$cacheCode) {
|
||||
Log::info('验证码不存在或已过期', [
|
||||
'mobile' => $mobile,
|
||||
'type' => $type
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证码是否匹配
|
||||
$isValid = false;
|
||||
|
||||
if ($isEncrypted) {
|
||||
// 前端已加密,需要对缓存中的验证码进行相同的加密处理
|
||||
$encryptedCacheCode = $this->encryptCode($cacheCode);
|
||||
$isValid = hash_equals($encryptedCacheCode, $code);
|
||||
|
||||
// 记录日志
|
||||
Log::info('加密验证码验证', [
|
||||
'mobile' => $mobile,
|
||||
'cache_code' => $cacheCode,
|
||||
'encrypted_cache_code' => $encryptedCacheCode,
|
||||
'input_code' => $code,
|
||||
'is_valid' => $isValid
|
||||
]);
|
||||
} else {
|
||||
// 未加密,直接比较
|
||||
$isValid = ($cacheCode === $code);
|
||||
|
||||
// 记录日志
|
||||
Log::info('明文验证码验证', [
|
||||
'mobile' => $mobile,
|
||||
'cache_code' => $cacheCode,
|
||||
'input_code' => $code,
|
||||
'is_valid' => $isValid
|
||||
]);
|
||||
}
|
||||
|
||||
// 验证成功后删除缓存
|
||||
if ($isValid) {
|
||||
Cache::rm($cacheKey);
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查发送频率限制
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function checkSendLimit($mobile, $type)
|
||||
{
|
||||
$cacheKey = $this->getCodeCacheKey($mobile, $type);
|
||||
|
||||
// 检查是否存在未过期的验证码
|
||||
if (Cache::has($cacheKey)) {
|
||||
throw new \Exception('验证码已发送,请稍后再试');
|
||||
}
|
||||
|
||||
// 检查当日发送次数限制
|
||||
$limitKey = "sms_limit:{$mobile}:" . date('Ymd');
|
||||
$sendCount = Cache::get($limitKey, 0);
|
||||
|
||||
if ($sendCount >= 10) {
|
||||
throw new \Exception('今日发送次数已达上限');
|
||||
}
|
||||
|
||||
// 更新发送次数
|
||||
Cache::set($limitKey, $sendCount + 1, 86400);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机验证码
|
||||
* @return string
|
||||
*/
|
||||
protected function generateCode()
|
||||
{
|
||||
// 生成4位数字验证码
|
||||
return sprintf("%0" . self::CODE_LENGTH . "d", mt_rand(0, pow(10, self::CODE_LENGTH) - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存验证码到缓存
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码
|
||||
* @param string $type 验证码类型
|
||||
*/
|
||||
protected function saveCode($mobile, $code, $type)
|
||||
{
|
||||
$cacheKey = $this->getCodeCacheKey($mobile, $type);
|
||||
Cache::set($cacheKey, $code, self::CODE_EXPIRE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发送验证码
|
||||
* @param string $mobile 手机号
|
||||
* @param string $code 验证码
|
||||
* @param string $type 验证码类型
|
||||
* @return bool
|
||||
*/
|
||||
protected function doSend($mobile, $code, $type)
|
||||
{
|
||||
// 实际项目中对接短信平台API
|
||||
// 这里仅做模拟,返回成功
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码缓存键名
|
||||
* @param string $mobile 手机号
|
||||
* @param string $type 验证码类型
|
||||
* @return string
|
||||
*/
|
||||
protected function getCodeCacheKey($mobile, $type)
|
||||
{
|
||||
return "sms_code:{$mobile}:{$type}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密验证码
|
||||
* 使用与前端相同的加密算法
|
||||
* @param string $code 原始验证码
|
||||
* @return string 加密后的验证码
|
||||
*/
|
||||
protected function encryptCode($code)
|
||||
{
|
||||
// 使用与前端相同的加密算法
|
||||
$salt = 'yishi_salt_2024'; // 与前端相同的盐值
|
||||
return hash('sha256', $code . $salt);
|
||||
}
|
||||
}
|
||||
135
Server/application/common/util/JwtUtil.php
Normal file
135
Server/application/common/util/JwtUtil.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
namespace app\common\util;
|
||||
|
||||
use think\facade\Config;
|
||||
use think\facade\Request;
|
||||
|
||||
/**
|
||||
* JWT工具类
|
||||
* 用于生成和验证JWT令牌
|
||||
*/
|
||||
class JwtUtil
|
||||
{
|
||||
/**
|
||||
* 密钥
|
||||
* @var string
|
||||
*/
|
||||
protected static $secret = 'YiShi@2023#JWT';
|
||||
|
||||
/**
|
||||
* 头部
|
||||
* @var array
|
||||
*/
|
||||
protected static $header = [
|
||||
'alg' => 'HS256', // 加密算法
|
||||
'typ' => 'JWT' // 类型
|
||||
];
|
||||
|
||||
/**
|
||||
* 创建JWT令牌
|
||||
* @param array $payload 载荷信息
|
||||
* @param int $expire 过期时间(秒),默认2小时
|
||||
* @return string
|
||||
*/
|
||||
public static function createToken($payload, $expire = 7200)
|
||||
{
|
||||
$header = self::base64UrlEncode(json_encode(self::$header, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
// 附加过期时间
|
||||
$payload['exp'] = time() + $expire;
|
||||
$payload['iat'] = time(); // 签发时间
|
||||
|
||||
$payload = self::base64UrlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||
$signature = self::signature($header . '.' . $payload, self::$secret);
|
||||
|
||||
return $header . '.' . $payload . '.' . $signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证令牌
|
||||
* @param string $token 令牌
|
||||
* @return array|bool 验证通过返回载荷信息,失败返回false
|
||||
*/
|
||||
public static function verifyToken($token)
|
||||
{
|
||||
if (empty($token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tokenArray = explode('.', $token);
|
||||
if (count($tokenArray) != 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list($header, $payload, $signature) = $tokenArray;
|
||||
|
||||
// 验证签名
|
||||
if (self::signature($header . '.' . $payload, self::$secret) !== $signature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解码载荷
|
||||
$payload = json_decode(self::base64UrlDecode($payload), true);
|
||||
|
||||
// 验证是否过期
|
||||
if (isset($payload['exp']) && $payload['exp'] < time()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* @param string $input 输入
|
||||
* @param string $key 密钥
|
||||
* @return string
|
||||
*/
|
||||
private static function signature($input, $key)
|
||||
{
|
||||
return self::base64UrlEncode(hash_hmac('sha256', $input, $key, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL安全的Base64编码
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private static function base64UrlEncode($input)
|
||||
{
|
||||
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL安全的Base64解码
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private static function base64UrlDecode($input)
|
||||
{
|
||||
$remainder = strlen($input) % 4;
|
||||
if ($remainder) {
|
||||
$input .= str_repeat('=', 4 - $remainder);
|
||||
}
|
||||
return base64_decode(str_replace(['-', '_'], ['+', '/'], $input));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中获取Token
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getRequestToken()
|
||||
{
|
||||
$authorization = Request::header('Authorization');
|
||||
if (!$authorization) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查Bearer前缀
|
||||
if (strpos($authorization, 'Bearer ') !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return substr($authorization, 7);
|
||||
}
|
||||
}
|
||||
52
Server/application/common/validate/Auth.php
Normal file
52
Server/application/common/validate/Auth.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace app\common\validate;
|
||||
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 认证相关验证器
|
||||
*/
|
||||
class Auth extends Validate
|
||||
{
|
||||
/**
|
||||
* 验证规则
|
||||
* @var array
|
||||
*/
|
||||
protected $rule = [
|
||||
'username' => 'require|length:3,20',
|
||||
'password' => 'require|length:6,64',
|
||||
'mobile' => 'require|mobile',
|
||||
'code' => 'require|length:4,6',
|
||||
'is_encrypted' => 'boolean',
|
||||
'type' => 'require|in:login,register',
|
||||
];
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
* @var array
|
||||
*/
|
||||
protected $message = [
|
||||
'username.require' => '用户名不能为空',
|
||||
'username.length' => '用户名长度必须在3-20个字符之间',
|
||||
'password.require' => '密码不能为空',
|
||||
'password.length' => '密码长度必须在6-64个字符之间',
|
||||
'mobile.require' => '手机号不能为空',
|
||||
'mobile.mobile' => '手机号格式不正确',
|
||||
'code.require' => '验证码不能为空',
|
||||
'code.length' => '验证码长度必须在4-6个字符之间',
|
||||
'is_encrypted.boolean' => '加密标志必须为布尔值',
|
||||
'type.require' => '验证码类型不能为空',
|
||||
'type.in' => '验证码类型不正确',
|
||||
];
|
||||
|
||||
/**
|
||||
* 验证场景
|
||||
* @var array
|
||||
*/
|
||||
protected $scene = [
|
||||
'login' => ['username', 'password', 'is_encrypted'],
|
||||
'mobile_login' => ['mobile', 'code', 'is_encrypted'],
|
||||
'refresh' => [],
|
||||
'send_code' => ['mobile', 'type'],
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user