公众号扫码登录

article/2025/9/18 8:43:17

1.流程概述

1.1 申请公众号

1.2 创建带参数的公众号二维码,参数值为scen_id的值

1.3 微信基础配置接口编写,get方式的接口为微信测试接口,必须能正常访问,post方式的接口为扫码回调接口,从请求中获取微信返回的xml包数据,其中eventKey为二维码的scen_id参数值,event为事件类型,subscribe为关注事件,scan为扫码事件,unsubscribe为取消关注事件,根据自己的具体业务需求来处理即可,两个接口得请求路径必须相同

1.4 公众号接口基础配置

2.申请测试公众号

地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

访问该地址,直接微信扫码登录即可,扫码成功后进入如下界面:
在这里插入图片描述

3.获取带参数的公众号二维码

3.1 通过appID和appsecret获取accessToken,测试公众号每天只能获取2000次

以get方式向微信发送请求即可(https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET)

public static String getAccessToken(String appSecret,String appId) {String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type+ "&appid=" + appId + "&secret=" + appSecret;// 获取accessTokenString accessTokenBody = cn.hutool.http.HttpUtil.get(accessTokenUrl);log.info("获取accessToken{}",accessTokenBody);com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(accessTokenBody);String accessToken = null;try {accessToken = (String) jsonObject.get("access_token");log.info("accessToken:{}",accessToken);} catch (Exception e) {log.error("获取accessToken异常");}return accessToken;}

3.2 通过上一步获取的accessToken创建二维码的ticket

二维码有临时二维码和永久二维码两种,创建ticket的路径都是相同的,只是参数不同而已。

临时二维码请求说明

http请求方式: POST URL:
https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
POST数据格式:json POST数据例子:{“expire_seconds”: 604800, “action_name”:
“QR_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}} 或者也可以使用以下
POST 数据创建字符串形式的二维码参数:{“expire_seconds”: 604800, “action_name”:
“QR_STR_SCENE”, “action_info”: {“scene”: {“scene_str”: “test”}}

}

永久二维码请求说明

http请求方式: POST URL:
https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
POST数据格式:json POST数据例子:{“action_name”: “QR_LIMIT_SCENE”,
“action_info”: {“scene”: {“scene_id”: 123}}} 或者也可以使用以下 POST
数据创建字符串形式的二维码参数: {“action_name”: “QR_LIMIT_STR_SCENE”, “action_info”:
{“scene”: {“scene_str”: “test”}}}

这里要注意的是:场景scen里的属性scen_id的值,如果是以0开头,会将0忽略,文档里也没有具体说明,测试的结果是这样的,而且值的长度不能过长,否则扫码只会获取到的eventKey会获取不到,或则只有一部分。

		String ticketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;JSONObject ticketParam = JSONUtil.createObj();JSONObject scene = JSONUtil.createObj();String key = "1" + GenerationSequenceUtil.getRandNum(8);scene.put("scene_id", key);log.info("scene:{}",scene);JSONObject actionInfo = JSONUtil.createObj();actionInfo.put("scene",scene);ticketParam.put("action_info", actionInfo);ticketParam.put("action_name", "QR_SCENE");ticketParam.put("expire_seconds", expireTime);log.info("ticketParam:{}",ticketParam);// ticketParam转json字符串String ticketParamToString = com.alibaba.fastjson.JSONObject.toJSONString(ticketParam);// 获取ticketString ticketBody = cn.hutool.http.HttpUtil.post(ticketUrl,ticketParamToString);log.info("获取ticketBody{}",ticketBody);com.alibaba.fastjson.JSONObject ticketContent = com.alibaba.fastjson.JSONObject.parseObject(ticketBody);String ticket = (String) ticketContent.get("ticket");

3.3 拼接url,返回url到前端获取二维码

上一步拿到的ticket直接拼接为该url参数即可。https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKE

3.4获取二维码的完整代码

工具类:

