假设两个APP分别是用户端和内部端
结构如下
API (用户端接口)
/api/ ├── auth/ # 认证相关 ├── users/ # 用户相关 ├── products/ # 商品相关 └── orders/ # 订单相关
Internal (内部端接口)
/internal/ ├── auth/ # 认证相关 ├── users/ # 用户相关 ├── products/ # 商品相关 └── orders/ # 订单相关
找到文件 application/common/library/Auth.php
增加一个类型
// 新增类型标识属性,api客户端,internal内部端protected $type = 'api';
在instance方法中增加处理参数的代码
// 处理类型参数if (is_array($options) && isset($options['type'])) {self::$instance->setType($options['type']);}
public function setType($type){$this->type = $type;// 加载对应类型的配置$config = Config::get("auth_groups.{$type}", []);// 合并配置到options$this->options = array_merge($this->options, $config);return $this;}
Token初始化中处理前缀
/*** 根据Token初始化** @param string $token Token* @return boolean*/public function init($token){if ($this->_logined) {return true;}if ($this->_error) {return false;}// 验证Token前缀if (!preg_match("/^{$this->type}_/", $token)) {$this->setError('Invalid token type');return false;}$data = Token::get($token);if (!$data) {return false;}$user_id = intval($data['user_id']);$modelClass = $this->options['model'] ?? User::class; // 动态模型if ($user_id > 0) {// $user = User::get($user_id);$user = $modelClass::get($user_id);if (!$user) {$this->setError('Account not exist');return false;}
修改register方法中的设置token
//设置Token // $this->_token = Random::uuid(); $rawToken = Random::uuid(); $this->_token = "{$this->type}_{$rawToken}";
login方法中动态获取登录字段
public function login($account, $password){// $field = Validate::is($account, 'email') ? 'email' : (Validate::regex($account, '/^1\d{10}$/') ? 'mobile' : 'username');// $user = User::get([$field => $account]);// 动态获取登录字段 -------------------------$field = $this->detectLoginField($account);$modelClass = $this->options['model'] ?? User::class;$user = $modelClass::get([$field => $account]);// -----------------------------------------if (!$user) {$this->setError('Account is incorrect');return false;}
// 新增登录字段检测方法 ---------------------------protected function detectLoginField($account){// 从配置获取字段检测规则$loginField = $this->options['login_field'] ?? ['username', 'email', 'mobile'];if (is_string($loginField)) {return $loginField; // 直接指定字段 }// 默认检测逻辑foreach ($loginField as $field) {if ($field === 'email' && Validate::is($account, 'email')) {return 'email';}if ($field === 'mobile' && Validate::regex($account, '/^1\d{10}$/')) {return 'mobile';}}return 'username';}
直接登录账号direct方法
public function direct($user_id){// $user = User::get($user_id);$modelClass = $this->options['model'] ?? User::class;$user = $modelClass::get($user_id);
// .....原代码
// $this->_token = Random::uuid();
$rawToken = Random::uuid();
$this->_token = "{$this->type}_{$rawToken}";
Token::set($this->_token, $user->id, $this->keeptime);
获取会员基本信息中增加账号类型
/*** 获取会员基本信息*/public function getUserinfo(){$data = $this->_user->toArray();$allowFields = $this->getAllowFields();$userinfo = array_intersect_key($data, array_flip($allowFields));$userinfo = array_merge($userinfo, Token::get($this->_token));$userinfo['avatar'] = url($userinfo['avatar'], '', false, true);// 账号类型$userinfo['account_type'] = $this->type;return $userinfo;}
给内部端设置用户表
CREATE TABLE `fa_internal_user` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',`group_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '组别ID',`username` varchar(32) DEFAULT '' COMMENT '用户名',`nickname` varchar(50) DEFAULT '' COMMENT '昵称',`password` varchar(32) DEFAULT '' COMMENT '密码',`salt` varchar(30) DEFAULT '' COMMENT '密码盐',`email` varchar(100) DEFAULT '' COMMENT '电子邮箱',`mobile` varchar(11) DEFAULT '' COMMENT '手机号',`avatar` varchar(255) DEFAULT '' COMMENT '头像',`level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '等级',`gender` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '性别',`birthday` date DEFAULT NULL COMMENT '生日',`bio` varchar(100) DEFAULT '' COMMENT '格言',`money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '余额',`score` int(10) NOT NULL DEFAULT '0' COMMENT '积分',`successions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '连续登录天数',`maxsuccessions` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '最大连续登录天数',`prevtime` bigint(16) DEFAULT NULL COMMENT '上次登录时间',`logintime` bigint(16) DEFAULT NULL COMMENT '登录时间',`loginip` varchar(50) DEFAULT '' COMMENT '登录IP',`loginfailure` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '失败次数',`loginfailuretime` bigint(16) DEFAULT NULL COMMENT '最后登录失败时间',`joinip` varchar(50) DEFAULT '' COMMENT '加入IP',`jointime` bigint(16) DEFAULT NULL COMMENT '加入时间',`createtime` bigint(16) DEFAULT NULL COMMENT '创建时间',`updatetime` bigint(16) DEFAULT NULL COMMENT '更新时间',`token` varchar(50) DEFAULT '' COMMENT 'Token',`status` varchar(30) DEFAULT '' COMMENT '状态',`verification` varchar(255) DEFAULT '' COMMENT '验证',PRIMARY KEY (`id`),KEY `username` (`username`),KEY `email` (`email`),KEY `mobile` (`mobile`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='内部端会员表';
增加Model 文件 application/common/model/InternalUser.php
增加内部端API基类 application/common/controller/InternalApi.php ,在internal模块下面controller都继承这个基类
<?phpnamespace app\common\controller;use app\common\library\Auth; use think\Config; use think\Hook; use think\Loader; use think\Validate;/*** 内部端API基类*/ class InternalApi extends Api {/*** 初始化操作 (重写父类方法)*/protected function _initialize(){// 跨域请求检测 check_cors_request();// 检测IP是否允许 check_ip_allowed();// 移除HTML标签$this->request->filter('trim,strip_tags,htmlspecialchars');// 初始化批发商端权限实例$this->auth = Auth::instance(['type' => 'internal']);$modulename = $this->request->module();$controllername = Loader::parseName($this->request->controller());$actionname = strtolower($this->request->action());// 获取Token$token = $this->request->server('HTTP_INTERNALTOKEN', $this->request->request('internaltoken', \think\Cookie::get('internaltoken')));$path = str_replace('.', '/', $controllername) . '/' . $actionname;// 设置当前请求的URI$this->auth->setRequestUri($path);// 检测是否需要验证登录if (!$this->auth->match($this->noNeedLogin)) {// 初始化内部端Token$this->auth->init($token);// 检测是否登录if (!$this->auth->isLogin()) {$this->error("请先登录", null, 401);}// 判断是否需要验证权限if (!$this->auth->match($this->noNeedRight)) {// 使用内部端权限规则校验// if (!$this->auth->check($path)) {// $this->error(__('You have no permission'), null, 403);// } }} else {// 如果有传递token才验证是否登录状态if ($token) {$this->auth->init($token);}}// 公共上传配置(保持与用户端一致)$upload = \app\common\model\Config::upload();Hook::listen("upload_config_init", $upload);Config::set('upload', array_merge(Config::get('upload'), $upload));// 加载当前控制器语言包$this->loadlang($controllername);}/*** 刷新Token(重写父类方法)*/protected function token(){$token = $this->request->param('__token__');// 使用内部端专用Token验证规则if (!Validate::make()->check(['__token__' => $token], ['__token__' => 'require|token:store'])) {$this->error(__('Token verification error'), ['__token__' => $this->request->token('internal')]);}// 刷新内部端专用Token$this->request->token('internal', 'md5');} }
最后在application/config.php 里面增加一个配置
'auth_groups' => ['api' => ['model' => \app\common\model\User::class,'login_field' => 'mobile',//['username', 'email', 'mobile'],'keeptime' => 2592000,],'internal' => ['model' => \app\common\model\InternalUser::class,'login_field' => 'username','keeptime' => 2592000,]],
这样,在请求api和internal的接口时,就可以验证各自的token