既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
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请求, 并进行必要的业务处理
- 前置中间件: 请求时不会先去执行到控制器的某个方法, 而是先经过中间件, 然后再到某个控制器的某个方法去执行代码,如果中间件有拦截, 流程就会在中间件截断, 不会再往下执行方法, 而且获取不到控制器和方法, 因为会先执行中间件, 再执行控制器的方法
- 后置中间件: 访问某个控制器的方法, 会先去执行方法里面的代码内容(不返回输出), 然后执行中间件的代码, 最后才是方法的输出返回, 如果中间件有拦截, 流程就会在中间件内截断, 但是方法内的代码也在前面执行了, 可以获取控制器和方法, 因为会先去执行这个控制器的方法, 再执行中间件
在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 错误码
错误码是用来描述当前接口处理的结果, 是前后端共同的约束
格式:
- code 错误码
- 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;
![img](https://img-blog.csdnimg.cn/img_convert/64ec846b6128f31ba0a41098d1490b8f.png)
![img](https://img-blog.csdnimg.cn/img_convert/2375582c78e7fcc695f048a7a18eae23.png)
![img](https://i-blog.csdnimg.cn/blog_migrate/c2b75ec3538a28a6ddae07f4789cfb8c.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**
自定义抛出异常的类
<?php
/\*\*
\* 老王
\*
\*\*/
namespace app\api\exception;
use think\Exception;
[外链图片转存中...(img-dzbmlvwO-1715640208201)]
[外链图片转存中...(img-li6HpIFo-1715640208202)]
[外链图片转存中...(img-rUuESoAK-1715640208202)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618658159)**