8 changed files with 479 additions and 6 deletions
@ -1,2 +1,60 @@ |
|||
<?php |
|||
// 应用公共文件 |
|||
use \Firebase\JWT\JWT; |
|||
use Firebase\JWT\Key; |
|||
|
|||
/** |
|||
* 生成验签 |
|||
* @param $data |
|||
* @return string |
|||
* @author whj |
|||
* @date 2023-08-22 15:31 |
|||
*/ |
|||
function signToken($data): string |
|||
{ |
|||
$key = 'LAL@lc!'; //这里是自定义的一个随机字串,应该写在config文件中的,解密时也会用,相当于加密中常用的 盐-salt |
|||
$token = array( |
|||
"iss" => $key, //签发者 可以为空 |
|||
"aud" => '', //面象的用户,可以为空 |
|||
"iat" => time(), //签发时间 |
|||
"nbf" => time() + 3, //在什么时候jwt开始生效 (这里表示生成100秒后才生效) |
|||
"exp" => time() + 7200, //token 过期时间 |
|||
"data" => $data //记录的userid的信息,这里是自已添加上去的,如果有其它信息,可以再添加数组的键值对 |
|||
); |
|||
return JWT::encode($token, $key, "HS384"); //根据参数生成了token,可选:HS256、HS384、HS512、RS256、ES256等 |
|||
} |
|||
|
|||
/** |
|||
* 验证token |
|||
* @param $token |
|||
* @return array|int[] |
|||
* @author whj |
|||
* @date 2023-08-22 15:31 |
|||
*/ |
|||
function checkToken($token): array |
|||
{ |
|||
$key = 'LAL@lc!'; |
|||
$status = array("code" => 2); |
|||
try { |
|||
JWT::$leeway = 60; //当前时间减去60,把时间留点余地 |
|||
$decoded = JWT::decode($token, new Key($key, 'HS384')); //同上的方式,这里要和签发的时候对应 |
|||
$arr = (array)$decoded; |
|||
$res['code'] = 200; |
|||
$res['data'] = $arr['data']; |
|||
$res['data'] = json_decode(json_encode($res['data']), true);//将stdObj类型转换为array |
|||
return $res; |
|||
|
|||
} catch (\Firebase\JWT\SignatureInvalidException $e) { //签名不正确 |
|||
$status['msg'] = "签名不正确"; |
|||
return $status; |
|||
} catch (\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用 |
|||
$status['msg'] = "token失效"; |
|||
return $status; |
|||
} catch (\Firebase\JWT\ExpiredException $e) { // token过期 |
|||
$status['msg'] = "token失效"; |
|||
return $status; |
|||
} catch (Exception $e) { //其他错误 |
|||
$status['msg'] = "未知错误"; |
|||
return $status; |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
<?php |
|||
declare (strict_types = 1); |
|||
|
|||
namespace app\middleware; |
|||
|
|||
use think\Exception; |
|||
use \think\facade\Request; |
|||
|
|||
class CheckToken |
|||
{ |
|||
/** |
|||
* |
|||
* 处理请求 |
|||
* @param $request |
|||
* @param \Closure $next |
|||
* @return mixed|\think\response\Json |
|||
* @author whj |
|||
* @date 2023-08-22 14:57 |
|||
*/ |
|||
public function handle($request, \Closure $next) |
|||
{ |
|||
try { |
|||
$token = Request::header()['token']??false; |
|||
if(!$token) |
|||
throw new Exception('Without Token',201); |
|||
$userinfo = checkToken($token); |
|||
if($userinfo['code'] != 200) |
|||
throw new Exception('Token checked error',202); |
|||
$request->userInfo = $userinfo['data']; |
|||
} |
|||
catch (\Exception $err){ |
|||
return json(['code'=>$err->getCode(),'msg'=>$err->getMessage()]); |
|||
} |
|||
return $next($request); |
|||
} |
|||
} |
|||
@ -0,0 +1,178 @@ |
|||
<?php |
|||
|
|||
namespace app\model; |
|||
|
|||
use think\Model; |
|||
|
|||
class User extends Model |
|||
{ |
|||
/** |
|||
* 注册用户 |
|||
* @param $data |
|||
* @return bool |
|||
* @throws \think\db\exception\DataNotFoundException |
|||
* @throws \think\db\exception\DbException |
|||
* @throws \think\db\exception\ModelNotFoundException |
|||
*/ |
|||
public function register($data) |
|||
{ |
|||
|
|||
$user = $this->where('phone', $data['phone'])->find(); |
|||
if ($user) return false; |
|||
|
|||
// 生成盐值 |
|||
$salt = $this->generateSalt(); |
|||
// 密码加盐值后哈希存储 |
|||
$password = $this->generateHashedPassword($data['password'], $salt); |
|||
|
|||
$this->save([ |
|||
'username' => $this->generateRandomUsername(), |
|||
'password' => $password, |
|||
'salt' => $salt, |
|||
'phone' => $data['phone'], |
|||
'invite_code' => $data['invite_code'], |
|||
'register_time' => date("Y-m-d H:i:s",time()) |
|||
]); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 用户登录 |
|||
* @param $data |
|||
* @return User|array|mixed|Model |
|||
* @throws \think\db\exception\DataNotFoundException |
|||
* @throws \think\db\exception\DbException |
|||
* @throws \think\db\exception\ModelNotFoundException |
|||
* @author whj |
|||
* @date 2023-08-22 16:28 |
|||
*/ |
|||
public function login($data) |
|||
{ |
|||
// 根据用户名查询用户信息 |
|||
$user = $this->where('phone', $data['phone'])->find(); |
|||
|
|||
if ($user) { |
|||
// 使用相同的盐值对输入密码进行哈希验证 |
|||
$hashedPassword = $this->generateHashedPassword($data['password'], $user->salt); |
|||
|
|||
if ($user->password === $hashedPassword) { |
|||
// 登录成功 |
|||
return ['status' => true, 'msg' => '手机号未注册', 'data' => $user]; |
|||
} else { |
|||
|
|||
return ['status' => false, 'msg' => '密码错误']; |
|||
} |
|||
} |
|||
|
|||
return ['status' => false, 'msg' => '手机号未注册']; |
|||
} |
|||
|
|||
/** |
|||
* 找回密码 |
|||
* @param $data |
|||
* @return array |
|||
* @throws \think\db\exception\DataNotFoundException |
|||
* @throws \think\db\exception\DbException |
|||
* @throws \think\db\exception\ModelNotFoundException |
|||
*/ |
|||
public function retrieve($data) |
|||
{ |
|||
// 根据用户名查询用户信息 |
|||
$user = $this->where('phone', $data['phone'])->find(); |
|||
|
|||
if ($user) { |
|||
|
|||
// 生成盐值 |
|||
$salt = $this->generateSalt(); |
|||
// 密码加盐值后哈希存储 |
|||
$user->password = $this->generateHashedPassword($data['password'], $salt); |
|||
$user->salt = $salt; |
|||
$user->update_time = date("Y-m-d H:i:s",time()); |
|||
$this->save(); |
|||
|
|||
return ['status' => true, 'msg' => '密码重制成功']; |
|||
} |
|||
|
|||
return ['status' => false, 'msg' => '手机号未注册']; |
|||
} |
|||
|
|||
/** |
|||
* 手机号短信验证码验证 |
|||
* @param $phone |
|||
* @param $smsCode |
|||
* @return bool |
|||
*/ |
|||
public function verifySmsCode($phone, $smsCode) |
|||
{ |
|||
// 在这个方法中,您可以调用您的短信服务提供商的API进行验证码验证 |
|||
// 这里简化为直接比较验证码 |
|||
// 请根据实际情况自行实现验证码验证逻辑 |
|||
|
|||
// 假设存储了正确的短信验证码 |
|||
$correctSmsCode = '123456'; |
|||
|
|||
if ($smsCode === $correctSmsCode) { |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 验证邀请码是否有效 |
|||
* @param $invite_code |
|||
* @return bool |
|||
* @throws \think\db\exception\DataNotFoundException |
|||
* @throws \think\db\exception\DbException |
|||
* @throws \think\db\exception\ModelNotFoundException |
|||
*/ |
|||
public function verifyInviteCode($invite_code) |
|||
{ |
|||
$codeRes = $this->where('invite_code',$invite_code)->find(); |
|||
|
|||
if (empty($codeRes)) { |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
/** |
|||
* 生成盐值 |
|||
* @return string |
|||
*/ |
|||
private function generateSalt() |
|||
{ |
|||
return $this->generateRandomUsername(6); |
|||
} |
|||
|
|||
/** |
|||
* 密码加盐值后哈希存储 |
|||
* @param $password |
|||
* @param $salt |
|||
* @return string |
|||
*/ |
|||
private function generateHashedPassword($password, $salt) |
|||
{ |
|||
return md5(md5($password) . md5($salt)); |
|||
} |
|||
|
|||
/** |
|||
* 获取随机用户命 |
|||
* @param $length |
|||
* @return string |
|||
*/ |
|||
private function generateRandomUsername($length = 8) { |
|||
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
|||
$username = ''; |
|||
|
|||
$maxIndex = strlen($characters) - 1; |
|||
for ($i = 0; $i < $length; $i++) { |
|||
$randomIndex = mt_rand(0, $maxIndex); |
|||
$username .= $characters[$randomIndex]; |
|||
} |
|||
|
|||
return $username; |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
<?php |
|||
declare (strict_types = 1); |
|||
|
|||
namespace app\validate; |
|||
|
|||
use think\Validate; |
|||
|
|||
class User extends Validate |
|||
{ |
|||
/** |
|||
* 定义验证规则 |
|||
* 格式:'字段名' => ['规则1','规则2'...] |
|||
* |
|||
* @var array |
|||
*/ |
|||
protected $rule = [ |
|||
'phone' => 'require|mobile', |
|||
'password' => 'require|min:6|max:20', |
|||
'sms_code' => 'require', |
|||
]; |
|||
|
|||
/** |
|||
* 定义错误信息 |
|||
* 格式:'字段名.规则名' => '错误信息' |
|||
* |
|||
* @var array |
|||
*/ |
|||
protected $message = [ |
|||
'phone.require' => '手机号必填', |
|||
'phone.mobile' => '手机号不正确', |
|||
'password.require' => '密码必填', |
|||
'password.min' => '密码长度最短为6个字符', |
|||
'password.max' => '密码长度最长为20个字符', |
|||
'sms_code' => '短信验证码必填' |
|||
]; |
|||
|
|||
protected $scene = [ |
|||
'login' => ['phone','password'], |
|||
'register' => ['phone','password','sms_code'], |
|||
'retrieve' => ['phone','password','sms_code'] |
|||
]; |
|||
} |
|||
Loading…
Reference in new issue