package com.school.util;import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.school.base.Constant;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;@Slf4j
@Configuration
public class QRCodeUtil {private static final String grant_type = "client_credential";private static final Integer expireTime = 24*60*60*1000;public static JSONObject createQRCode(String userId,String appId,String appSecret) {String accessToken = getAccessToken(appSecret,appId);String ticketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;log.info("ticketUrl:{}",ticketUrl);JSONObject ticketParam = JSONUtil.createObj();JSONObject scene = JSONUtil.createObj();// 生成一个随机值String key = "1" + GenerationSequenceUtil.getRandNum(8);scene.put("scene_id", key);log.info("scene:{}",scene);JSONObject actionInfo = JSONUtil.createObj();actionInfo.put("scene",scene);ticketParam.put("action_info", actionInfo);ticketParam.put("action_name", "QR_SCENE");ticketParam.put("expire_seconds", expireTime);log.info("ticketParam:{}",ticketParam);// ticketParam转json字符串String ticketParamToString = com.alibaba.fastjson.JSONObject.toJSONString(ticketParam);// 获取ticketString ticketBody = cn.hutool.http.HttpUtil.post(ticketUrl,ticketParamToString);log.info("获取ticketBody{}",ticketBody);com.alibaba.fastjson.JSONObject ticketContent = com.alibaba.fastjson.JSONObject.parseObject(ticketBody);String ticket = (String) ticketContent.get("ticket");String QRCodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;JSONObject returnJson = JSONUtil.createObj();returnJson.put("url",QRCodeUrl);returnJson.put("key",key);return returnJson;}public static String getAccessToken(String appSecret,String appId) {String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type+ "&appid=" + appId + "&secret=" + appSecret;// 获取accessTokenString accessTokenBody = cn.hutool.http.HttpUtil.get(accessTokenUrl);log.info("获取accessToken{}",accessTokenBody);com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(accessTokenBody);String accessToken = null;try {accessToken = (String) jsonObject.get("access_token");log.info("accessToken:{}",accessToken);} catch (Exception e) {log.error("获取accessToken异常");}return accessToken;}}

接口:

/*** 获取公众号二维码*/@ApiOperation(value = "获取公众号二维码")@GetMapping("/createQRCode")@Passpublic ResponseModel<cn.hutool.json.JSONObject> createQRCode(@RequestParam(name = "userId",required = false) String userId) {cn.hutool.json.JSONObject qrCode = QRCodeUtil.createQRCode(userId,appId,appSecret);String key = (String) qrCode.get("key");if (userId != null) {redisTemplate.opsForValue().set(key + ":userId",userId);} else {redisTemplate.opsForValue().set(key, 0);}return ResponseHelper.succeed(qrCode);}

解释:这里返回的是二维码和一个随机值,然后用redis以该随机值为key,value初始值为0存储到redis中,到这里二维码的创建就完成了,存储随机值的目的是,用户扫码之后,我们在微信调用回调接口时,处理业务逻辑之后,是将结果返回给微信,无法返回到前端,这里就需要前端根据刚才的随机值来获取扫码结果,我们将扫码后的登录结果存储到redis中即可

4.编写微信基础配置接口

前面已经将带参数的二维码生成了,这里需要两个接口,而且路径必须相同,一个是get方式的,这个接口是用于微信调用,测试接口是否畅通,如果该接口未通,测试公众号这边的基础配置就会配置失败,另一个为post请求,微信会给改接口发送xml包,接口接收到后根据xml包数据进行业务处理

/**** 微信服务器触发get请求用于检测签名* @return*/@GetMapping("/receiveWx")@ResponseBody@Passpublic void handleWxCheckSignature(HttpServletRequest request, HttpServletResponse response) throws IOException {response.getWriter().println(request.getParameter("echostr"));}
/*** 微信公众号回调事件** @param request* @return*/@Pass@PostMapping(value = "/receiveWx")public void checkWxToken(HttpServletRequest request,HttpServletResponse response) throws IOException, DocumentException {Map<String, String> xmlToMap = xmlToMap(request);log.info("map:{}",xmlToMap);String eventKey = xmlToMap.get("EventKey");String event = xmlToMap.get("Event");String openId = xmlToMap.get("FromUserName");String toUserName = xmlToMap.get("ToUserName");String accessToken = QRCodeUtil.getAccessToken(appSecret,appId);TextMessage text = new TextMessage();text.setFromUserName(toUserName);text.setToUserName(openId);text.setCreateTime(System.currentTimeMillis() + "");text.setMsgType("text");//        try {if (ComUtil.isNotEmpty(event)) {if (event.equals("subscribe")) {eventKey = eventKey.replaceAll("qrscene_","");log.info("eventKey:{}",eventKey);Object value = redisTemplate.opsForValue().get(eventKey + ":userId");String userId = null;if (value != null) {userId = (String) value;}User user = userService.getById(userId);if (user != null) {userService.bindWx(userId, openId);// 用户绑定微信成功 赋值redisTemplate.opsForValue().set(userId + ":isBindWx",openId,60,TimeUnit.SECONDS);text.setContent("绑定微信成功");} else {String userName = "书院学员" + GenerationSequenceUtil.unRepeatSixCode();Long id = IdWorker.getId();user = User.builder().id(id).userName(userName).avatar("(NULL)").code(GenerationSequenceUtil.unRepeatSixCode()).openId(openId).gender(Constant.GenericNumber.NUMBER_ZERO).status(Constant.GenericNumber.NUMBER_ONE).createTime(LocalDateTime.now()).build();userService.save(user);// 添加用户学员关系userToRoleService.saveLocal(id, Constant.RolesRelation.Student.getId());text.setContent("注册成功,请再次扫描二维码登录");}} else if (event.equals("SCAN")) {eventKey = eventKey.replaceAll("qrscene_","");log.info("eventKey:{}",eventKey);Object value = redisTemplate.opsForValue().get(eventKey + ":userId");String userId = null;if (value != null) {userId = (String) value;}User user = userService.getById(userId);if (user != null) {userService.bindWx(userId, openId);// 用户绑定微信成功 赋值redisTemplate.opsForValue().set(userId + ":isBindWx",openId,60,TimeUnit.SECONDS);text.setContent("绑定微信成功");} else {log.info("accessToken:{}", accessToken);String userUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";String userJson = HttpUtil.get(userUrl);log.info("获取的userJson{}", userJson);final WxMpController.UserInfo userInfo = JSON.parseObject(userJson, WxMpController.UserInfo.class);log.info("获取的userInfo{}", userInfo);WxUserVo wxUserVo = WxUserVo.builder().userName("书院学员").avatar("(NULL)").wxId(openId).build();LoginVO loginVO = userService.loginByWxId(wxUserVo, Constant.LoginApp.tenant);log.info("loginVO:{}", loginVO);redisTemplate.opsForValue().set(eventKey, loginVO, 60 * 60, TimeUnit.SECONDS);text.setContent("已登录" + loginVO.getUser().getUserName());}} else if (event.equals("unsubscribe")) {User userByOpenId = userService.getUserByOpenId(openId);if (userByOpenId != null) {try {userService.deleteByUserNo(String.valueOf(userByOpenId.getId()));} catch (Exception e) {log.info("删除用户失败");}}}}String message = textMessageToxml(text);response.setCharacterEncoding("UTF-8");response.getWriter().println(message);}public Map<String ,String > xmlToMap(HttpServletRequest request) throws IOException, DocumentException {HashMap<String, String> map = new HashMap<>();SAXReader saxReader = new SAXReader();ServletInputStream inputStream = request.getInputStream();Document read = saxReader.read(inputStream);Element rootElement = read.getRootElement();List<Element> elements = rootElement.elements();for (Element e:elements) {map.put(e.getName(),e.getText());}inputStream.close();return map;}public String textMessageToxml(TextMessage textMessage) {XStream xStream = new XStream();xStream.alias("xml", textMessage.getClass());return xStream.toXML(textMessage);}

5.公众号接口基础配置

URL的域名,如果是http,端口就必须是80,如果是https,端口就必须是443,而且get请求的端口必须是可以正常访问的,否则配置失败;我这里本地调试用的natapp内网穿透,网站内有教程 https://natapp.cn/
在这里插入图片描述

6.前端调用获取登录结果接口

前端生成二维码之后,轮询该接口即可;或则前后端使用websoket进行通信也行

 /*** 获取登录返回对象*/@ApiOperation(value = "获取登录返回对象")@GetMapping("/getLoginVO")@Passpublic ResponseModel<Object> getLoginVO(@RequestParam(name = "key") String key) {Object result = redisTemplate.opsForValue().get(key);if (result != null) {if (result instanceof String || result instanceof Integer) {return ResponseHelper.succeed(Constant.GenericNumber.NUMBER_ZERO);} else {LoginVO loginVO = (LoginVO) result;return ResponseHelper.succeed(loginVO);}} else {return ResponseHelper.succeed(Constant.GenericNumber.NUMBER_ZERO);}}

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

相关文章

二维码扫描登录,你必须知道的 3 件事!

作者 | 互联网平头哥 本文经授权转载自互联网平头哥&#xff08;ID&#xff1a;it_pingtouge&#xff09; 扫二维码登录现在比较常见&#xff0c;比如微信、支付宝等 PC 端登录&#xff0c;并且好像每款 APP 都支持扫码登录&#xff0c;不搞个扫码登录都不好意思。作为技术人员…

微信扫码登录实现

需求 使用微信扫码登录的授权方式登录系统 实现 此扫码登陆过程中使用了&#xff0c;微信开放平台&#xff08;需支付300开通开发者认证&#xff09;的网站应用实现的。 官方文档&#xff1a;https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wec…

web扫码登录

文章目录 需求流程交互流程服务交互流程 关键思路代码生成二维码&#xff0c;返回给PC展示轮询查询二维码状态APP扫码请求登录 总结 需求 pc端实现app扫码登录 流程 交互流程 服务交互流程 关键思路 主要问题在于如何识别APP端用户&#xff0c;然后传递给PC端已经登录成功 …

开放平台–扫描微信二维码登录

准备 如不了解第三方登录流程&#xff0c;建议先大概了解一下&#xff0c;在来看看代码。 说明&#xff1a; 由于开放平台无测试号测试&#xff0c;所以只能上开放平台进行配置信息。公众平台的测试号并不能给开放平台使用。 微信开放平台地址&#xff1a;https://open.weixi…

二维码登陆

上一段时间研究微信公共账号,发现微信提供了一个扫码登陆验证的功能。近期头痛于经常忘记用户名密码,因此考虑是否可以结合这个功能,完成免密码登陆。百度后发现&#xff0c;有很多仁兄已经做过类似的功能了。 如这篇文章: 实现网站二维码扫描登录 仔细研究后&#xff0c;发现很…

二维码登录(三)扫码登录

承接上篇博客&#xff0c;在进行二维码生成之后&#xff0c;app进行扫码&#xff0c;扫码成功之后&#xff0c;手机点击登录&#xff0c;进行绑定登录关系&#xff0c;后台做自动关联与自动登录。 本文git地址&#xff1a;https://github.com/xvshu/qrlogin 1&#xff0c;扫码…

微信扫码登陆(1)---扫码登录流程讲解、获取授权登陆二维码

扫码登录流程讲解、获取授权登陆二维码 具体流程可以看微信官网的扫码登录文档 地址&#xff1a;准备工作 | 微信开放文档 其实官方文档已经讲的非常清楚而且讲的也很明白。 一、扫码登录流程讲解 1、首先准备工作 网站应用微信登录是基于OAuth2.0协议标准构建的微信OAut…

二维码扫码登录

项目结构 模块介绍 流程1 pc端&#xff1a;1:打开二维码登录网页index.html2:index.html调用GetQrCodeServlet3:GetQrCodeServlet干2件事 a:生成随机的uuid,是一个唯一标识&#xff0c;该标识贯穿整个流程 b:生成二维码图片&#xff0c;二维码信息&#xff1a;http://60.2…

扫码登录详解

目录 扫码流程分析 流程详解 步骤一&#xff1a;PC端准备二维码 步骤三&#xff1a;状态确认 token认证机制 扫码流程分析 1.扫码前&#xff0c;手机端已经是登陆状态&#xff0c;PC端显示一个二维码&#xff0c;等待扫描。 2.手机端打开应用&#xff0c;扫描PC端的二维码…

新增商品

1、在http代理设置&#xff0c;添加过滤参数&#xff1a; .*pusher\.*\/websocket.* .*/venilog/log/one .*\.(bmp|css|js|gif|ico|jpe?g|png|swf|woff) .*pusher\/info.* 2、启动http代理设置&#xff0c;录制脚本&#xff0c;将登陆和发布商品的脚本修改成’登陆‘&#x…

自定义商品分类,选择分类之后,添加商品附属性;仿淘宝后台添加商品附属性的价格和数量

效果图&#xff08;图片较大&#xff0c;加载较慢&#xff09; 页面部分&#xff1a; <div class"layui-form-item" id"add_attribute"><label class"layui-form-label">商品分类</label><div class"layui-input-i…

javaScript原生版购物车:全选、单选、全删、商品数量增减、计算总价、添加商品(代码)

题目&#xff1a; CSS代码如下&#xff1a; <style> *{ margin: 0px; padding: 0px; } .header,.content,.floot{ width: 800px; margin:0px auto; } .header ul li,.content ul li{ float: left; list-style: none; width: 100px; line-height: 100px; text-align: cen…

商品管理的新增

实现的功能如下&#xff1a; 点击新增弹出新增模态框通过下拉框绑定商品类别和商品单位保存新增 主要代码&#xff1a; 以上就是我的分享&#xff0c;如果有更好的方法或不懂得地方欢迎在评论区教导和提问喔&#xff01;

案例:实现在购物车中添加商品和删除购物车中指定商品的功能

一、向购物车中添加商品 1.1.创建AddCartServlet public class AddCartServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doPost(request, response);}SuppressWarnings(&q…

14.商品添加功能

商品用例的bean层 添加商品的用例实现首先要有封装商品的bean&#xff0c;还要有封装商品种类的bean。 封装商品种类的bean package cn.bingou.domain;/*** 商品种类的bean* author 吹静静**/ public class ProdCategory {private int id;private String cname;public ProdCate…

二十三、商城 - 商品录入-新增商品(11)

目录 &#x1f33b;&#x1f33b; 一、商品录入【选择商品分类】1.1 需求分析1.2 准备工作1.3 代码实现1.3.1 一级分类下拉选择框1.3.2 二级分类下拉选择框1.3.3 三级分类下拉选择框1.3.4 读取模板ID 二、商品录入【品牌选择】2.1 需求分析2.2 代码实现 三、商品录入【扩展属性…

java添加功能_商品添加功能的实现

返回的结果&#xff1a; package com.taotao.common.utils; import java.util.List; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; /** * 淘淘商城自定义响应结构 */ public class TaotaoResult { // 定义jackson对象…

把商品添加到购物车的方法

因为抢华为荣耀的缘故&#xff0c;研究了下京东添加商品到购物车的方法&#xff0c;具体步骤如下 一&#xff1a;打开商品页面&#xff0c;找到商品的id 例如这个商品的id就是1056970 二&#xff1a;把链接里面的http://gate.jd.com/InitCart.aspx?pid###&pcount1&pt…

php 商品模块添加商品属性,添加新商品

进入添加商品页后&#xff0c;如图1&#xff0c;2 添加新商品 图1 图2 促销价格前台表现 图3 图4 图5 附加价格前台表现 图6 添加新商品 图7 配件和关联商品前台表现形式 图8 图9 通用信息商品分类&#xff1a;商品分类是必填项&#xff0c;用于帮助客户找到需要的商品。如果您…

添加商品

添加商品 步骤分析: 1.在index.jsp添加一个超链接 跳转到add.jsp 2.add.jsp放入一个表单 3.表单提交到 AddProductServlet 封装数据 调用service完成保存操作 跳转到FindAllServlet (请求转发和重定向) 有表单使用的时候若使用请求转发会出现重复提交 方案1:重定向 …