【尚医通】微信扫码登录和手机号登录冲突问题解决思路

news2024/9/23 15:21:18

【尚医通】微信扫码登录和手机号登录冲突问题解决思路

问题描述

最近做尚医通遇到一个问题,微信扫码登录手机号登录特殊情况 下会发生冲突,导致无法登录的问题。下面就描述一下几种情况。

正常情况:用户第一次一上来就使用微信扫码登录,后端在调用回调函数的时候就完成了用户注册,由于是第一次登录,需要绑定手机号,于是又调用了一次手机号登录的接口,手机号登录成功之后就完成了绑定。之后就可以随便使用扫码登录或手机号登录了。

特殊情况:用户第一次一上来就使用手机号登录,后端会根据手机号去数据库搜索“唯一”的一条数据,如果结果为空则说明是第一次登录,直接注册;如果不为空,说明已经注册过,就直接登录。用户第二次登录时使用的是微信扫码登录,后端在调用回调函数的时候再次完成用户注册,然后又绑定手机号。那么现在数据库就有两条手机号相同的数据了。第三次登陆时若使用手机号登录,那么“唯一”查询就不满足了,就会报错,最后登录不了。


回顾流程

首先回顾一下扫码登录的流程。

在这里插入图片描述

更多详情请见:资源中心 - 微信开放平台 (qq.com)

解决思路

上回调函数代码!

