1. 配置中间件

1.1 创建中间键

php think make:middleware Check

  1. <?php
  2. namespace app\http\middleware;
  3. use app\common\TokenVerify;
  4. use think\facade\Request;
  5. class Check
  6. {
  7. // 放行的baseUrl
  8. private static array $release = [
  9. // 放行用户进行注册
  10. 'http://rangeloney.com/index.php/user/index/register',
  11. 'https://rangeloney.com/index.php/user/index/register',
  12. // 测试时放行 login
  13. // 'http://rangeloney.com/index.php/user/index/login',
  14. // 'https://rangeloney.com/index.php/user/index/login',
  15. ];
  16. // 入口方法
  17. public function handle($request, \Closure $next)
  18. {
  19. // 获取 baseUrl,不要查询字符串
  20. $baseUrl = Request::baseUrl(true);
  21. // 请求放行
  22. if (in_array($baseUrl, self::$release) || !isset($token) || $token === '') {
  23. return $next($request);
  24. }
  25. // 获取请求中的 token 和 uid
  26. $token = $request->header('token');
  27. $back = []; // 定义一个用于返回消息的数组
  28. // token 验证
  29. $res = (new TokenVerify())->checkToken($token);
  30. if (!$res) {
  31. list($back['status'], $back['msg']) = ['101', 'token验证失败'];
  32. } else { // 验证成功则用户不需要登录,前台控制页面的跳转
  33. list($back['status'], $back['msg']) = ['100', 'token验证成功'];
  34. }
  35. return json($back);
  36. }
  37. }

1.2 注册中间键

application 目录下创建 middleware.php ,再其中注册中间件

  1. <?php
  2. return [
  3. // 注册这个 check 中间件
  4. 'check' => app\http\middleware\Check::class,
  5. ];

2. Model

在 common文件下建立 user 模型

  1. <?php
  2. namespace app\common\model;
  3. use think\db\exception\DataNotFoundException;
  4. use think\db\exception\ModelNotFoundException;
  5. use think\exception\DbException;
  6. use think\Model;
  7. class User extends Model
  8. {
  9. // 设置当前模型对应的完整数据表名称
  10. protected string $table = 'user';
  11. // 设置主键
  12. protected string $pk = 'IDX';
  13. /**
  14. * 根据用户名和密码查询单个用户信息
  15. * @param $username
  16. * 用户名
  17. * @param $password
  18. * 密码
  19. * @return array|false|string
  20. * 查询成功返回处理后的数组数据,失败返回false
  21. */
  22. public function findUserInfoByUserNameAndPwd($username, $password)
  23. {
  24. try {
  25. $user = User::where([
  26. 'USERNAME' => $username,
  27. 'PASSWORD' => $password
  28. ])->findOrEmpty();
  29. } catch (DataNotFoundException $e) {
  30. return '数据未查到:' . $e;
  31. } catch (ModelNotFoundException $e) {
  32. return '模型未查到:' . $e;
  33. } catch (DbException $e) {
  34. return '数据库连接异常' . $e;
  35. }
  36. // 处理一下数据返回给调用它的控制器
  37. return ($user->isEmpty()) ? false :
  38. [
  39. 'username' => $user['USERNAME'],
  40. 'password' => $user['PASSWORD'],
  41. 'identity' => $user['IDENTITY'],
  42. 'power' => $user['JURISDICTION'],
  43. 'latestLoginTime' => $user['LATEST_LOGIN_TIME'],
  44. 'id' => $user['IDX']
  45. ];
  46. }
  47. }

3. Login 方法

