TP6+JWT开发APP接口

article/2025/11/9 5:49:25

1. 开发环境

  1. PHP框架: ThinkPHP6
  2. 操作系统: MacOs
  3. Web服务器: Nginx

2. APP接口

2.1. 简介

客户端与服务端进行数据交互的通道, 大部分APP接口都是通过HTTP协议通信的

2.2. HTTP通信的三要素
  1. URL 通信地址
  2. Method 通信方式(GET|POST|PUT|DELETE)
  3. Params 通信的内容
2.3 APP接口的输入

客户端传递参数

  1. URL参数传递, 将参数拼接在URL后边, GET方式
  2. Request Body, 将参数放在Body体内, POST方式, 常见的传输格式Content-Type: multipart/form-data , application/x-www-form-urlencoded, json, 其中multipart/form-data , application/x-www-form-urlencoded会将变量以关联数组传入

服务端获取参数

  1. 获取GET方式的参数, 可以通过$_GET获取,
  2. 获取POST方式的参数, form-data与x-www-form-urlencoded格式通过$_POST获取, json格式通过file_get_contents(“php://input”)获取
  3. 对于body中提交的json对象, 你无需使用php://input去获取, 可以直接当做表单提交的数据使用,因为框架已经自动处理过了已经封装好
2.4 APP接口的输出

JSON是一种轻量级的数据交换格式
接口输出三要素

  1. code 错误码
  2. msg 错误码对应的描述
  3. data 接口返回的数据

封装接口JSON格式输出
在app下新建公共common文件夹, 在common新建lib文件夹用来存放一些公共类库文件

  1. 定义一个Class类, 在控制器引用后静态化调用
<?php
/*** 老王***/
namespace app\common\lib;
use app\common\lib\error\ApiErrDesc;Class ResponseJson{/*** @param null $data* @param int $httpCode* @return \think\response\Json*/public static function success($data = null, $httpCode = 200){$status = ApiErrDesc::SUCCESS[0];$message = ApiErrDesc::SUCCESS[1];return $this->jsonResponse($status, $message, $data, $httpCode);}/*** @param $status* @param $message* @param null $data* @param int $httpCode* @return \think\response\Json*/public static function error($status, $message, $data = null, $httpCode = 500){return $this->jsonResponse($status, $message, $data, $httpCode);}/*** @param $status* @param $message* @param $data* @param int $httpCode* @return \think\response\Json*/private function jsonResponse($status, $message, $data, $httpCode = 200){$result = ['status' => $status, // 业务状态码'message' => $message,'result' => $data];return json($result, $httpCode);}
}

控制器调用

<?php
/*** 老王***/namespace app\api\controller;
use app\BaseController;
use app\common\lib\ResponseJson;class Test extends BaseController
{public function index(){ResponseJson::success(['test' => 'test']);ResponseJson::error(1, '未知错误');}
}
  1. 定义一个Trait, 用户代码复用, 在控制器引用后, 可以像调用该控制器类的方法一样
<?php
/*** 老王***/
namespace app\common\lib;
use app\common\lib\error\ApiErrDesc;Trait ResponseJson{/*** @param null $data* @param int $httpCode* @return \think\response\Json*/public function success($data = null, $httpCode = 200){$status = ApiErrDesc::SUCCESS[0];$message = ApiErrDesc::SUCCESS[1];return $this->jsonResponse($status, $message, $data, $httpCode);}/*** @param $status* @param $message* @param null $data* @param int $httpCode* @return \think\response\Json*/public function error($status, $message, $data = null, $httpCode = 500){return $this->jsonResponse($status, $message, $data, $httpCode);}/*** @param $status* @param $message* @param $data* @param int $httpCode* @return \think\response\Json*/private function jsonResponse($status, $message, $data, $httpCode = 200){$result = ['status' => $status, // 业务状态码'message' => $message,'result' => $data];return json($result, $httpCode);}
}

控制器调用

<?php
/*** 老王***/namespace app\api\controller;
use app\BaseController;
use app\common\lib\ResponseJson;class Test extends BaseController
{use ResponseJson;public function index(){$this->success(['test' => 'test']);$this->error(1, '未知错误');}
}

3. APP接口的鉴权

客户端要带着凭证来调用APP接口, 即有权限才可以调用
在这里插入图片描述

3.1传统web的cookie session

在这里插入图片描述
web是基于浏览器, 可以采用该方式, 但是APP不是基于浏览器cooike和session的机制, 需要采用JWT

3.2 JWT (Json Web Token)

JWT原理即服务端认证以后, 生成一个JSON对象, 返回给客户端, 后续客户端所有的请求都必须带上这个JSON对象, 而服务端依靠这个JSON来认定用户身份
完整的JWT格式输出是以 . 分割的三段Base64编码
在这里插入图片描述
组成

  1. header, 通常包含两部分: 类型和采用的加密算法, header需要经过Base64Url编码后作为JWT的第一部分
  2. payload, 载体, 包含了claim, iss: 签发者, exp: 过期时间戳, sub: 面向的用户, aud: 接收方, iat: 签发时间, 可以存放不敏感的信息, payload需要经过Base64Url编码后作为JWT的第二部分
  3. signature, 创建签名, 使用编码后的header和payload以及一个密钥, 使用header中指定的签名算法进行签名, 生成的签名作为JWT的第三部分, 该签名是在服务端完成的, 客户端不知道密钥, 更安全
3.3 代码封装JWT的使用

JWT的官网 JWT
找到PHP的库, 利用composer (PHP用来管理依赖关系的工具) 下载包文件lcobucci/jwt
切换到项目目录执行composer

composer require lcobucci/jwt

在app/common/auth文件下, 创建JWT封装类库文件

<?php
/*** 老王***/namespace app\common\lib\auth;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\ValidationData;/*** 单例模式* 一次请求中所有使用到jwt的地方都是一个用户* Class JwtAuth* @package app\common\lib\auth* 1. 接口鉴权* 2. 获取用户身份*/
class JwtAuth
{/*** @var*/private $token;/*** @var*/private $decodeToken;/*** claim iss* @var string*/private $iss = 'chao.com';/*** claim aud* @var string*/private $aud = 'tp6_server_app';/*** 身份 uid* @var string*/private $uid;/*** @var string*/private $secret = 'TP6&*chao1992#$LJL*&^&*9089';/*** @var null*/private static $instance = null;/** 私有化 构造函数*/private function __construct(){}/***/private function __clone(){// TODO: Implement __clone() method.}/*** 单例模式 获取jwtAuth句柄* @return JwtAuth|null*/public static function getInstance(){if (is_null(self::$instance)){self::$instance = new self();}return self::$instance;}/*** @param $uid* @return $this* 设置身份信息*/public function setUid($uid){$this->uid = $uid;return $this;}/*** @param $token* 设置token*/public function setToken($token){$this->token = $token;return $this;}/*** @param $token*/public function decode(){if (!$this->decodeToken){// 把字符串转成Token对象$this->decodeToken = (new Parser())->parse((string)$this->token);$this->uid = $this->decodeToken->getClaim('uid');}return $this->decodeToken;}/*** 校验signature, 判断token是否过期或者被篡改* @return bool*/public function verify(){$signer = new Sha256();$privateKey = new Key($this->secret);$result = $this->decode()->verify($signer, $privateKey);return $result;}/*** 校验参数* @return bool*/public function validate(){$data = new ValidationData();$data->setIssuer($this->iss);$data->setAudience($this->aud);return $this->decode()->validate($data);}/*** @return $this*/public function encode(){$signer = new Sha256();$privateKey = new Key($this->secret);$time = time(); // 颁发时间$this->token = (new Builder())->withHeader('alg', 'HS256')->issuedBy($this->iss)->permittedFor($this->aud)->issuedAt($time)->expiresAt($time + 3600)  // 过期时间->withClaim('uid', $this->uid)  // 自定义参数->getToken($signer, $privateKey);return $this;}/*** 获取token* @return string*/public function getToken(){return (string)$this->token;}/*** @return string*/public function getUid(){return $this->uid;}}

控制器调用

<?php
/*** 老王***/namespace app\api\controller;
use app\BaseController;
use app\common\lib\auth\JwtAuth;
use app\common\lib\ResponseJson;class Test extends BaseController
{use ResponseJson;public function index(){// 获取jwtAuth的句柄$jwtAuth = JwtAuth::getInstance();$token = $jwtAuth->setUid(1)->encode()->getToken();$this->success(['token' => $token]);}
}
3.4 中间件

中间件主要用于拦截或者过滤应用的HTTP请求, 并进行必要的业务处理

  1. 前置中间件: 请求时不会先去执行到控制器的某个方法, 而是先经过中间件, 然后再到某个控制器的某个方法去执行代码,如果中间件有拦截, 流程就会在中间件截断, 不会再往下执行方法, 而且获取不到控制器和方法, 因为会先执行中间件, 再执行控制器的方法
  2. 后置中间件: 访问某个控制器的方法, 会先去执行方法里面的代码内容(不返回输出), 然后执行中间件的代码, 最后才是方法的输出返回, 如果中间件有拦截, 流程就会在中间件内截断, 但是方法内的代码也在前面执行了, 可以获取控制器和方法, 因为会先去执行这个控制器的方法, 再执行中间件

在app/api模块下, 新建middleware文件夹, 然后在该文件夹下创建类

<?php
/*** 老王***/namespace app\api\middleware;use app\api\exception\ApiException;
use app\common\lib\auth\JwtAuth;
use app\common\lib\error\ApiErrDesc;
use app\common\lib\ResponseJson;/*** Class CheckAuth* @package app\api\middleware* 中间件*/
class CheckAuth
{/*** @param $request* @param \Closure $next*/public function handle($request, \Closure $next){$token = $request->header('token');if ($token){// 校验$jwtAuth = JwtAuth::getInstance();$jwtAuth->setToken($token);if ($jwtAuth->validate() && $jwtAuth->verify()){return $next($request);}else{throw new ApiException(ApiErrDesc::ERR_LOGIN);}}else{throw new ApiException(ApiErrDesc::ERR_PARAMS);}}
}

注册中间件
在app/api模块下, 新建middleware.php文件

<?php
// 该模块下的中间件定义文件, 对该模块下的所有控制器都有效, 在middleware下写好中间件后, 需要在该文件下配置绑定
// 如果使得该中间件只针对某一个控制器有效, 可以借助路由来设置, 不需要在该文件下配置绑定, 直接在middleware下写好中间件然后通过路由绑定->middleware()
return [app\api\middleware\CheckAuth::class
];

4. 业务逻辑异常处理

4.1 错误码

错误码是用来描述当前接口处理的结果, 是前后端共同的约束
格式:

  1. code 错误码
  2. msg 错误码对应的描述

一般以配置文件( config/status.php )或者类的常量来定义错误码, 统一管理便于维护
在app/common/lib文件夹下新建error, 并在该文件夹下创建类库

<?php
/*** 老王***/namespace app\common\lib\error;/*** Class ApiErrDesc* @package app\common\lib\error* APi返回码类库*/
class ApiErrDesc
{/*** Api通用错误码* error_code < 1000*/const SUCCESS = [1, 'Success'];const UNKNOWN_ERR = [0, '未知错误'];const ERR_URL = [2, '请求接口不存在'];const ERR_PARAMS = [100, '参数错误'];/*** 用户登录相关的错误码* error_code 1000-1100*/const UNKNOWN_USER = [1001, '用户不存在'];const ERR_PASSWORD = [1002, '密码错误'];const ERR_LOGIN = [1000, '登录过期'];}
4.2 业务异常

try…catch 可以捕获异常
set_exception_handler set_error_handler设置用户自定义的异常处理函数, 用于没有用try/catch块来捕获的异常
api模块是与客户端交互的接口类库, 出现异常需要按照接口的输出格式返回给客户端, 便于客户端进行操作, TP6中app/ExceptionHandler.php是系统自带的应用异常处理类, 通过页面形式输出错误信息, 但这种并不适用api接口
所以需要在app/api/exception文件夹下新建异常处理的类
自定义接收异常并格式化输出类

<?php
/*** 老王***/namespace app\api\exception;
use think\exception\Handle;
use think\Response;
use Throwable;
use app\common\lib\ResponseJson;
use app\common\lib\error\ApiErrDesc;
/*** Class ApiHandler* @package app\api\exception* api模块下的异常处理类*/
class ApiHandle extends Handle
{protected $httpCode = 500;/*** Render an exception into an HTTP response.** @access public* @param \think\Request   $request* @param Throwable $e* @return Response*/public function render($request, Throwable $e): Response{if ($e instanceof ApiException){$status = $e->getCode();$message = $e->getMessage();}else{$status = $e->getCode();if(!$status || $status < 0){$status = ApiErrDesc::UNKNOWN_ERR[0];}$message = $e->getMessage() ?: ApiErrDesc::UNKNOWN_ERR[1];}// 添加自定义异常处理机制if (method_exists($e, 'getStatusCode')){$this->httpCode = $e->getStatusCode();}return ResponseJson::error($status, $message, null, $this->httpCode);}}

自定义抛出异常的类

<?php
/*** 老王***/namespace app\api\exception;use think\Exception;
use Throwable;class ApiException extends Exception
{public function __construct(array $apiErrConst, Throwable $previous = null){$message = $apiErrConst[1];$code = $apiErrConst[0];parent::__construct($message, $code, $previous);}
}

后边在控制器中返回错误错误信息, 就可以直接调用ApiException抛出异常, 再由ApiHandle中的handle方法接管异常信息, 并格式化输出


http://chatgpt.dhexx.cn/article/kq6mSrc7.shtml

相关文章

快速开发一个混合APP(Hybrid APP)

混合开发APP&#xff08;Hybrid APP&#xff09;简单说就是将原生功能封装成JS接口&#xff0c;前端通过调用接口实现功能。前端使用html来开发APP&#xff0c;可以实现自动更新&#xff0c;一套代码同时生成Android和iOS两端安装包&#xff0c;开发速度快&#xff0c;用户体验…

Hybrid App开发 四大主流平台

Hybrid App在过去的两年中已经成为移动界的核心话题&#xff0c;但是作为一名Web开发者来说要如何站在移动互联网的浪潮之巅呢&#xff1f;是选择学习原生开发&#xff0c;研究Java、 Object-C、C#等语言&#xff0c;还是选择继续使用网页开发&#xff0c;容忍HTML5功能的局限性…

whatsapp协议api接口

测试demo&#xff1a;http://39.109.122.117:8081/login Whatsappapi 一、登录注册 登录 发送验证码 效验短信验证码 查询是否封号 获取版本号 查询是否封号&#xff08;商业版&#xff09; 发送验证码&#xff08;商业版&#xff09; 效验短信验证码&#xff08;商业…

第一次做APP接口开发过程总结

关于平湖人社APP后台开发总结APP后台接口开发我总结出以下几个部分: 需求分析与整理分析业务需求涉及的模块功能与数据库关联表接口开发文档大致编写并提交产品与APP开发人员 1.主要是webservise 地址:http://XXXX/services/SbcxWebservice?wsdl2.接口函数统一为 sbp_sbcx_XXX…

做APP接口开发过程总结

关于平湖人社APP后台开发总结 APP后台接口开发我总结出以下几个部分: 需求分析与整理 分析业务需求涉及的模块功能与数据库关联表 接口开发文档大致编写并提交产品与APP开发人员 1.主要是webservise 地址:http://XXXX/services/SbcxWebservice?wsdl 2.接口函数统一为 sbp_sbcx…

java性能优化案例——面试可能用得到

一、背景 1.1 业务背景 支付宝卡包存放着用户的会员卡和优惠券。无论是卡券cell&#xff0c;还是卡券详情&#xff0c;都是通过静态模板配置加上动态可变数据&#xff0c;最终呈现给终端用户的。 下面【图1】展现了卡券数据在C端用户的展现形式&#xff0c;【图2】表示了C端…

Java性能优化可算是讲明白了,优化权威指南就是强

在JAVA程序中&#xff0c;性能问题的大部分原因并不在于JAVA语言&#xff0c;而是程序本身。养成良好的编码习惯非常重要&#xff0c;能够显著地提升程序性能。 Java是目前软件开发领域中使用最广泛的编程语言之一。Java应用程序在许多垂直领域&#xff08;银行、电信、医疗保…

面试官:关于Java性能优化,你有什么技巧

通过使用一些辅助性工具来找到程序中的瓶颈&#xff0c;然后就可以对瓶颈部分的代码进行优化。 一般有两种方案&#xff1a;即优化代码或更改设计方法。我们一般会选择后者&#xff0c;因为不去调用以下代码要比调用一些优化的代码更能提高程序的性能。而一个设计良好的程序能…

JAVA性能优化思路探究,让程序超顺畅

1、背景介绍 一个系统的上线除了常规的功能性测试外&#xff0c;还需要经过严格的性能测试&#xff0c;满足预期的性能指标&#xff08;常见的有响应时间&#xff0c;tps等&#xff09;&#xff0c;才允许上生产环境。 广义的性能测试一般还包含负载测试&#xff08;用于测试…

Java接口性能优化实例

Java接口性能优化实例 文章目录 Java接口性能优化实例概述用到的工具和环境工具环境 找瓶颈启示和收获 概述 最近公司的下单接口有些慢&#xff0c;老板担心无法支撑双11&#xff0c;想让我优化一把&#xff0c;但是前提是不允许大改&#xff0c;因为下单接口太复杂了&#xf…

JAVA程序性能优化

制约程序性能的根源 常用的性能评估指标 并发&#xff1a;同一时间多少请求访问&#xff1a;如同一时间10个用户请求&#xff0c;并发就是10TPS&#xff1a;transaction per second&#xff1a;相当于写操作QPS&#xff1a;query per second&#xff1a;相当于读请求&#xf…

Java性能优化的35个细节(珍藏版)

代码优化&#xff0c;一个很重要的课题。可能有些人觉得没用&#xff0c;一些细小的地方有什么好修改的&#xff0c;改与不改对于代码的运行效率有什么影响呢&#xff1f;这个问题我是这么考虑的&#xff0c;就像大海里面的鲸鱼一样&#xff0c;它吃一条小虾米有用吗&#xff1…

Java 中 10 大简单的性能优化

Java 7 ForkJoinPool和 Java 8 的并行Stream有助于并行化东西&#xff0c;这在您将 Java 程序部署到多核处理器机器上时非常有用。与跨网络上的不同机器进行扩展相比&#xff0c;这种并行性的优势在于您几乎可以完全消除延迟效应&#xff0c;因为所有内核都可以访问相同的内存。…

Java性能优化的七个方向

博客主页&#xff1a;https://tomcat.blog.csdn.net 博主昵称&#xff1a;农民工老王 主要领域&#xff1a;Java、Linux、K8S 期待大家的关注&#x1f496;点赞&#x1f44d;收藏⭐留言&#x1f4ac; 目录 概述复用优化计算优化并行执行变同步为异步惰性加载 结果集优化资源冲…

JAVA性能优化实例

目录 概述 Sql性能优化 多线程 利用内存缓存 功能优化 参考博客 概述 性能优化的几个点&#xff0c;大致可以分为&#xff1a; sql优化使用多线程利用内存&#xff0c;缓存等&#xff0c;将固定不常更改的数据放入在&#xff0c;存取更快的内存当中功能实现逻辑优化 Sql性…

10种简单的Java性能优化学习

10种简单的Java性能优化学习 你是否正打算优化hashCode()方法&#xff1f;是否想要绕开正则表达式&#xff1f;Lukas Eder介绍了很多简单方便的性能优化小贴士以及扩展程序性能的技巧。 最近“全网域&#xff08;Web Scale&#xff09;”一词被炒得火热&#xff0c;人们也正在…

Java性能优化实践:分享Java性能优化的实践经验

Java性能优化是一个非常重要的话题&#xff0c;在现代软件开发中扮演着至关重要的角色。在本篇博客中&#xff0c;我将分享一些Java性能优化的实践经验&#xff0c;包括使用JavaProfiler分析程序性能、使用JMH进行基准测试、优化JVM参数等&#xff0c;帮助开发者更好地理解Java…

11 个简单的 Java 性能调优技巧

大多数开发人员理所当然地以为性能优化很复杂&#xff0c;需要大量的经验和知识。好吧&#xff0c;不能说这是完全错误的。优化应用程序以获得最佳性能不是一件容易的事情。但是&#xff0c;这并不意味着如果你不具备这些知识&#xff0c;就不能做任何事情。这里有11个易于遵循…

Java性能优化

文章目录 一、JVM内存模型1.1、程序计数器&#xff08;Program Counter Register&#xff09;1.2、JVM虚拟机栈&#xff08;JVM Stacks&#xff09;1.3、本地方法栈&#xff08;Native Method Stacks&#xff09;1.4、堆&#xff08;Heap&#xff09;1.5、方法区&#xff08;Me…

Java性能优化的50个细节(珍藏版)

在JAVA程序中,性能问题的大部分原因并不在于JAVA语言,而是程序本身。养成良好的编码习惯非常重要,能够显著地提升程序性能。 1. 尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要…