微信公众号扫码登录PC后台系统

微信公众号沙箱测试环境登录地址 

https://open.weixin.qq.com/connect/qrconnect?appid=wx39c379788eb1286a&scope=snsapi_login&redirect_uri=http%3A%2F%2Fmp.weixin.qq.com%2Fdebug%2Fcgi-bin%2Fsandbox%3Ft%3Dsandbox%2Flogin

账号和地址配置:

1. 获取appID 和 appsecret 

2. 配置接口信息:天界URL回调地址(需要后台先写好接口不然添加不成功),token(加解密需要)

验证url接口

 /** @param signature 微信加密签名,signature结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce参数。* @param timestamp 时间戳* @param nonce     这是个随机数* @param echostr   随机字符串,验证成功后原样返回*/@GetMapping("/wx/event")public void get(@RequestParam(required = false) String signature,@RequestParam(required = false) String timestamp,@RequestParam(required = false) String nonce,@RequestParam(required = false) String echostr,HttpServletResponse response) throws IOException {log.info("接受事件=====>{}", echostr);response.setCharacterEncoding("UTF-8");response.getWriter().write(echostr);response.getWriter().flush();response.getWriter().close();}

 处理微信推送事件接口

 //处理微信推送事件@PostMapping("/wx/event")public void post(final HttpServletRequest request, HttpServletResponse response) {// TODO 做验签操作log.info("接受事件公众号事件");try {// 微信加密签名final String signature = request.getParameter("signature");// 时间戳final String timestamp = request.getParameter("timestamp");// 随机数final String nonce = request.getParameter("nonce");// 随机字符串final String echostr = request.getParameter("echostr");//将xml文件转成易处理的mapfinal Map<String, String> map = ParseXmlUtils.parseXml(request);//开发者微信号final String toUserName = map.get("ToUserName");//OpenIdfinal String fromUserName = map.get("FromUserName");//消息创建时间 (整型)final String createTime = map.get("CreateTime");//消息类型,eventfinal String msgType = map.get("MsgType");//事件类型final String event = map.get("Event");String sceneStr = "";String msg = "";if ("event".equals(msgType)) {if (event.equals("subscribe")) {final String ticket = map.get("Ticket");if (ticket != null) {sceneStr = map.get("EventKey").replace("qrscene_", "");}msg = ReplyModel.responseReply(map, "欢迎您使用测试公众号");}//注:事件类型为SCAN即已关注else if (event.equals("SCAN")) {final String ticket = map.get("Ticket");if (ticket != null) {sceneStr = map.get("EventKey");}msg = ReplyModel.responseReply(map, "扫码登录");}} else {// 用户行为msg = ReplyModel.responseReply(map, "欢迎关注该公众号");}log.info("event:{}" + event);log.info("场景值:{}" + sceneStr);log.info("openId:{}" + fromUserName);log.info("ToUserName:{}" + toUserName);// 如果scene_str 不为空代表用户已经扫描并且关注了公众号if (StringUtils.isNotEmpty(sceneStr)) {QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();queryWrapper.eq("open_id", fromUserName);SysUser sysUser = sysUserMapper.selectOne(queryWrapper);if (sysUser == null) {// 保存用户微信的信息 ,为了测试我直接在数据库添加