application 目录下创建user模块,user模块下创建Index类

  1. <?php
  2. namespace app\user\controller;
  3. use app\common\TokenVerify;
  4. use think\Controller;
  5. class Index extends Controller
  6. {
  7. /**
  8. * 用户登录:用户名密码正确后签发 token 给客户端端
  9. * @return string|
  10. */
  11. public function Login()
  12. {
  13. $username = input('username'); // 获取用户名
  14. $password = input('password'); // 获取密码
  15. // 获取用户信息
  16. $info = model('common/User')->findUserInfoByUserNameAndPwd($username, $password);
  17. // 未查到相关信息
  18. if (!$info) {
  19. return json([
  20. 'status' => '102',
  21. 'msg' => '密码或用户名输入错误'
  22. ]);
  23. }
  24. // 签发token给客户端
  25. return json((new TokenVerify)->creatToken($info));
  26. }
  27. // 用户注册
  28. public function Register()
  29. {
  30. return '进行用户注册';
  31. // 用户名
  32. // 手机号
  33. // 密码
  34. }
  35. }

4. Token 验证类

application 目录下创建爱你 common 文件夹下 创建 TokenVerify类

  1. <?php
  2. namespace app\common;
  3. use Exception;
  4. use think\Controller;
  5. use \Firebase\JWT\JWT;
  6. use think\facade\Cache;
  7. class TokenVerify extends Controller
  8. {
  9. // redis 键前缀
  10. private static string $redisPrefix = 'xs';
  11. // redis 过期时间
  12. private static int $redisExpire = 7200;
  13. // secret
  14. private static string $secret = '214';
  15. /**
  16. * 于生成一个 token
  17. * @param $info
  18. * 需要用户的部分信息
  19. * @return array
  20. * 返回一个数组,包含:token、uid、身份、权限
  21. */
  22. public function creatToken($info)
  23. {
  24. $secret = self::$secret; // secret
  25. $time = time(); // 当前时间
  26. $uid = md5($info['id'] . $info['username'] . $time); // 生成uid
  27. $payload = [ // payload
  28. 'iss' => 'http://rangeloney.com', // 签发者 可选
  29. 'aud' => [ // 接收该JWT的一方,可选
  30. 'identity' => $info['identity'], // 用户身份
  31. 'power' => $info['power'] // 权限数字
  32. ],
  33. 'iat' => $time, // 签发时间
  34. 'nbf' => $time, // 某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用
  35. 'exp' => $time + 3600 * 2, // 过期时间,这里设置2个小时
  36. 'data' => [ // 自定义信息
  37. 'uid' => $uid,
  38. 'username' => $info['username']
  39. ]
  40. ];
  41. // 将这个 token 存到 redis中,设置键为:前缀 + 用户名,设置值为 uid
  42. $key = self::$redisPrefix . '_' . $info['username'];
  43. Cache::store('redis')->set($key, $uid, self::$redisExpire);
  44. // 返回 token (将其他信息都存在了token中,我并不需要额外设置)
  45. $token = JWT::encode($payload, $secret, 'HS256');
  46. return [
  47. 'token' => $token, // token
  48. ];
  49. }
  50. /**
  51. * token验证
  52. * @param $token
  53. * 客户端传入的 token
  54. * @return bool
  55. * 返回 true 验证通过,返回 false 验证位通过
  56. */
  57. public function checkToken($token)
  58. {
  59. try {
  60. $clientToken = JWT::decode($token, self::$secret, ['HS256']); // 解码来自客户端的 token
  61. $tokenArr = self::object_array($clientToken); // stdClass 转 array
  62. list($username, $uid) = [$tokenArr['data']['username'], $tokenArr['data']['uid']];
  63. $redis = Cache::store('redis')->handler();
  64. $key = self::$redisPrefix . '_' . $username;
  65. // 如果这个键不存在则验证失败
  66. if (!$redis->exists($key)) {
  67. return false;
  68. }
  69. // 从新设置这个 token 的过期时间,达到延长的效果
  70. Cache::store('redis')->set($key, $uid, self::$redisExpire);
  71. } catch (Exception $e) {
  72. return false;
  73. }
  74. return true;
  75. }
  76. // PHP stdClass Object转 array
  77. private static function object_array($array)
  78. {
  79. if (is_object($array)) {
  80. $array = (array)$array;
  81. }
  82. if (is_array($array)) {
  83. foreach ($array as $key => $value) {
  84. $array[$key] = self::object_array($value);
  85. }
  86. }
  87. return $array;
  88. }
  89. }

5. 小结

5.1 其它相关说明