1. 配置中间件
1.1 创建中间键
php think make:middleware Check
<?phpnamespace app\http\middleware;use app\common\TokenVerify;use think\facade\Request;class Check{// 放行的baseUrlprivate static array $release = [// 放行用户进行注册'http://rangeloney.com/index.php/user/index/register','https://rangeloney.com/index.php/user/index/register',// 测试时放行 login// 'http://rangeloney.com/index.php/user/index/login',// 'https://rangeloney.com/index.php/user/index/login',];// 入口方法public function handle($request, \Closure $next){// 获取 baseUrl,不要查询字符串$baseUrl = Request::baseUrl(true);// 请求放行if (in_array($baseUrl, self::$release) || !isset($token) || $token === '') {return $next($request);}// 获取请求中的 token 和 uid$token = $request->header('token');$back = []; // 定义一个用于返回消息的数组// token 验证$res = (new TokenVerify())->checkToken($token);if (!$res) {list($back['status'], $back['msg']) = ['101', 'token验证失败'];} else { // 验证成功则用户不需要登录,前台控制页面的跳转list($back['status'], $back['msg']) = ['100', 'token验证成功'];}return json($back);}}
1.2 注册中间键
application 目录下创建 middleware.php ,再其中注册中间件
<?phpreturn [// 注册这个 check 中间件'check' => app\http\middleware\Check::class,];
2. Model
在 common文件下建立 user 模型
<?phpnamespace app\common\model;use think\db\exception\DataNotFoundException;use think\db\exception\ModelNotFoundException;use think\exception\DbException;use think\Model;class User extends Model{// 设置当前模型对应的完整数据表名称protected string $table = 'user';// 设置主键protected string $pk = 'IDX';/*** 根据用户名和密码查询单个用户信息* @param $username* 用户名* @param $password* 密码* @return array|false|string* 查询成功返回处理后的数组数据,失败返回false*/public function findUserInfoByUserNameAndPwd($username, $password){try {$user = User::where(['USERNAME' => $username,'PASSWORD' => $password])->findOrEmpty();} catch (DataNotFoundException $e) {return '数据未查到:' . $e;} catch (ModelNotFoundException $e) {return '模型未查到:' . $e;} catch (DbException $e) {return '数据库连接异常' . $e;}// 处理一下数据返回给调用它的控制器return ($user->isEmpty()) ? false :['username' => $user['USERNAME'],'password' => $user['PASSWORD'],'identity' => $user['IDENTITY'],'power' => $user['JURISDICTION'],'latestLoginTime' => $user['LATEST_LOGIN_TIME'],'id' => $user['IDX']];}}
3. Login 方法
application 目录下创建user模块,user模块下创建Index类
<?phpnamespace app\user\controller;use app\common\TokenVerify;use think\Controller;class Index extends Controller{/*** 用户登录:用户名密码正确后签发 token 给客户端端* @return string|*/public function Login(){$username = input('username'); // 获取用户名$password = input('password'); // 获取密码// 获取用户信息$info = model('common/User')->findUserInfoByUserNameAndPwd($username, $password);// 未查到相关信息if (!$info) {return json(['status' => '102','msg' => '密码或用户名输入错误']);}// 签发token给客户端return json((new TokenVerify)->creatToken($info));}// 用户注册public function Register(){return '进行用户注册';// 用户名// 手机号// 密码}}
4. Token 验证类
application 目录下创建爱你 common 文件夹下 创建 TokenVerify类
<?phpnamespace app\common;use Exception;use think\Controller;use \Firebase\JWT\JWT;use think\facade\Cache;class TokenVerify extends Controller{// redis 键前缀private static string $redisPrefix = 'xs';// redis 过期时间private static int $redisExpire = 7200;// secretprivate static string $secret = '214';/*** 于生成一个 token* @param $info* 需要用户的部分信息* @return array* 返回一个数组,包含:token、uid、身份、权限*/public function creatToken($info){$secret = self::$secret; // secret$time = time(); // 当前时间$uid = md5($info['id'] . $info['username'] . $time); // 生成uid$payload = [ // payload'iss' => 'http://rangeloney.com', // 签发者 可选'aud' => [ // 接收该JWT的一方,可选'identity' => $info['identity'], // 用户身份'power' => $info['power'] // 权限数字],'iat' => $time, // 签发时间'nbf' => $time, // 某个时间点后才能访问,比如设置time+30,表示当前时间30秒后才能使用'exp' => $time + 3600 * 2, // 过期时间,这里设置2个小时'data' => [ // 自定义信息'uid' => $uid,'username' => $info['username']]];// 将这个 token 存到 redis中,设置键为:前缀 + 用户名,设置值为 uid$key = self::$redisPrefix . '_' . $info['username'];Cache::store('redis')->set($key, $uid, self::$redisExpire);// 返回 token (将其他信息都存在了token中,我并不需要额外设置)$token = JWT::encode($payload, $secret, 'HS256');return ['token' => $token, // token];}/*** token验证* @param $token* 客户端传入的 token* @return bool* 返回 true 验证通过,返回 false 验证位通过*/public function checkToken($token){try {$clientToken = JWT::decode($token, self::$secret, ['HS256']); // 解码来自客户端的 token$tokenArr = self::object_array($clientToken); // stdClass 转 arraylist($username, $uid) = [$tokenArr['data']['username'], $tokenArr['data']['uid']];$redis = Cache::store('redis')->handler();$key = self::$redisPrefix . '_' . $username;// 如果这个键不存在则验证失败if (!$redis->exists($key)) {return false;}// 从新设置这个 token 的过期时间,达到延长的效果Cache::store('redis')->set($key, $uid, self::$redisExpire);} catch (Exception $e) {return false;}return true;}// PHP stdClass Object转 arrayprivate static function object_array($array){if (is_object($array)) {$array = (array)$array;}if (is_array($array)) {foreach ($array as $key => $value) {$array[$key] = self::object_array($value);}}return $array;}}
