diff --git a/app/admin/controller/user/Admin.php b/app/admin/controller/user/Admin.php
index 81f2a48..8a44f5b 100644
--- a/app/admin/controller/user/Admin.php
+++ b/app/admin/controller/user/Admin.php
@@ -76,6 +76,7 @@ class Admin extends AdminController
}
$save ? $this->success('保存成功') : $this->error('保存失败');
}
+ $this->assign('genderArr', $this->model->genderArr);
return $this->fetch();
}
@@ -103,6 +104,7 @@ class Admin extends AdminController
$save ? $this->success('保存成功') : $this->error('保存失败');
}
$this->assign('row', $row);
+ $this->assign('genderArr', $this->model->genderArr);
return $this->fetch();
}
diff --git a/app/admin/model/User.php b/app/admin/model/User.php
index b03b214..e6852bd 100644
--- a/app/admin/model/User.php
+++ b/app/admin/model/User.php
@@ -11,4 +11,7 @@ class User extends TimeModel
protected $defaultSoftDelete = '0';
+ public $genderArr = [
+ 1 => '男', 2 => '女', 3 => '未知'
+ ];
}
\ No newline at end of file
diff --git a/app/admin/view/user/admin/add.html b/app/admin/view/user/admin/add.html
index a867f2e..822bfee 100644
--- a/app/admin/view/user/admin/add.html
+++ b/app/admin/view/user/admin/add.html
@@ -47,9 +47,9 @@
diff --git a/app/admin/view/user/admin/edit.html b/app/admin/view/user/admin/edit.html
index 916d2ea..9bb06e0 100644
--- a/app/admin/view/user/admin/edit.html
+++ b/app/admin/view/user/admin/edit.html
@@ -23,9 +23,9 @@
diff --git a/app/api/controller/ApiController.php b/app/api/controller/ApiController.php
index e7c0c81..ba1a05c 100644
--- a/app/api/controller/ApiController.php
+++ b/app/api/controller/ApiController.php
@@ -1,6 +1,7 @@
post();
// 用户密码
$salt = isset($data['salt'])?$data['salt']:makeSalt(6);
- $arr['encpass']=password($data['upass']);
+
+ $arr['encpass']=password($data['upass'].$salt);
$arr['salt']=$salt;
} else {
$arr = ["ver" => "00", "date" => time()];
@@ -59,30 +67,50 @@ class Index extends BaseController
//
$config = Configuration::forSymmetricSigner($signer,$key);
+
+ //$builder = new Builder();
+ // 设置发行时间和过期时间
$now = new DateTimeImmutable(); // 当前时间
+ // 设置发行时间和过期时间
+ $secondsToAdd = (int) config('jwt.token_ttl');
+ $expiresAt = $now->add(new \DateInterval('PT' . $secondsToAdd . 'S'));
+
+ // $token = $builder
+ // ->issuedAt($now) // iat: 发行时间
+ // ->expiresAt($expiresAt) // exp: 过期时间
+ // ->withIssuer('iss', 'xtt') // iss: 发行人
+ // ->withSubject('sub', 'xtoken') // sub: 主题
+ // ->withAudience('aud', 'ttc'); // aud: 受众
+
$token = $config->builder()
// 签发人
- ->issuedBy('https://douyin.xingtongworld.com/')
+ ->issuedBy('https://douyin.xingtongworld.com/')
// 受众
- ->permittedFor('https://douyin.xingtongworld.com/')
+ ->permittedFor('https://douyin.xingtongworld.com/')
// JWT ID 编号 唯一标识
- ->identifiedBy($claims['id'])
+ ->identifiedBy($claims['id'])
// 签发时间
- ->issuedAt($now)
+ ->issuedAt($now)
// 在1分钟后才可使用
-// ->canOnlyBeUsedAfter($now->modify('+1 minute'))
+ // ->canOnlyBeUsedAfter($now->modify('+1 minute'))
// 过期时间1小时
- ->expiresAt($now->modify('+1 hour'))
+ ->expiresAt($now->modify('+1 hour'))
// 自定义uid 额外参数
- ->withClaim('uid', $claims['id'])
- ->withClaim('name',$claims['nick_name'])
+ ->withClaim('uid', $claims['id'])
+ ->withClaim('name',$claims['nick_name'])
// 自定义header 参数
// ->withHeader('foo', 'bar')
// 生成token
- ->getToken($config->signer(), $config->signingKey());
+ ->getToken($config->signer(), $config->signingKey());
// base64
return $token->toString();
+
+ // foreach ($claims as $key => $value) {
+ // $token = $token->withClaim($key, $value);
+ // }
+
+ // return (string) $token->sign($signer, $key);
}
}
\ No newline at end of file
diff --git a/app/api/controller/Passport.php b/app/api/controller/Passport.php
index c473e46..c99d2d5 100644
--- a/app/api/controller/Passport.php
+++ b/app/api/controller/Passport.php
@@ -1,90 +1,79 @@
[
+ 'except' => ['login', 'register']
+ ]
+ ];
+
/**
* 登录
*/
- public function login():Json
+ public function login(): Json
{
if (!$this->request->isPost()) {
return $this->renderError('不支持GET请求');
}
$data = $this->postData();
+ try {
+ validate()->rule([
+ 'uname|用户名' => 'require',
+ 'upass|密码' => 'require',
+ ])->check($data);
+ } catch (ValidateException $v) {
+ return $this->renderError($v->getMessage());
+ }
$model = new UserService;
- //
- $data = $this->postData();
- if (($userInfo = $model->login($data['uname'],$data['upass'])) === false) {
+ if (($userInfo = $model->login($data['uname'], $data['upass'])) === false) {
return $this->renderError($model->getError() ?: '登录失败');
}
return $this->renderSuccess([
'userId' => $userInfo['uid'],
- 'token' => $model->getToken($userInfo['uid'],$userInfo['nick_name'])
- ], '');
+ 'token' => LcJWTService::createToken($userInfo['uid'], $data['uname'])
+ ], '登录成功');
}
/**
* 用户注册
* @return Json
*/
- public function register():Json
+ public function register(): Json
{
if (!$this->request->isPost()) {
return $this->renderError('不支持GET请求');
}
+ $post = $this->postData();
+ try {
+ validate()->rule([
+ 'uname|用户名' => 'require',
+ 'upass|密码' => 'require',
+ 'qr_upass|确认密码' => 'require|confirm:upass',
+ 'phone|手机号' => 'require|mobile'
+ ])->check($post);
+ } catch (ValidateException $v) {
+ return $this->renderError($v->getMessage());
+ }
$model = new UserService;
- if (($userInfo = $model->register($this->postData())) === false) {
+ if (($userInfo = $model->register($post)) === false) {
return $this->renderError($model->getError() ?: '注册失败');
}
return $this->renderSuccess("注册成功");
}
-
-}
-
-// app/controller/Index.php
-// namespace app\controller;
-
-// use app\service\JWTService;
-// use think\Request;
-
-// class IndexController
-// {
-// protected $jwtService;
-
-// public function __construct(JWTService $jwtService)
-// {
-// $this->jwtService = $jwtService;
-// }
-
-// public function login(Request $request)
-// {
-// $username = $request->param('username');
-// $password = $request->param('password');
-
-// // 假设验证用户名和密码成功
-// $claims = ['id' => 1, 'username' => $username];
-
-// $token = $this->jwtService->createToken($claims);
-
-// return json(['token' => $token]);
-// }
-
-// public function protectedRoute(Request $request)
-// {
-// $claims = $request->attributes->get('claims');
-
-// if (empty($claims)) {
-// return json(['error' => 'Unauthorized'], 401);
-// }
-
-// return json(['message' => 'Welcome, ' . $claims['username']]);
-// }
-// }
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/app/api/controller/User.php b/app/api/controller/User.php
new file mode 100644
index 0000000..e4d852e
--- /dev/null
+++ b/app/api/controller/User.php
@@ -0,0 +1,32 @@
+request->isPost()) {
+ return $this->renderError('不支持GET请求');
+ }
+ $data = $this->postData();
+ $model = new UserModel();
+ $field = 'password,salt,last_login_time,create_time,update_time,delete_time';
+ $row = $model->where('uid', $data['uid'])->withoutField($field)->find();
+ if ($row->isEmpty()) {
+ return $this->renderError($model->getError() ?: '用户不存在');
+ }
+ if ($row['status'] != 1) return $this->renderError('用户被禁用');
+ unset($row['status']);
+ if ($row['gender']) {
+ $row['gender'] = $model->genderArr[$row['gender']] ?? '';
+ }
+ return $this->renderSuccess($row->toArray());
+ }
+
+}
\ No newline at end of file
diff --git a/app/api/middleware.php b/app/api/middleware.php
index 576c8bd..cc32af2 100644
--- a/app/api/middleware.php
+++ b/app/api/middleware.php
@@ -8,9 +8,4 @@ return [
// 系统操作日志
// \app\admin\middleware\SystemLog::class,
- // Csrf安全校验
-// \app\api\middleware\CsrfMiddleware::class,
- // jwt 中间件
- \app\api\middleware\JWTAuth::class,
-
];
\ No newline at end of file
diff --git a/app/api/middleware/CsrfMiddleware.php b/app/api/middleware/CsrfMiddleware.php
deleted file mode 100644
index 2d7e378..0000000
--- a/app/api/middleware/CsrfMiddleware.php
+++ /dev/null
@@ -1,40 +0,0 @@
-method(), ['GET', 'HEAD', 'OPTIONS'])) {
-
- // 跨域校验
- $refererUrl = $request->header('REFERER', null);
- $refererInfo = parse_url($refererUrl);
- $host = $request->host(true);
- if (!isset($refererInfo['host']) || $refererInfo['host'] != $host) {
- $this->error('当前请求不合法!');
- }
-
- // CSRF校验
- $ckCsrfToken = $request->post('ckCsrfToken', null);
- $data = !empty($ckCsrfToken) ? ['__token__' => $ckCsrfToken] : [];
-
- $check = $request->checkToken('__token__', $data);
- if (!$check) {
- $this->error('请求验证失败,请重新刷新页面!');
- }
-
- }
- }
- return $next($request);
- }
-}
diff --git a/app/api/middleware/JWTAuth.php b/app/api/middleware/JWTAuth.php
deleted file mode 100644
index 33be46e..0000000
--- a/app/api/middleware/JWTAuth.php
+++ /dev/null
@@ -1,36 +0,0 @@
-jwtService = $jwtService;
- }
-
- public function handle(Request $request, \Closure $next)
- {
- $token = $request->header('Authorization');
-
- if (!$token) {
- throw new Exception('Missing token');
- }
-
- $claims = $this->jwtService->verifyToken($token);
-
- if (empty($claims)) {
- throw new Exception('Invalid token');
- }
-
- // 将 claims 存储到 request 对象中
- $request->attributes->set('claims', $claims);
-
- return $next($request);
- }
-}
\ No newline at end of file
diff --git a/app/api/middleware/LcJWTAuth.php b/app/api/middleware/LcJWTAuth.php
new file mode 100644
index 0000000..dfbfabd
--- /dev/null
+++ b/app/api/middleware/LcJWTAuth.php
@@ -0,0 +1,29 @@
+header('token');
+
+ if (!$token) {
+ return json(['code' => 401, 'msg' => 'token cannot be empty']);
+ }
+
+ $data = LcJWTService::parseToken($token);
+ $claims = $data['claims'];
+
+ $validateRes = LcJWTService::validationToken($token, $claims->get('uid'));
+
+ if ($validateRes !== true) {
+ return json(['code' => 401, 'msg' => 'token verification failed']);
+ }
+
+ return $next($request);
+ }
+}
\ No newline at end of file
diff --git a/app/api/service/JWTService.php b/app/api/service/JWTService.php
deleted file mode 100644
index d13e54d..0000000
--- a/app/api/service/JWTService.php
+++ /dev/null
@@ -1,147 +0,0 @@
-secret = config('jwt.secret');
- }
-
- public function createToken(array $claims): string
- {
- $signer = new Sha256();
- $key = InMemory::plainText($this->secret);
-
- $now = new DateTimeImmutable(); // 当前时间
-
- $builder = new Builder();
-
- // 设置发行时间和过期时间
- $secondsToAdd = (int) config('jwt.token_ttl');
- $expiresAt = $now->add(new \DateInterval('PT' . $secondsToAdd . 'S'));
-
- $token = $builder
- ->issuedAt($now) // iat: 发行时间
- ->expiresAt($expiresAt) // exp: 过期时间
-
- // 使用专用方法设置注册声明
- ->withIssuer('your_issuer') // iss: 发行人
- ->withSubject('your_subject') // sub: 主题
- ->withAudience('your_audience'); // aud: 受众
-
- // 添加自定义 Claims
- foreach ($claims as $key => $value) {
- $token = $token->withClaim($key, $value); // 自定义声明仍然可以使用 withClaim
- }
-
- // 构建并签名 Token
- $signedToken = $token->sign($signer, $key);
-
- return (string) $signedToken;
- }
-
- public function verifyToken(string $token): array
- {
- try {
- $parser = new \Lcobucci\JWT\Parser();
- $token = $parser->parse($token);
-
- if ($token->verify(new Sha256(), InMemory::plainText($this->secret))) {
- return $token->getClaims();
- }
- } catch (\Exception $e) {
- // 处理异常
- }
-
- return [];
- }
-}
-// app/service/JWTService.php
-namespace app\service;
-
-use Lcobucci\JWT\Builder;
-use Lcobucci\JWT\Signer\Hmac\Sha256;
-use Lcobucci\JWT\Signer\Key\InMemory;
-use Lcobucci\JWT\Token;
-use DateTimeImmutable;
-
-class JWTService
-{
- private $secret;
-
- public function __construct()
- {
- $this->secret = config('jwt.secret');
- }
-
- public function createToken(array $claims): string
- {
- $signer = new Sha256();
- $key = InMemory::plainText($this->secret);
-
- $now = new DateTimeImmutable(); // 当前时间
-
- $builder = new Builder();
-
- // 设置发行时间和过期时间
- $secondsToAdd = (int) config('jwt.token_ttl');
- $expiresAt = $now->add(new \DateInterval('PT' . $secondsToAdd . 'S'));
-
- $token = $builder
- ->issuedAt($now) // iat: 发行时间
- ->expiresAt($expiresAt) // exp: 过期时间
-
- // 使用辅助方法设置注册声明
- ->withRegisteredClaim('iss', 'your_issuer') // iss: 发行人
- ->withRegisteredClaim('sub', 'your_subject') // sub: 主题
- ->withRegisteredClaim('aud', 'your_audience'); // aud: 受众
-
- // 添加自定义 Claims
- foreach ($claims as $key => $value) {
- $token = $token->withClaim($key, $value); // 自定义声明仍然可以使用 withClaim
- }
-
- // 构建并签名 Token
- $signedToken = $token->sign($signer, $key);
-
- return (string) $signedToken;
- }
-
- public function verifyToken(string $token): array
- {
- try {
- $parser = new \Lcobucci\JWT\Parser();
- $token = $parser->parse($token);
-
- if ($token->verify(new Sha256(), InMemory::plainText($this->secret))) {
- return $token->getClaims();
- }
- } catch (\Exception $e) {
- // 处理异常
- }
-
- return [];
- }
-
- /**
- * 辅助方法用于设置注册声明。
- *
- * @param string $claimName
- * @param mixed $claimValue
- * @return \Lcobucci\JWT\Builder
- */
- private function withRegisteredClaim(string $claimName, $claimValue): Builder
- {
- return (new Builder())->withClaim($claimName, $claimValue);
- }
-}
\ No newline at end of file
diff --git a/app/api/service/LcJWTService.php b/app/api/service/LcJWTService.php
new file mode 100644
index 0000000..46dd577
--- /dev/null
+++ b/app/api/service/LcJWTService.php
@@ -0,0 +1,128 @@
+builder()
+ // 签发人
+ ->issuedBy(self::$url)
+ // 受众
+ ->permittedFor(self::$url)
+ // JWT ID 编号 唯一标识
+ ->identifiedBy($userId)
+ // 签发时间
+ ->issuedAt($now)
+ // 在1分钟后才可使用
+ // ->canOnlyBeUsedAfter($now->modify('+1 minute'))
+ // 过期时间1小时
+ ->expiresAt($now->modify('+1 hour'))
+ // 自定义uid 额外参数
+ ->withClaim('uid', $userId)
+ ->withClaim('name', $uname)
+ // 自定义header 参数
+ ->withHeader('foo', 'bar')
+ // 生成token
+ ->getToken($config->signer(), $config->signingKey());
+
+ return $token->toString();
+ }
+
+ /**
+ * 解析令牌
+ */
+ public static function parseToken(string $token)
+ {
+ $config = self::getConfig();
+ assert($config instanceof Configuration);
+
+ $token = $config->parser()->parse($token);
+
+ assert($token instanceof Plain);
+
+ return [
+ 'header' => $token->headers(),// Retrieves the token headers
+ 'claims' => $token->claims()// Retrieves the token claims
+ ];
+ }
+
+ /**
+ * 验证令牌
+ */
+ public static function validationToken(string $token, int $userId)
+ {
+ $config = self::getConfig();
+ assert($config instanceof Configuration);
+
+ $token = $config->parser()->parse($token);
+ assert($token instanceof Plain);
+
+ //Lcobucci\JWT\Validation\Constraint\IdentifiedBy: 验证jwt id是否匹配
+ //Lcobucci\JWT\Validation\Constraint\IssuedBy: 验证签发人参数是否匹配
+ //Lcobucci\JWT\Validation\Constraint\PermittedFor: 验证受众人参数是否匹配
+ //Lcobucci\JWT\Validation\Constraint\RelatedTo: 验证自定义cliam参数是否匹配
+ //Lcobucci\JWT\Validation\Constraint\SignedWith: 验证令牌是否已使用预期的签名者和密钥签名
+ //Lcobucci\JWT\Validation\Constraint\ValidAt: 验证要求iat,nbf和exp(支持余地配置)
+
+ //验证jwt id是否匹配
+ $validate_jwt_id = new \Lcobucci\JWT\Validation\Constraint\IdentifiedBy($userId);
+ $config->setValidationConstraints($validate_jwt_id);
+ //验证签发人url是否正确
+ $validate_issued = new \Lcobucci\JWT\Validation\Constraint\IssuedBy(self::$url);
+ $config->setValidationConstraints($validate_issued);
+ //验证客户端url是否匹配
+ $validate_aud = new \Lcobucci\JWT\Validation\Constraint\PermittedFor(self::$url);
+ $config->setValidationConstraints($validate_aud);
+
+ //验证是否过期
+ $timezone = new \DateTimeZone('Asia/Shanghai');
+ $now = new \Lcobucci\Clock\SystemClock($timezone);
+ $validate_jwt_at = new \Lcobucci\JWT\Validation\Constraint\ValidAt($now);
+ $config->setValidationConstraints($validate_jwt_at);
+
+ $constraints = $config->validationConstraints();
+
+ try {
+ $config->validator()->assert($token, ...$constraints);
+ return true;
+ } catch (RequiredConstraintsViolated $e) {
+ // list of constraints violation exceptions:
+ return $e->getMessage();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/api/service/UserService.php b/app/api/service/UserService.php
index 2cb79a9..8fab6cc 100644
--- a/app/api/service/UserService.php
+++ b/app/api/service/UserService.php
@@ -1,7 +1,11 @@
$uname,'delete_time'=>0])->find();
+ $drs = User::where(['nick_name' => $uname, 'delete_time' => 0])->find();
// 异常处理
- if(!isset($drs)){
+ if (!isset($drs)) {
throwError('用户不存在');
return -1;
- }else{
- $fpass = password($pass.$drs['salt']);
+ } else {
+ $fpass = password($pass . $drs['salt']);
+ // var_dump($fpass.'|'.$drs['password']);
+ // exit;
// 对比密码
- if($drs['password'] != $fpass){
+ if ($drs['password'] != $fpass) {
throwError('密码错误');
return false;
}
@@ -43,75 +53,27 @@ class UserService {
}
/**
- * @param array $arr
+ * @param array $data
* @return bool
*/
- public function register(array $arr){
+ public function register(array $data): bool
+ {
+ $arr = [
+ 'nick_name' => $data['uname'],
+ 'password' => $data['upass'],
+ 'mobile' => $data['phone'],
+ ];
$salt = makeSalt(6);
// 密码加密
- $arr['password'] = password($arr['password'].$salt);
+ $arr['password'] = password($arr['password'] . $salt);
// 生成salt
- $arr['salt'] = $salt;
- $dtime =time();
+ $arr['salt'] = $salt;
+ $dtime = time();
$arr['create_time'] = $dtime;
$arr['update_time'] = $dtime;
// 保存
$model = new User;
- $uid = $model->save($arr);
- return isset($uid)?true:false;
- }
-
- /**
- * 根据uid,nick_name 换取JWT
- * @param int $userId
- * @param string $uname
- * @return string
- * @throws \Exception
- */
- public function getToken(int $userId,string $uname): string
- {
- static $token = '';
- if (empty($token)) {
- $token = $this->makeToken($userId,$uname);
- }
- return $token;
- }
-
- /**
- * 生成JWT
- * @param int $userId
- * @param string $uname
- * @return string
- * @throws \Exception
- */
- private function makeToken(int $userId,string $uname): string
- {
- $signer = new Sha256();
- $key = InMemory::plainText(config('jwt.secret'));
- //
- $config = Configuration::forSymmetricSigner($signer,$key);
- $now = new DateTimeImmutable(); // 当前时间
- //
- $token = $config->builder()
- // 签发人
- ->issuedBy('https://douyin.xingtongworld.com/')
- // 受众
- ->permittedFor('https://douyin.xingtongworld.com/')
- // JWT ID 编号 唯一标识
- ->identifiedBy($userId)
- // 签发时间
- ->issuedAt($now)
- // 在1分钟后才可使用
-// ->canOnlyBeUsedAfter($now->modify('+1 minute'))
- // 过期时间1小时
- ->expiresAt($now->modify('+1 hour'))
- // 自定义uid 额外参数
- ->withClaim('uid', $userId)
- ->withClaim('name',$uname)
- // 自定义header 参数
- // ->withHeader('foo', 'bar')
- // 生成token
- ->getToken($config->signer(), $config->signingKey());
- return $token->toString();
+ $uid = $model->save($arr);
+ return (bool)$uid;
}
}
\ No newline at end of file
diff --git a/composer.json b/composer.json
index f1a5d55..451126e 100644
--- a/composer.json
+++ b/composer.json
@@ -23,13 +23,15 @@
"topthink/think-view": "^1.0",
"topthink/think-captcha": "^3.0",
"aliyuncs/oss-sdk-php": "^2.3",
+ "firebase/php-jwt": "6.4",
"qcloud/cos-sdk-v5": "^2.0",
"qiniu/php-sdk": "^7.2",
"alibabacloud/client": "^1.5",
"jianyan74/php-excel": "^1.0",
"zhongshaofa/easy-admin": "^1.0.2",
"ext-json": "*",
- "zhongshaofa/thinkphp-log-trace": "^1.0"
+ "zhongshaofa/thinkphp-log-trace": "^1.0",
+ "lcobucci/jwt": "3.4"
},
"require-dev": {
"symfony/var-dumper": "^4.2",