//微信扫描后回调的方法
    @GetMapping("/callback")
    public String callback(@RequestParam("code") String code, @RequestParam("state") String state) {
        //1.获取临时票据 code
        log.info("code:{}", code);
        //2.拿着code和微信id和密钥,请求微信固定地址,得到两个值
        //2.1使用code和appid以及appscrect换取access_token
        // %s表示一个占位符
        StringBuffer baseAccessTokenUrl = new StringBuffer()
                .append("https://api.weixin.qq.com/sns/oauth2/access_token")
                .append("?appid=%s")
                .append("&secret=%s")
                .append("&code=%s")
                .append("&grant_type=authorization_code");
        String accessTokenUrl = String.format(baseAccessTokenUrl.toString(),
                ConstantWxPropertiesUtil.WX_OPEN_APP_ID,
                ConstantWxPropertiesUtil.WX_OPEN_APP_SECRET,
                code);
        //3.使用httpclient请求这个地址
        try {
            String accesstokenInfo = HttpClientUtils.get(accessTokenUrl);
            log.info("accesstokenInfo:{}", accesstokenInfo);
            //4.从返回字符串中获取两个值 openid 和access_token
            JSONObject jsonObject = JSON.parseObject(accesstokenInfo);
            String access_token = jsonObject.getString("access_token");
            //openid为每个微信用户唯一
            String openid = jsonObject.getString("openid");
            log.info("access_token:{}", access_token);
            log.info("openid:{}", openid);
            //5.根据access_token获取微信用户的基本信息
            String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                    "?access_token=%s" +
                    "&openid=%s";
            String userInfoUrl = String.format(baseUserInfoUrl, access_token, openid);
            String resultInfo = HttpClientUtils.get(userInfoUrl);
            log.info("resultInfo:{}", resultInfo);
            JSONObject resultUserInfoJson = JSON.parseObject(resultInfo);
            //6.解析用户信息
            //6.1用户名称
            String nickname = resultUserInfoJson.getString("nickname");
            //6.2用户头像
            String headimgurl = resultUserInfoJson.getString("headimgurl");

            //7.如果第一次微信登录添加到数据库
            UserInfo userInfo = userInfoService.saveUser(nickname, openid);

            //8.返回name和token字符串
            String name = userInfo.getName();
            if (StringUtils.isEmpty(name)) {
                name = userInfo.getNickName();
            }
            if (StringUtils.isEmpty(name)) {
                name = userInfo.getPhone();
            }
            //判断userInfo是否有手机号,如果手机号为空,返回openid
            //如果手机号不为空,返回openid值是空字符串
            //前端根据“openid”判断:如果openid为空,已绑定手机号(不需要再绑定手机号),否则需要绑定手机号
            if (StringUtils.isEmpty(userInfo.getPhone())) {
                openid = userInfo.getOpenid();
            } else {
                openid = "";
            }
            //使用jwt生成token字符串
            String token = JwtHelper.createToken(userInfo.getId(), name);
            //跳转到前端页面
            return "redirect:" + ConstantWxPropertiesUtil.YYGH_BASE_URL
                    + "/weixin/callback?token=" + token + "&openid="
                    + openid + "&name=" + URLEncoder.encode(name, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

问题主要出现在第七步。

解决思路如下:

  1. 如果数据库中已经存在了手机号登录时的数据,那么微信登录时就不应该再向数据库中添加数据。
  2. 但是在回调函数中我们无法通过手机号查询数据库。所以我们无法在回调函数中实现是否注册的判断,那不如索性把第七步给删了,改成只搜索用户信息,不保存用户信息

回调函数中无法一口气解决所有的问题,所以我们只得把重心调转到登录方法上。

上登录方法代码!!!:

//手机号登录接口
    @Override
    public Map<String, Object> loginUser(LoginVo loginVo) {
        //1.从loginVo获取输入的手机号和验证码
        String phone = loginVo.getPhone();
        String code = loginVo.getCode();
        //2.判断手机号和验证码是否为空
        if (StringUtils.isBlank(phone) || StringUtils.isBlank(code)) {
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }
        //3.判断手机验证码和输入的验证码是否一致
        String redisCode = redisTemplate.opsForValue().get(phone);
        if (!code.equals(redisCode)) {
            throw new YyghException(ResultCodeEnum.CODE_ERROR);
        }

        //4.绑定手机号
        UserInfo userInfo = null;
        if (!StringUtils.isBlank(loginVo.getOpenid())) {
            userInfo = this.selectWxInfoByOpenId(loginVo.getOpenid());
            if (null != userInfo) {
                userInfo.setPhone(loginVo.getPhone());
                updateById(userInfo);
            } else {
                throw new YyghException(ResultCodeEnum.DATA_ERROR);
            }
        }
        //5.如果userinfo为空,进行正常的手机登录
        if (userInfo == null) {
            //判断是否第一次登录:根据手机号查询数据库,如果不存在相同手机号就是第一次登录
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getPhone, phone);
            userInfo = baseMapper.selectOne(queryWrapper);
            if (userInfo == null) {
                //第一次登录,添加信息到数据库
                userInfo = new UserInfo();
                userInfo.setName("");
                userInfo.setPhone(phone);
                userInfo.setStatus(1);
                baseMapper.insert(userInfo);
            }
        }


        //6.校验是否禁用
        if (userInfo.getStatus() == 0) {
            throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
        }
        //7.不是第一次,直接登录
        //返回登录信息
        //返回登录用户名
        //返回token信息
        Map<String, Object> map = new HashMap<>();
        String name = userInfo.getName();
        if (StringUtils.isBlank(name)) {
            name = userInfo.getNickName();
        }
        if (StringUtils.isBlank(name)) {
            name = userInfo.getPhone();
        }
        map.put("name", name);
        // token生成
        String token = JwtHelper.createToken(userInfo.getId(), name);
        map.put("token", token);
        return map;
    }

我们重写第4步代码。

解决思路如下:

在第4步绑定手机号的时候,我们先根据手机号把用户给查询出来。

  1. 如果用户存在且还没设置openid,那么我们就将前端传过来的openid设置给该用户,完成绑定;
  2. 如果用户存在且也已经设置了openid,那么我们直接抛一个异常,提示前端该手机已经绑定了微信号。
  3. 如果用户不存在,那么我们利用openid,phone直接注册一个新用户并登录,实现注册,绑定,登录一条龙。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137656.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一碗云南米线,加剧速食食品赛道“内卷”?

说起云南&#xff0c;人们的印象往往是藏在苍山洱海、玉龙雪山里的风花雪月。然而&#xff0c;生活中最常见的“滇味”&#xff0c;却是一碗鲜香美味、软中带劲的米线。近年来&#xff0c;从广西的螺蛳粉到河南的酸辣粉&#xff0c;越来越多带着地域特色的主食被装进小小纸桶&a…

[ Azure - Subscriptions ] 解决办法:此订阅未在 Microsoft.Insights 资源提供程序中注册

问题描述 在使用新的 Azure 订阅中某个服务的时候出现错误&#xff0c;错误信息为&#xff1a;“此订阅未在 Microsoft.Insights 资源提供程序中注册。” 本文发生的示例是在使用 Azure Monitor 时&#xff0c;出现了该错误。 英文的错误提示&#xff1a; To run this query…

日本知名汽车零部件公司巡礼系列之株式会社141

株式会社141 业务内容&#xff1a; &#xff08;发动机系、燃料系、排气管系&#xff09;・A机器零件&#xff08;打印机用零件&#xff09; 搬运设备部件&#xff08;导轨部件&#xff09;・小型马达用部件&#xff08;轴类、壳体类、辅助部件&#xff09; 公司简介&#x…

如何外网访问登录员工管理系统平台

员工管理系统平台网站是企业常用办公工具之一&#xff0c;为了安全性和稳定性&#xff0c;一般都部署在公司内部内网web服务器上&#xff0c;在办公室内通过内网IP端口进行登录访问。那么&#xff0c;如何实现在外网或者在家也能访问公司内网的管理网站呢&#xff1f; 今天给大…

Spring Data JPA @DomainEvents 发布领域事件以及遇到的坑。

文章目录发布领域事件监听领域事件遇到的问题发布领域事件 通过repositories管理的实体是聚合根。在领域驱动设计(DDD)的应用中&#xff0c;这些聚合根通常会发布领域事件。Spring Data提供了一个名为DomainEvents的注解&#xff0c;可以在聚合根的一个方法上使用该注解&#…

JD青龙面板任务代理池部署与使用教程

JD脚本中一些任务会黑ip 导致没法跑或者跑不出东西 比如联盟跑出来全是劵 这时候就需要代理池 本文为3步 1.购买设置代理 2.部署代理池 3.脚本配置代理池 使用效果展示 1.设置代理 代理推荐购买星空 点我查看星空代理 按下图购买及生成API API填入部署的配置中(下文有说)…

ThinkPHP 之 SQLI审计分析(三)

说明 该文章来源于徒弟lu2ker转载至此处&#xff0c;更多文章可参考&#xff1a;https://github.com/lu2ker/ 文章目录说明0x00 测试代码做了什么0x01 分析调用链0x03 总结Time&#xff1a;9-23影响版本&#xff1a;ThinkPHP5.1.22 Payload&#xff1a; /public/index.php/i…

万物皆可集成系列:低代码对接微信小程序

近年来&#xff0c;微信小程序的开发如火如荼&#xff0c;很多移动端应用为了更方便被大家所使用的&#xff0c;都步入了小程序的行列 那么对于低代码平台开发的移动端应用是否可以和小程序集成呢&#xff1f;这里我以微信小程序为例。为大家介绍如何在 首先&#xff0c;众所…

ELF格式学习

ELF格式学习 一、简介 ELF的英文全称是Executable and Linking Format&#xff0c;最初是由UNIX系统实验室开发、发布的ABI(Application Binary Interface)接口的一部分&#xff0c;也是Linux的主要可执行文件格式。 ELF文件种类&#xff1a; 可执行文件&#xff08;.out&a…

内核解读之内存管理(1)CPU体系架构UMA和NUMA

文章目录1. SMP&#xff08;UMA&#xff09; 体系架构2. NUMA 体系架构3. NUMA 结构基本概念内存和cpu有着密不可分的联系&#xff0c;学习内存管理&#xff0c;先了解下cpu的架构。1. SMP&#xff08;UMA&#xff09; 体系架构 CPU 计算平台体系架构分为 SMP 体系架构和 NUMA…

【Kotlin】函数 ② ( Unit 函数 | TODO 函数抛出异常返回 Nothing 类型 | 反引号函数名 )

文章目录一、Unit 函数二、TODO 函数抛出异常返回 Nothing 类型三、反引号函数名一、Unit 函数 Java 语言 中 没有返回值的函数 其 返回类型 是 void ; Kotlin 语言 中 没有返回值的函数 其返回类型是 Unit , 该函数又称为 Unit 函数 ; Kotlin 语言中 推出 Unit 类型概念 , 是…

电子产品拆解分析-暖手宝

①电子产品拆解分析-暖手宝一、功能介绍二、拆解电路分析以及器件作用1、暖手宝整体电路2、锂电池充电升压电路根据数据手册以及电路实物绘画原理图3、控制PTC加热垫换挡电路根据实物绘画原理图4、主控MUC电路三、各模块对应的原理图一、功能介绍 ①按键三档调温暖手&#xff1…

如何站在开发者的角度理解框架的设计思想?

感谢支持 我已经在CSDN发布了100多篇原创文章&#xff0c;如果你读过我的文章&#xff0c;欢迎点击下方的链接&#xff0c;帮我五星评价一下&#xff0c;谢谢。 很简单&#xff0c;点击下图&#xff0c;像截图一样五星评价一下就可以了&#xff1a; 有问必答 最近有好多读者…

Vue(六)

1. 列表渲染&#xff08;接五&#xff09; 1.1 Vue监测数据的原理—数组 修改数组&#xff1a;最后追加元素&#xff1a;push&#xff1b;删除最后一个元素&#xff1a;pop&#xff1b;删除第一个元素&#xff1a;shift&#xff1b;在最前面加一个&#xff1a;unshift;在指定位…

shell之函数和数组练习案例

目录函数和数组练习案例1、编写函数&#xff0c;实现打印绿色OK和红色FAILED判断是否有参数&#xff0c;存在为Ok&#xff0c;不存在为FAILED2、编写函数&#xff0c;实现判断是否无位置参数&#xff0c;如无参数&#xff0c;提示错误3、编写函数实现两个数字做为参数&#xff…

ArcGIS基础实验操作100例--实验46按要素融合多边形

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验46 按要素融合多边形 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

ctf中linux内核态的漏洞挖掘与利用系列1

说明 该系列文章主要是从ctf比赛入手&#xff0c;针对linux内核上的漏洞分析、挖掘与利用做讲解&#xff0c;本篇文章主要介绍内核漏洞利用所需的前置知识以及准备工作。 linux内核态与用户态的区别 以 Intel CPU 为例&#xff0c;按照权限级别划分&#xff0c;Intel把 CPU指…

8.JS笔记-函数

1.函数 函数是封装了一段可以被重复执行调用的代码块。目的是让大量代码重复使用 封装&#xff1a;将一个或多个功能通过函数的方式进行封装&#xff0c;对外只提供一个简单的函数接口&#xff08;将主板、CPU等封到主机里&#xff09; 2.函数使用 2.1 声明函数 function 函…

DHCP自动分配IP命令配置

由路由器R3充当服务器给PC3和PC4分配IP地址&#xff1a; [r3] dhcp enable-----开启DHCP服务器 [r3] ip pool ? STRING<1-64> Pool name [r3] ip pool aaa-----创建地址池&#xff08;aaa是地址池的名字&#xff09; [r3-ip-pool-aaa]network 192.168.4.0 mask 24-----…

【C进阶】C语言终话,了解计算机的程序环境和预处理过程

目录 &#x1f3c2;前言&#x1f3c2;&#xff1a; &#x1f3c7;一、详解编译与链接&#x1f3c7;&#xff1a; 1.程序的翻译环境与执行环境&#xff1a; 2.翻译环境&#xff1a; 3.翻译阶段&#xff1a; ①.编译&#xff1a; ②.链接&#xff1a; 4.运行环境&#xff1a; …