thinkphp6验证码+登陆状态+redis+中间件
tp6笔记
json() 提供 方法
修改 开启报错查看
env
中开启 de_bug
database配置数据库 会先使用 .env 中的配置
tp6 中使用
Db:: 需要 use 门面模式 user think\facade/\Db
app.php
config/app.php 错误信息
多应用模式
composer require topthink/think-multi-app
在当前控制器下创建 route 路由文件夹
配置域名访问
http://www.tp6.com/demo.php/index/test?a=1
在public 下复制index.php 改名为 demo.php
$response = $http->name(‘demo’)->run(); 若是文件名不同 可用 name指向
config 可以在每个 目录下 +
admin / config 当前目录有用
api/config …
路由
多应用 需要 加 文件名 admin/text index/…
config 下 业务状态码
创建 status.php`
return [
‘success’ => 1,
‘error’ => 0,
];
未开启强制路由
都可以
http://www.tp6.com/index.php/demo/index/t demo 应用 inde想控制器 t方法
http://www.tp6.com/demo.php/index/t
has 判断某个值是否设置
容器和依赖注入
全局中间件
可以通过命令行指令快速生成中间件
php think make:middleware Check
<?php
namespace app\middleware;
class Check
{
public function handle($request, \Closure $next)
{
// 添加中间件执行代码
...
return $next($request);
}
}
在 app/middleware.php
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
// \think\middleware\SessionInit::class
app\middleware\Check::class,
];
应用中间件
模板引擎
composer安装
composer require topthink/think-view
简单的密码+盐处理
密码 123
md5(‘123_md5’) 处理字符串
验证码
composer require topthink/think-captcha
验证码使用 验证是时 徐快需要在中间件中开启session
模型分层架构
思维
验证器
$data = [
'username' => $username,
'password' => $password,
'captcha' => $captcha,
];
$validate = new \app\admin\validate\AdminUser();
if(!$validate->check($data)) {
return show(config("status.error"), $validate->getError());
}
<?php
/**
* Created by singwa
* User: singwa
* motto: 现在的努力是为了小时候吹过的牛逼!
* Time: 07:11
*/
namespace app\admin\validate;
use think\Validate;
class AdminUser extends Validate {
protected $rule = [
'username' => 'require',
'password' => 'require',
'captcha' => 'require|checkCapcha',
];
protected $message = [
'username' => '用户名必须,请重新输入',
'password' => '密码必须',
'captcha' => '验证码必须',
];
protected function checkCapcha($value, $rule, $data = []) {
if(!captcha_check($value)) {
return "您输入的验证码不正确!";
}
return true;
}
}
Url 注意
如何正确分层之操作datebase
注意使用 init初始化
如何处理在init里无法重定向
注意 如何 /admin/index/index 写的时候需要+ /’
前置中间件
preg_match()
1 正则
2 str 匹配2中有无 1
return 1 0
后置中间件
session
session('adminUser', $res); session('adminUser', null);
- 1
business 业务逻辑层
在business 处理业务逻辑 -> 调用 model 的sql执行 在business进行 异常抛出 在c层try catch 补获
model 放在 common 公共文件里
redis 实现验证码 存活时间60s
安装 https://blog.csdn.net/kxukai/article/details/106692983
日志
开启日志 runtime 可以查看执行的原生sql语句 进行分析
工厂模式
短信可以使用redis 进行限制 存手机号 + 发信息间隔时间~
如何流控 20%阿里云短信 80%百度云短信
案例:2018年年底 央视春晚 百度手机号登录发送验证码分发不同运营商流量, 就是用这个来做的, 并发50万 发送短信验证码。
简单粗暴 实用 最简单的方法往往最有效
$a = rand(0,99);
if($a < 80) {
// 阿里云逻辑
} else {
// 百度云逻辑
}
- 1
- 2
- 3
- 4
- 5
- 6
前后端分离 redis+ token 不使用session+cookie
config(‘statis.success’)
异常处理 try catch
捕获异常时 注意记录日志 以供分析
tp6 helper 全局搜索自带抛出异常 throw_if 同laravel
手动抛出异常
throw new \think\Exception("不存在该验证码", config('status.code.not_code'));
- 1
捕获异常
try {
$result = (new \app\common\business\User())->login($data);
} catch (\Exception $e) {
return show($e->getCode(), $e->getMessage());
}
// 阻止数据库抛出异常 被 用户看到 手动抛出~
try {
$this->userObj->save($userData);
$userId = $this->userObj->id;
}catch (\Exception $e) {
throw new \think\Exception("数据库内部异常");
}
redis 删除 等于null 即可
redis 写登录 逻辑
前后端分离 所以是 api 接口
- 用户点击发送验证码 把 sms . 手机号 存入 redis 内容有 验证码 存活60s //这里sms为了再redis中进行区分别的缓存
- 接收数据 转换格式 验证 isajax ispost
if (!$this->request->isPost() || !$this->request->isAjax() )
return show(config("status.error"), "非法请求");
$phoneNumber = input("phone_number",'','trim');
$code = input("code", 0, "intval");
- validate 验证器 验证参数
- 验证 用户输入的 code !== redis中的该手机号对应的 code
// 用户输入的 code !== redis中的该手机号对应的 code
$redisCode = cache(config("redis.code_pre") . $data['phone_number']);
if (empty($redisCode) || $redisCode != $data['code']) {
throw new \think\Exception("不存在该验证码", config('status.code.not_code'));
}
从第五层开始要在 business 逻辑层进行处理 判断
例子 抛出异常最好用 try catch 进行补获 避免数据库抛出暴露信息被用户查看
5. 还是在common/ business 操作
根据用户手机号 查找有该用户登录记录
// 需要去判断表 是否有 用户记录 phone_number
// 生成token
$user = $this->userObj->getUserByPhoneNumber($data['phone_number']);
这里model 层在 common/model datebase 操作公共的 可以共用
然后进行判断 操作用户记录
6. 然后把生成的token作为k 用户的id 和用户名作为 v 存入redis 存活时间 ~
$redisData = [
"id" => $userId,
"username" => $username,
];
$res = cache(config("redis.token_pre") . $token, $redisData, Time::userLoginExpiresTime($data['type']));
7.最后把前端需要的数据返回
中间件 进行 前端 检测是否登录 2种方法
第一种方法 需要验证login的controller extends 该控制器 进行检测
class AuthBase extends ApiBase
{
public $userId = 0;
public $username = "";
public $accessToken = "";
public $isLogin = 1;
public function initialize()
{
parent::initialize(); // TODO: Change the autogenerated stub
// if ($this->isLogin == 1) {
// $this->userId = 6; // 测试场景
// return true;
// }
$this->accessToken = $this->request->header("access-token");
if (!$this->accessToken || !$this->isLogin()) {
return $this->show(config("status.not_login"), "没有登录");
}
}
/**
* 判断用户是否登录
* @return bool
*/
public function isLogin()
{
$userInfo = cache(config("redis.token_pre") . $this->accessToken);
if (!$userInfo) {
return false;
}
if (!empty($userInfo['id']) && !empty($userInfo['username'])) {
$this->username = $userInfo['username'];
$this->userId = $userInfo['id'];
return true;
}
return false;
}
}
第二种方法使用中间件 进行检测
在api 文件下 创建一个
修改 middlevare.php 中 对应信息
<?php
declare (strict_types=1);
namespace app\api\middleware;
class Auth
{
/**
* 处理请求
*
* @param \think\Request $request
* @param \Closure $next
* @return Response
*/
public function handle($request, \Closure $next)
{
//前置中间件
// if (empty(session('adminUser')) && !preg_match("/login/", $request->pathinfo())) {
// return redirect((string)url('login/index'));
// }
//ajax 返回api格式 例如处理rbac
//这样会处理所有api下的控制器 所以需要
// return $this->show(config("status.not_login"), "没有登录");
if (in_array($request->pathinfo(), config('login.need_verify_login')))
if (!$this->isLogin()) return ajaxReturn(0, '', '未登录,请先登录');
return $next($request);
}
public function end(\think\Response $response)
{
}
/**
* 判断用户是否登录
* @return bool
*/
public function isLogin()
{
$token = request()->header("access-token");
$userInfo = cache(config("redis.token_pre") . $token);
if (!$userInfo) {
return false;
}
if (!empty($userInfo['id']) && !empty($userInfo['username'])) {
return true;
}
return false;
}
}
免登录
打开登录界面 前 执行控制器 的 中间件 进行 验证 获得返回数据 进行 重定向至跳转首页 or 等待用户登录
##退出登录
清空当前redis中 k 拼接 对应的token v 为 null
model 层代码优化
business 逻辑层
model 层
这里可以父类继承 调用 减少代码量
面向过程 => 面向对象 统一返回给前端
模型 使用
with 会 两次
执行sql语句 进行查询 数据量大 大公司常用
withjoin 一次join 查询
sku 建议用2 方便 直接对应sku表的id
统一规格也使用 sku
系统浏览量 pv
每个商品的浏览量
商品展示流程
展示商品有自己默认skuid
用户点击根据skuid 到sku查询到 该商品的goodsid
然后根据goodsid 到goods 获取该商品信息 和 该商品对应的 所有 sku信息
这里gids
1,11 :1 : 1 //skuid
1, 11 规格id // 颜色, 尺码
sku
购物车
浏览器缓存
mysql redis
存redis 高性能
hash
表如何设计的
流程
1.加入购物车
用户登录状态 点击商品加入购物车
即加入redis 中
(1) 到mysql根据skuid查找该商品的sku数据
2.购物车展示页面
3.购物车删除 修改数量
4.购物车排序
封装在基础类库
hgetall
删除 hdel test 1 2
获取redis 购物车数据 进行排序
hLen 获取数据
返回值
integer-reply: 哈希集中字段的数量,当 key 指定的哈希集不存在时返回 0
例子
redis> HSET myhash field1 “Hello”
(integer) 1
redis> HSET myhash field2 “World”
(integer) 1
redis> HLEN myhash
(integer) 2
redis>
库存 严谨判断 购物车+ 订单 未支付 …2
hMget 返回 key 指定的哈希集中指定字段的值。 对于哈希集中不存在的每个字段,返回 nil 值。
hGetAll 返回 key 指定的哈希集中所有的字段和值。
订单
主表副表
订单流程
进入 加入 购物车页面 订单确认页面 和 提交订单页面 都需要判断 库存
提交订单流程
- 删除购物车 的该商品
- 减库存
- insert order 主表 副表
redis 延迟队列处理无效订单
用户点击 确定订单时 未支付
存 redis 20分钟有效期 然后脚本每秒 和当前时间比较 小于当前时间的则 把该订单状态改为 已取消 恢复该订单对应sku的库存
存redis 订单id time()+20min
定时任务
暂时有bug 生成 所以要手动挡
最后在当前tp6 目录的 命令行 运行 php think order 即可开启进程
如果定时任务 进程挂了这么解决? supervise维持进程任务自动重启
支付抽离 单独服务 子服务 微服务
写出用户所有流程
支付流程
从购物车点击进入结算 下单页 先减库存
点击立即付款 跳转支付界面 然后看支付回调
成功 失败 进行数据库处理 库存等等…
Start
前置中间件 检测 是否登录 || 创建一个base来继承 在其中判断 pathinfo 对比
判断前端传来的token 是否存在redis
登录 注册 用redis存储60s 验证码 进行判断 sms . 133333333 验证码 | sms 是标识 拼接手机号 作 k v是验证码 参3存活时间
登录成功 redis 存储 k => user_token . 后台生成唯一token v => 登录的用户信息 然后把token 给header头
进首页 验证
注册登录
- 登录流程
验证登录
购物车
订单
支付