微信公众号扫码登录

1.设计

我们采用的是个人号登录方式,这样拿不到我们的userInfo用户信息,然后我们将用户发来的消息(xml消息体)中的FromUser作为我们唯一的openId
整体流程:
1.用户扫码公众号码,然后发一条消息:验证码,我们就会通过api回复一个随机的码存入Redis中(主要结构是loginCode.随机码,value为openId)
2.当用户输入后点击登录就进入我们的注册模块,同时关联角色和权限,实现网关的统一鉴权
3.用户就可以根据个人的openId来维护个人信息
4.用户登录成功后,返回token,前端所有亲亲贵带着token就可以访问了

2.代码

1.微信的callback接口,实现了对微信公众平台的回调验证功能,确保请求真实有效
在接收微信公众平台的回调请求时,该方法会对请求中的参数进行验证,确保这个请求是真实的,如果验证成功就会返回一个随机的字符串确保它的有效性;

 /*** 1.回调验证(当前这个服务是否同样的)* 该代码实现了微信公众平台的回调验证功能。在接收到来自微信服务器的消息回调请求时,* 该方法会首先对请求中的参数进行签名验证,以确保请求是真实的。如果验证通过,* 则方法会返回一个随机字符串,作为确认该请求的有效性。如果验证失败,则方法会返回一个错误消息。* @param signature:微信加密签名* @param timestamp:时间戳作加密使用* @param nonce:随机数* @param echostr:随机字符串* @return:如果通过则返回随机字符串 echostr,否则unknown*/@GetMapping("callback")public String callback(@RequestParam("signature") String signature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam("echostr") String echostr) {log.info("get验签请求参数:signature:{},timestamp:{},nonce:{},echostr:{}",signature, timestamp, nonce, echostr);String shaStr = SHA1.getSHA1(token, timestamp, nonce, "");if (signature.equals(shaStr)) { //判断生成的字符串签名与传入的签名是否一致return echostr;}return "unknown";}

在这里插入图片描述
确定请求有效性后,返回:在这里插入图片描述
2.对于普通消息的处理:
**1.在我们接收到来自微信服务器的普通消息时,我们会将消息解析为XML格式——>2.然后我们利用子当以的UtilXml转为Map集合,然后提取消息类型事件类型,然后将消息类型和事件类型封装起来——>3.**根据类型确定一个消息处理器(WxChatMsgHandler对象),我们的处理器会根据不同的类型,生成对应的回复内容

 /*** 2.普通消息的处理* 。在接收到来自微信服务器的普通消息时,该方法会将消息解析为 XML 格式,并从消息中提取出消息类型和事件类型(如果有)。* 根据消息类型和事件类型,该方法会选择一个适当的消息处理器(即 WxChatMsgHandler 对象),并将消息的具体内容传给该处理器进行处理。* 处理器会根据不同的消息类型和事件类型,生成相应的回复内容,并返回给微信服务器。* @param requestBody* @param signature* @param timestamp* @param nonce* @param msgSignature* @return*/@PostMapping(value = "callback", produces = "application/xml;charset=UTF-8")public String callback(@RequestBody String requestBody, // 接收到的原始 XML 消息内容@RequestParam("signature") String signature, // 签名串@RequestParam("timestamp") String timestamp, // 时间戳@RequestParam("nonce") String nonce, // 随机数@RequestParam(value = "msg_signature", required = false) String msgSignature // 消息签名) {// 打印接收到的消息内容log.info("接收到微信消息:requestBody:{}", requestBody);// 使用 MessageUtil 工具类将 XML 消息解析为 MapMap<String, String> messageMap = MessageUtil.parseXml(requestBody);// 获取消息类型和事件类型String msgType = messageMap.get("MsgType");String event = messageMap.get("Event") == null ? "" : messageMap.get("Event");log.info("msgType:{},event:{}", msgType, event);// 构造一个字符串,用于标识消息类型和事件类型StringBuilder sb = new StringBuilder();sb.append(msgType);if (!StringUtils.isEmpty(event)) {sb.append(".");sb.append(event);}// 根据消息类型和事件类型获取对应的处理器String msgTypeKey = sb.toString();WxChatMsgHandler wxChatMsgHandler = wxChatMsgFactory.getHandlerByMsgType(msgTypeKey);if (Objects.isNull(wxChatMsgHandler)) {return "unknown";}// 使用处理器处理消息,并生成回复内容String replyContent = wxChatMsgHandler.dealMsg(messageMap);log.info("replyContent:{}", replyContent);return replyContent;}

将XML转为Map的工具类:
https://blog.csdn.net/weixin_57128596/article/details/136136650?spm=1001.2014.3001.5501

根据类型确定处理器:
实现InitalLizingBean,重写afterPropertiesSet方法,将类型以及对应的处理器放入Map中

package com.wyh.wx.handler;import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Component
public class WxChatMsgFactory implements InitializingBean {@Resourceprivate List<WxChatMsgHandler> wxChatMsgHandlerList;private Map<WxChatMsgTypeEnum, WxChatMsgHandler> handlerMap = new HashMap<>();public WxChatMsgHandler getHandlerByMsgType(String msgType) {WxChatMsgTypeEnum msgTypeEnum = WxChatMsgTypeEnum.getByMsgType(msgType);return handlerMap.get(msgTypeEnum);}@Overridepublic void afterPropertiesSet() throws Exception {for (WxChatMsgHandler wxChatMsgHandler : wxChatMsgHandlerList) {handlerMap.put(wxChatMsgHandler.getMsgType(), wxChatMsgHandler);}}}

处理器WxChatMsgHandler根据具体类型响应消息:

package com.wyh.wx.handler;import com.wyh.wx.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;@Component
@Slf4j
public class ReceiveTextMsgHandler implements WxChatMsgHandler {private static final String KEY_WORD = "验证码";private static final String LOGIN_PREFIX = "loginCode";@Resourceprivate RedisUtil redisUtil;@Overridepublic WxChatMsgTypeEnum getMsgType() {return WxChatMsgTypeEnum.TEXT_MSG; // 处理文本消息}@Overridepublic String dealMsg(Map<String, String> messageMap) {log.info("接收到文本消息事件");String content = messageMap.get("Content"); // 文本消息内容if (!KEY_WORD.equals(content)) {return ""; // 如果不是“验证码”,则返回空字符串}String fromUserName = messageMap.get("FromUserName"); // 发送者的 OpenIDString toUserName = messageMap.get("ToUserName"); // 接收者的 OpenIDRandom random = new Random();int num = random.nextInt(9000) + 1000; // 生成一个 4 位数字的验证码String numKey = redisUtil.buildKey(LOGIN_PREFIX, String.valueOf(num)); // 验证码的键redisUtil.setNx(numKey, fromUserName, 5L, TimeUnit.MINUTES); // 将发送者的 OpenID 作为值,并将其存储在 Redis 中,有效期为 5 分钟String numContent = "您当前的验证码是:" + num + "! 5分钟内有效"; // 验证码的文本内容String replyContent = "<xml>\n" +"  <ToUserName><![CDATA[" + fromUserName + "]]></ToUserName>\n" +"  <FromUserName><![CDATA[" + toUserName + "]]></FromUserName>\n" +"  <CreateTime>12345678</CreateTime>\n" +"  <MsgType><![CDATA[text]]></MsgType>\n" +"  <Content><![CDATA[" + numContent + "]]></Content>\n" +"</xml>"; // 回复的 XML 内容return replyContent; // 返回回复的 XML 内容}}

3.得到验证码后我们请求登录

流程:
在得到验证码后,我们利用验证码请求登录,将用户注册到数据库中并得到tokenValue

代码:

 @RequestMapping("doLogin")public Result<SaTokenInfo> doLogin(@RequestParam("validCode") String validCode) {try {Preconditions.checkArgument(!StringUtils.isBlank(validCode), "验证码不能为空!");return Result.ok(authUserDomainService.doLogin(validCode));} catch (Exception e) {log.error("UserController.doLogin.error:{}", e.getMessage(), e);return Result.fail("用户登录失败");}}

业务Domain层:
1:首先会根据我们的验证码得到我们的唯一的openId,然后将其封装到AuthUser类中,进行注册(如果数据库中已经存在则直接返回true),否则进行注册,关联角色权限
2:注册完后,利用SaToken进行登录loginId(),然后获取token信息
在这里插入图片描述
3:然后我们就可以通过tokenValue去请求一些权限接口

 /*** 4.登录1. 根据验证码生成登录键(loginKey)2. 从 Redis 中获取 openId,如果不存在则返回 null3. 如果 openId 不为空,则使用 AuthUserBO 对象封装用户信息,并调用 register 方法将其注册到系统中4. 使用 StpUtil.login 方法登录系统,并获取 Token 信息5. 返回 Token 信息* @param validCode* @return*/@Overridepublic SaTokenInfo doLogin(String validCode) {// 根据验证码生成登录键String loginKey = redisUtil.buildKey(LOGIN_PREFIX, validCode);// 从 Redis 中获取 openId,如果不存在则返回 nullString openId = redisUtil.get(loginKey);if (StringUtils.isBlank(openId)) {return null;}// 使用 AuthUserBO 对象封装用户信息,并调用 register 方法将其注册到系统中(new一个authUser封装openid去数据库查询)AuthUserBO authUserBO = new AuthUserBO();authUserBO.setUserName(openId);this.register(authUserBO);// 使用 StpUtil.login 方法登录系统,并获取 Token 信息StpUtil.login(openId);SaTokenInfo tokenInfo = StpUtil.getTokenInfo();// 返回 Token 信息return tokenInfo;}/*** 注册** @param authUserBO* @return*/@Override@SneakyThrows@Transactional(rollbackFor = Exception.class)public Boolean register(AuthUserBO authUserBO) {//校验用户是否存在,如果存在该用户,直接returnAuthUser existAuthUser = new AuthUser();existAuthUser.setUserName(authUserBO.getUserName());List<AuthUser> existUser = authUserService.queryByCondition(existAuthUser);if (existUser.size() > 0) {return true;}//1.Bo转entity类AuthUser authUser = AuthUserBOConverter.INSTANCE.convertBOToEntity(authUserBO);//2.如果密码不为空,则进行md+盐值加密(照顾微信登录)if(!StringUtils.isBlank(authUser.getPassword())){authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(), salt));}//3.用户open状态authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());//4.插入用户数据Integer count = authUserService.insert(authUser);//5.建立一个角色与用户的关系AuthRole authRole = new AuthRole();authRole.setRoleKey(AuthConstant.NORMAL_USER);AuthRole roleResult = authRoleService.queryByCondition(authRole); //查询 普通用户 这一角色Long roleId = roleResult.getId();Long userId = authUser.getId();//5.将用户与角色的关系进行建立AuthUserRole authUserRole = new AuthUserRole();authUserRole.setUserId(userId);authUserRole.setRoleId(roleId);authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());authUserRoleService.insert(authUserRole);//4.auth业务中将用户角色以及对应的权限注入redis(UserName作为openId)String roleKey = redisUtil.buildKey(authRolePrefix, authUser.getUserName());LinkedList<AuthRole> roleList = new LinkedList<>();roleList.add(authRole);redisUtil.set(roleKey,new Gson().toJson(roleList));//4.2将权限注入RedisAuthRolePermission authRolePermission = new AuthRolePermission();authRolePermission.setRoleId(roleId); //新注册用户的角色idList<AuthRolePermission> authRolePermissionList = authRolePermissionService.queryByCondition(authRolePermission);List<Long> permissionIdList = authRolePermissionList.stream().map(AuthRolePermission::getPermissionId).collect(Collectors.toList()); //对应角色id的所有权限idsList<AuthPermission> permissionList = authPermissionService.queryByPermissionIds(permissionIdList);String permissionKey = redisUtil.buildKey(authPermissionPrefix, authUser.getUserName());redisUtil.set(permissionKey,new Gson().toJson(permissionList)); //将权限对应key和集合注入redis中return count > 0;}

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

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

相关文章

最适合初学者的Python入门详细攻略,一文讲清,赶紧收藏!

前言 目前python可以说是一门非常火爆的编程语言&#xff0c;应用范围也非常的广泛&#xff0c;工资也挺高&#xff0c;未来发展也极好。 Python究竟应该怎么学呢&#xff0c;我自己最初也是从零基础开始学习Python的&#xff0c;给大家分享Python的学习思路和方法。一味的买…

sqlserver对已有的表插入列

现有如下的一个表&#xff1b; 现在要插入一个 人员id 列&#xff1b;如下图在设计视图的行首单击&#xff0c;选择 插入列&#xff1b; 然后添加一个 人员id 列&#xff1b; 保存&#xff0c;出现下图提示&#xff0c;不能保存设计&#xff1b; 这就直接使用sql语句更改&#…

openGauss学习笔记-220 openGauss性能调优-确定性能调优范围-查询最耗性能的SQL

文章目录 openGauss学习笔记-220 openGauss性能调优-确定性能调优范围-查询最耗性能的SQL220.1 操作步骤 openGauss学习笔记-220 openGauss性能调优-确定性能调优范围-查询最耗性能的SQL 系统中有些SQL语句运行了很长时间还没有结束&#xff0c;这些语句会消耗很多的系统性能&…

QGIS004:【08图层工具箱】-导出到电子表格、提取图层范围

摘要&#xff1a;QGIS图层工具箱常用工具有导出到电子表格、提取图层范围等选项&#xff0c;本文介绍各选项的基本操作。 实验数据&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ZK4_ShrQ5BsbyWfJ6fVW4A?pwdpiap 提取码&#xff1a;piap 一、导出到电子表格 工具…

Java与JavaScript同源不同性

Java是目前编程领域使用非常广泛的编程语言&#xff0c;相较于JavaScript&#xff0c;Java更被人们熟知。很多Java程序员想学门脚本语言&#xff0c;一看JavaScript和Java这么像&#xff0c;很有亲切感&#xff0c;那干脆就学它了&#xff0c;这也间接的帮助了JavaScript的发展…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之NavDestination组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之NavDestination组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、NavDestination组件 作为NavRouter组件的子组件&#xff0c;用于显示导…

代码随想录算法训练营第十九天|654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

654.最大二叉树 刷题https://leetcode.cn/problems/maximum-binary-tree/description/文章讲解https://programmercarl.com/0654.%E6%9C%80%E5%A4%A7%E4%BA%8C%E5%8F%89%E6%A0%91.html视频讲解https://www.bilibili.com/video/BV1MG411G7ox/?vd_sourceaf4853e80f89e28094a5fe…

【LeetCode: 103. 二叉树的锯齿形层序遍历 + BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

PyTorch 快速安装 - 基于 JupyterHub 并运行 K8s

PyTorch 快速安装 - 基于 JupyterHub 并运行 K8s 运行 PyTorch 可以直接逻辑运行、容器中运行、KubeFlow 中运行以及基于 JupyterHub&#xff08;独立运行或运行在 K8s 之上&#xff09;等多种模式。这里介绍运行在 K8s 上基于 JupyterHub 的 PyTorch 方法&#xff0c;这也是运…

阿里云服务器租用收费标准价格表(2024年更新)

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

阿里云服务器服务费怎么计算的?详细报价解析

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

云计算基础-云计算概念

云计算定义 云计算是一种基于互联网的计算方式&#xff0c;通过这种计算方式&#xff0c;共享的软硬件资源和信息可以按需提供给计算机和其他设备。云计算依赖资源共享以达成规模经济&#xff0c;类似基础设置(如电力网)。 云计算最基本的概念就是云加端&#xff0c;我们有一个…