//                    sysUser = new SysUser();
//                    sysUser.setOpenId(fromUserName);
//                    sysUser.setUsername(fromUserName);
//                    sysUser.setRealname(null);
//                    String salt = oConvertUtils.randomGen(8);
//                    String passwordEncode = PasswordUtil.encrypt(fromUserName, salt, salt);
//                    sysUser.setSalt(salt);
//                    sysUser.setMemberLevelId("1");
//                    sysUser.setPassword(passwordEncode);
//                    sysUser.setStatus(1);
//                    sysUser.setDelFlag(CommonConstant.DEL_FLAG_0);
//                    sysUserMapper.insertUser(sysUser);}// 将微信公众号用户ID缓存到redis中,标记用户已经扫码完成,执行登录逻辑。redisCache.setCacheObject(sceneStr, fromUserName, 60, TimeUnit.SECONDS);}log.info("打印消息体=========>{}", msg);response.setCharacterEncoding("UTF-8");PrintWriter out = response.getWriter();out.print(msg);out.flush();out.close();} catch (Exception e) {e.printStackTrace();}}
ParseXmlUtils # xml解析工具类
package com.spark.common.utils;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author Jerry* @date 2024-01-26 10:36* 解析微信公众号服务器返回的xml文件*/
public class ParseXmlUtils {public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {//存放所有的子节点Map<String, String> map = new HashMap<String, String>();//获取到文档对象InputStream inputStream = request.getInputStream();SAXReader reader = new SAXReader();//读取输入流,获取文档对象Document document = reader.read(inputStream);//根据文档对象获取根节点Element root = document.getRootElement();//获取根节点的所有子节点List<Element> elementList = root.elements();//遍历所有的子节点for (Element e : elementList) {//所有的子节点放入map中map.put(e.getName(), e.getText());}//关闭流inputStream.close();return map;}}
解析微信公众号服务器返回的xml文件相关类:
BaseMessage # 基础消息类
package com.spark.common.wx;import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;import java.util.Map;/*** @author Jerry* @date 2024-01-26 15:37*/
@Data
@XStreamAlias("xml")
public class BaseMessage {// 开发者微信号private String ToUserName;// 发送方帐号(一个OpenID)private String FromUserName;// 消息创建时间 (整型)private String CreateTime;// 消息类型(text/image/location/link)private String MsgType;public BaseMessage(Map<String, String> requestMap) {this.ToUserName = requestMap.get("FromUserName");this.FromUserName = requestMap.get("ToUserName");this.CreateTime = System.currentTimeMillis() / 1000 + "";}}
TextMessage # 文本消息类型
package com.spark.common.wx;import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;import java.util.Map;/*** @author Jerry* @date 2024-01-26 15:39*/
@Data
@XStreamAlias("xml")
public class TextMessage extends BaseMessage{// 消息内容private String Content;public TextMessage(Map<String, String> requestMap, String Content) {super(requestMap);this.setMsgType("text");this.Content = Content;}
}
ReplyModel #处理组装返回xml
package com.spark.common.wx;import com.thoughtworks.xstream.XStream;import java.util.Map;/*** @author Jerry* @date 2024-01-26 15:40* 解析微信公众号服务器返回的xml文件*/
public class ReplyModel {public static String responseReply(Map<String, String> parseXml, String content) {BaseMessage msg = null;//获取到消息的类型,如果还有其他类型的消息直接加case就可以String type = parseXml.get("MsgType");//调用处理文本消息的方法if (type.equals("text") || type.equals("event")) {msg = dealTextMessage(parseXml, content);}//如果该消息不为nullif (msg != null) {//调用把消息对象处理为xml数据包的方法return beanToxml(msg);}return null;}///把消息对象处理为xml数据包的方法private static String beanToxml(BaseMessage msg) {XStream xStream = new XStream();xStream.processAnnotations(TextMessage.class);String xml = xStream.toXML(msg);return xml;}//处理文本消息private static BaseMessage dealTextMessage(Map<String, String> parseXml, String content) {//将键值对的map直接转为对象,并为Content参数赋值return new TextMessage(parseXml, content);}}

获取登录二维码 

官方文档:微信开放文档

