总体的思路步骤大致如下:
代码片段基于CRM Chat项目。项目地址CRMChat: TP6+swoole4开源客服系统,支持微信网页、H5端、PC端客服接入,商家端有PC端管理、H5端、App端接待,支持用户添加标签、分组等功能,前后端所有代码全部开源
1、从后端获取一个唯一的key值,让前端带着这个key组成链接,生成二维码。
- /**
- * 获取登录唯一code
- * @return mixed
- */
- public function getLoginKey()
- {
- $key = md5(time() . uniqid());
- $time = time() + 600;
- CacheService::set($key, 1, 600);
- return $this->success(['key' => $key, 'time' => $time]);
- }
- // 获取客服扫码key
- getSanCodeKey() {
- getSanCodeKey().then(res => {
- this.codeKey = res.data.key
- this.creatQrCode()
- this.scanTime = setInterval(() => {
- this.timeNum++
- if(this.timeNum >= 60) {
- this.timeNum = 0
- window.clearInterval(this.scanTime)
- this.rxpired = true
- } else {
- this.getScanStatus()
- }
-
- }, 1000)
- }).catch(error => {
- this.timeNum = 0
- window.clearInterval(this.scanTime)
- this.rxpired = true
- this.$Message.error(error.msg)
- })
- },

2、前端轮询接口二维码扫码结果接口
- // 扫码登录情况
- getScanStatus() {
- scanStatus(this.codeKey).then(async res => {
- // 0 = 二维码过期需要重新获取授权凭证
- if(res.data.status == 0) {
- this.timeNum = 0
- window.clearInterval(this.scanTime)
- this.rxpired = true
- }
- // 1=正在扫描
- if(res.data.status == 1) {
-
- }
- // 3 扫描成功正在登录
- if(res.data.status == 3) {
- window.clearInterval(this.scanTime)
- let expires = this.getExpiresTime(res.data.exp_time);
- // 记录用户登陆信息
- setCookies('kefu_uuid', res.data.kefuInfo.uid, expires);
- setCookies('kefu_token', res.data.token, expires);
- setCookies('kefu_expires_time', res.data.exp_time, expires);
- setCookies('kefuInfo', res.data.kefuInfo, expires);
- // 记录用户信息
- this.$store.commit('kefu/setInfo', res.data.kefuInfo)
- if(this.$store.state.media.isMobile) {
- //手机页面
- return this.$router.replace({ path: this.$route.query.redirect || '/kefu/mobile_list' });
- } else {
- // pc页面
- return this.$router.replace({ path: this.$route.query.redirect || '/kefu/pc_list' });
- }
- }
- }).catch(error => {
- this.$Modal.error({
- title: '提示',
- content: error.msg
- });
- this.timeNum = 0
- window.clearInterval(this.scanTime)
- this.rxpired = true
- })
- },
3、app端扫描二维码,扫码后跳转到确定授权页面
- // 扫描二维码
- handleScanCode() {
- // let str = "http://192.168.31.192:8081/pages/users/scan_login/index?key=463aca66113f65396dc28e3f0041a2f2";
- uni.scanCode({
- onlyFromCamera: true, // 是否只允许相机扫码,不允许相机选择图片
- success(res) {
- console.log(res);
- navigateTo(1, '/pages/view/authorizedLogin/index', {
- key: res.result.split('=')[1]
- });
- },
- fail: () => {}
- });
- },

4、取消则返回上一页,确定就带着code值请求后端,首先进行一系列的判断code,然后在标记该用户的uuid值。kefuId是在中间件传过来的(该项目使用的jwt 认证)。最后改变二维码状态是扫码中。
- /**
- * 确认登录
- * @param Request $request
- * @param string $code
- * @return mixed
- */
- public function setLoginCode(Request $request)
- {
- $code = $request->post('code');
- if (!$code) {
- return app('json')->fail('登录CODE不存在');
- }
- $cacheCode = CacheService::get($code);
- if ($cacheCode === false || $cacheCode === null) {
- return app('json')->fail('二维码已过期请重新扫描');
- }
- $userInfo = $this->services->get(['id' => $request->kefuId()]);
- if (!$userInfo) {
- return app('json')->fail('您不是客服无法登录');
- }
- $userInfo->uniqid = $code;
- $userInfo->save();
- CacheService::set($code, '0', 600);
- return app('json')->success('登录成功');
- }
中间件:
- use app\Request;
- use app\services\kefu\LoginServices;
- use crmeb\interfaces\MiddlewareInterface;
- use think\facade\Config;
-
- /**
- * Class KefuAuthTokenMiddleware
- * @package app\kefu\middleware
- */
- class KefuAuthTokenMiddleware implements MiddlewareInterface
- {
-
- /**
- * @param Request $request
- * @param \Closure $next
- * @throws \Psr\SimpleCache\InvalidArgumentException
- * @throws \think\db\exception\DataNotFoundException
- * @throws \think\db\exception\DbException
- * @throws \think\db\exception\ModelNotFoundException
- */
- public function handle(Request $request, \Closure $next)
- {
- $authInfo = null;
- $token = trim(ltrim($request->header(Config::get('cookie.token_name', 'Authori-zation')), 'Bearer'));
- /** @var LoginServices $services */
- $services = app()->make(LoginServices::class);
- $kefuInfo = $services->parseToken($token);
-
- Request::macro('kefuId', function () use (&$kefuInfo) {
- return (int)$kefuInfo['id'];
- });
-
- Request::macro('kefuInfo', function () use (&$kefuInfo) {
- return $kefuInfo;
- });
-
- return $next($request);
- }
- }
5、后端返回二维码扫码结果
- /**
- * 检测有没有人扫描登录
- * @param string $key
- * @return array|int[]
- * @throws InvalidArgumentException
- * @throws DataNotFoundException
- * @throws DbException
- * @throws ModelNotFoundException
- */
- public function scanLogin(string $key)
- {
- $hasKey = CacheService::has($key);
- if ($hasKey === false) {
- $status = 0;//不存在需要刷新二维码
- } else {
- $keyValue = CacheService::get($key);
- if ($keyValue === '0') {
- $status = 1;//正在扫描中
- $kefuInfo = $this->dao->get(['uniqid' => $key], ['account', 'uniqid']);
- if ($kefuInfo) {
- $tokenInfo = $this->authLogin($kefuInfo->account);
- $tokenInfo['status'] = 3;
- $kefuInfo->uniqid = '';
- $kefuInfo->save();
- CacheService::delete($key);
- return $tokenInfo;
- }
- } else {
- $status = 2;//没有扫描
- }
- }
- return ['status' => $status];
- }
6、扫描确认成功,保存用户信息,跳转页面