 /*** 获取二维码** @param sceneStr* @return*/@GetMapping("/getTempQrCode")public AjaxResult getTempQrCode(@RequestParam(name = "sceneStr") String sceneStr) {String result = createTempQrCode(getAccessToken(), sceneStr);return AjaxResult.success(result);}public static String createTempQrCode(String accessToken, String sceneStr) {String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;Map<String, Object> data = new HashMap<>();data.put("expire_seconds", 3600);data.put("action_name", "QR_STR_SCENE");Map<String, Object> actionInfo = new HashMap<>();Map<String, Object> scene = new HashMap<>();scene.put("scene_str", sceneStr);actionInfo.put("scene", scene);data.put("action_info", actionInfo);String json = HttpUtil.createPost(url).header("Content-Type", "application/json").body(JSONUtil.toJsonStr(data)).execute().body();System.out.println("json = " + json);String qrcode = (String) JSONUtil.getByPath(JSONUtil.parse(json), "ticket");return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + qrcode;}public String getAccessToken() {String accessToken = "";//获取access_token填写client_credentialString grantType = "client_credential";String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grantType + "&appid=" + wxProperties.getAppId() + "&secret=" + wxProperties.getAppSecret();try {URL urlGet = new URL(url);HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();// 必须是get方式请求http.setRequestMethod("GET");http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");http.setDoOutput(true);http.setDoInput(true);System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒http.connect();InputStream is = http.getInputStream();int size = is.available();byte[] jsonBytes = new byte[size];is.read(jsonBytes);String message = new String(jsonBytes, "UTF-8");JSONObject demoJson = JSONObject.parseObject(message);System.out.println("JSON字符串:" + demoJson);accessToken = demoJson.getString("access_token");is.close();} catch (Exception e) {e.printStackTrace();}return accessToken;}

调用链路:微信扫码 ===> 微信后台回调我们后台判断事件类型,将随机字符串和openid写入缓存中,前端定时扫码缓存接口,有则跳转登录。

后端检查是否扫码接口:

  / * 检查扫码状态** @param sceneStr 随机字符串* @return*/@GetMapping("/checkScanState")public AjaxResult wechatLogin(@RequestParam(name = "sceneStr") String sceneStr) {Object openId = redisCache.getCacheObject(sceneStr);if (openId == null) {return AjaxResult.success("未登入");}QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();queryWrapper.eq("open_id", openId);SysUser sysUser = sysUserMapper.selectOne(queryWrapper);if (sysUser != null) {AjaxResult ajax = AjaxResult.success();String token = sysLoginService.autoLogin(sysUser.getUserName(), (String) openId);ajax.put(Constants.TOKEN, token);return ajax;}return AjaxResult.success("未登入");}

前端代码:

 // 微信公众号授权登录wechatLogin() {this.isWechatLogin = true;this.sceneStr = this.generateRandomString(16);this.getLoginQrCode();},// 账号密码登录userNamePassLogin() {this.isWechatLogin = false;},// 获取登录二维码getLoginQrCode() {// todo 需要改成随机字符串let data = {sceneStr: this.sceneStr}getQrCode(data).then(res => {this.qrImageUrl = res.msg;setInterval(() => {this.check();}, 1000);})},// 随机数generateRandomString(length) {const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';let result = '';for (let i = 0; i < length; i++) {const randomIndex = Math.floor(Math.random() * characters.length);result += characters[randomIndex];}return result;},check() {if (!this.isWechatLogin) return false;let data = {sceneStr: this.sceneStr}this.$store.dispatch("CheckScanState", data).then((res) => {if (res.token) {this.isWechatLogin = false;}console.log(res);console.log("check....");this.$router.push({path: this.redirect || "/"}).catch(() => {});}).catch(() => {});},

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++之构造函数和析构函数

目录 名词解释&#xff1a; 1、 c的类在没有加权限的时候&#xff0c;默认为private。只有类内可以访问&#xff0c;类外不能访问。 2、共有接口 &#xff08;1&#xff09;接着上面的代码&#xff0c;如何在类外直接访问private的内容&#xff1f; &#xff08;2&#xff…

什么是NAT?NAT类型有哪些?

晚上好&#xff0c;我的网工朋友。NAT是一种地址转换技术&#xff0c;它可以将IP数据报文头中的IP地址转换为另一个IP地址&#xff0c;并通过转换端口号达到地址重用的目的。 在大多数网络环境中&#xff0c;我们都需要通过 NAT 来访问 Internet。 NAT作为一种缓解IPv4公网地址…

二、mongoose的使用,实现用户集合的操作

前言 mongodb&#xff1a;为了在node应用中与MongoDB交互&#xff0c;开发者需要使用MongoDB的驱动程序&#xff0c;所以安装的mongodb就是其驱动程序&#xff1b; mongoose: 是一个用于 MongoDB 的对象建模工具&#xff0c;提供了一个丰富的查询语言和许多其他功能&#xff0c…

Linux:共享内存

文章目录 System V共享内存的原理管理共享内存shmgetshmatshmdtshmctl 共享内存和管道实现进程间同步通信 前面介绍完了匿名管道和命名管道&#xff0c;那么本篇要引入的主题是共享内存 System V 作为进程通信部分的内容&#xff0c;共享内存必然有其存在的意义和价值&#x…

计算机网络——网络层(2)

计算机网络——网络层&#xff08;2&#xff09; 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 网络层——控制平面概述路由选择转发表路由协议路由信息的交换小结 路由选择算法常见的路由选择算法距离矢量路由算法工作原理优缺点分析 链路状态路由算法基本工作原理优…

day11_oop

今日内容 零、 复习昨日 一、作业 二、封装 三、继承 四、重写 五、this和super 六、访问修饰符 零、 复习昨日 数组创建的两种方式 new int[3];new int[]{1,2,3,4}; 手写一个遍历输出数组元素的伪代码 构造方法什么作用? 创建对象,将对象的属性初始化 有参无参构造什么区别?…

【IEEE会议征稿通知】2024年算法、软件工程与网络安全国际学术会议(ASENS 2024)

2024年算法、软件工程与网络安全国际学术会议&#xff08;ASENS 2024&#xff09; The International Conference on Algorithms, Software Engineering and Network Security 2024年算法、软件工程与网络安全国际学术会议&#xff08;ASENS 2024&#xff09;将于2024年3月29…

error: failed to open index: Database already open. Cannot acquire lock报错解决办法

ordinals节点数据同步出现报错 error: failed to open index: Database already open. Cannot acquire lock.问题分析&#xff1a; 出现问题的原因是btcoin core节点数据没有同步完我们就开始进行ordinals数据同步&#xff0c;导致/root/.local/share/ord/index.redb 文件数据…

深度强化学习(王树森)笔记03

深度强化学习&#xff08;DRL&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。本文在ChatGPT辅助下完成。 参考链接 Deep Reinforcement Learning官方链接&#xff1a;https://github.com/wangshusen/DRL 源代码链接&#xff1a;https://github.c…

在Visual Studio 2022中将源文件扩展名改为 .c 后,没有显示 #define _CRT_SECURE_NO_WARNINGS 1?

一、问题 在Visual Studio 2022中将源文件扩展名改为 .c 后&#xff0c;没有显示 #define _CRT_SECURE_NO_WARNINGS 1&#xff1f; 二、解答 对于使用了不安全的C运行时库函数&#xff08;如strcpy、scanf等&#xff09;而触发的安全警告&#xff0c;编译器不会默认包含_CRT_S…

ubuntu中的rsyslog

目录 1. rsyslog简介 2. 查看/var/log 3. syslog的配置文件 3.1 /etc/rsyslog.d/50-default.conf 3.2 /etc/rsyslog.conf 4. 如何写入syslog 4.1 C语言 4.2 shell 4.3 内核输出 5. syslog.1和syslog.2.gz等文件是如何生成 6. logrotate是如何被执行 7. 如何限制sys…

OJ_糖果分享游戏

题干 c实现 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<vector> using namespace std;void ShareCandy(vector<int>& student) {int size student.size();vector<int> share(size); //保存每个同学交换前&#xff0c;糖果数量…