springboot实现微信扫码登录和绑定
前言:系统中若用到微信扫码登录,则要进行微信公众账号授权,所以在开发功能之前,
需要到微信公众平台申请一个服务号,当然仅仅只是作为开发者,则使用测试公众账号也行。
有了公众号后,则需登录公众后台进行一些基础配置,配置流程如下
1.点击设置–>选择公众号设置功能设置–>配置好业务域名和网页授权域名。
2.点击开发–>选择基本配置–>配置公众号开发信息
详细配置可查看[这篇博文]
一、需求说明
1、用户在登录的时候可通过微信扫码登录
2、若用户在系统中未绑定微信,则可让用户到个人中心扫码绑定
二、业务实现逻辑
扫码登录的实现逻辑
1、用户进入系统登录页时需要生成微信登录二维码
2、用户扫码完成网页授权操作,然后微信要通知咱们系统,
3、微信回调咱们系统后,咱们系统需再向微信索取用户的微信账户信息(主要是拿用户的openid),
然后拿此openid到用户表查询一下,存在则说明此用户属于咱们系统,然后将用户信息丢到
缓存中
4、前端轮训后台是否登录
扫码绑定的实现逻辑
1、用户进入系统的个人中心时需要生成微信绑定二维码(此二维码需携带用户信息,如userid)
2、若当前用户没有关注公众号,则用户扫码跳转到关注公众号页面,完成关注并绑定
3、若当前用户已经关注公众号,则用户扫码跳转到公众号聊天欢迎页,同时进行账号绑定
4、前端轮训后台是否绑定
三、代码实现逻辑
* 微信登录流程:
* 1、前端请求后台接口生成UUID
* 2、前端获取授权二维码(传入UUID)
* 3、前端轮询后台是否登录(根据uuid轮询)
* 微信绑定流程:
* 1、前端请求后台接口生成UUID
* 2、前端获取绑定二维码(传入UUID,userId)
* 3、前端轮询后台是否绑定(根据uuid轮询)
pom.xml<!--wxforjava依赖原文链接:https://github.com/Wechat-Group/WxJava-->
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-mp</artifactId><version>2.9.0</version>
</dependency>
<!-- 谷歌二维码支持包原文链接:https://blog.csdn.net/xm526489770/article/details/83651555
-->
<dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.0</version>
</dependency><dependency><groupId>net.glxn</groupId><artifactId>qrgen</artifactId><version>1.4</version>
</dependency>
WeChatUtil微信工具类
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
public class WeChatUtil {private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);//wxjava所需服务类private static WxMpService wxMpService = null;//微信用户事件回调地址private static String WEIXIN_OAUTH2="https://XXX/api/wx/common/callback";/*** 获取WxMpService*/public static WxMpService getWxMpService() {if (wxMpService == null) {WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage();config.setAppId(appId); // 设置微信公众号的appidconfig.setSecret(appSecret); // 设置微信公众号的app corpSecretconfig.setToken("XXX"); // 设置微信公众号的token//config.setAesKey("..."); // 设置微信公众号的EncodingAESKeyconfig.setOauth2redirectUri(WEIXIN_OAUTH2);wxMpService = new WxMpServiceImpl();wxMpService.setWxMpConfigStorage(config);}return wxMpService;}
}
QRCodeUtil二维码生成工具类
生成二维码工具类的博客
Controller代码
@Api(tags = { "微信常用接口" }, description = "微信常用接口")
@RestController
@RequestMapping("/api/wx/common/")
public class WxAuthController {private static Logger logger = LoggerFactory.getLogger(WxAuthController.class);//用户操作相关的service@Autowiredprivate UsersService usersService;//获取微信发送消息的Serviceprivate static WxMpService wxMpService=WeChatUtil.getWxMpService();//远程调用服务@Autowiredprivate RestTemplate restTemplate;/*** @Desc获取唯一标识 此接口只接收前端的请求* @Author LRH* @Date 2020/6/28 17:02*/@ApiOperation("获取系统全局唯一标识")@RequestMapping(value = "/getGUID", method = {RequestMethod.POST})public JSONObject getGUID() throws IOException {//生成UUIDString uuid = UUID.randomUUID().toString().replaceAll("-", "");//返回封装JSONObject jsonObject = new JSONObject();jsonObject.put("Data", uuid);jsonObject.put(Constant.RETURN_STA, uuid == null ? Constant.ERROR : Constant.SUCCESS);//响应状态码jsonObject.put(Constant.RETURN_MSG, uuid == null ? Constant.QUERY_MSG_F : Constant.QUERY_MSG_S);//响应消息return jsonObject;}/*** @Desc该接口只接收微信回调请求获取code* @Author LRH* @Date 2020/6/28 16:05*/@ApiOperation("接收微信回调请求(网页授权回调)")@RequestMapping(value = "/code", method = {RequestMethod.GET,RequestMethod.POST})public void getCallBackCode(HttpServletRequest request, HttpServletResponse response) throws IOException, WxErrorException {logger.info("=============微信登录回调");String code = request.getParameter("code"); //获取codeString handlerType = request.getParameter("handlerType"); //获取二维码类型String uuid = request.getParameter("uuid"); //获取二维码uuidlogger.info("接收微信数据:code:" + code + ",handlerType:" + handlerType + ",uuid:" + uuid);// 获取用户微信账户信息(包含用户openid)WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(code);logger.info("返回微信用户信息:" + JSONObject.toJSONString(accessToken));if (accessToken != null) {if (handlerType.equals("login")) { //如果是登录//查询用户是否属于这个系统的账户logger.info("查询用户 "+accessToken.getOpenId()+" 是否属于这个系统的账户");UserVo user= usersService.getUserInfoByOpenId(accessToken.getOpenId());if(user!=null){user.setIsLogin(true); //不为空则允许登录logger.info("欢迎登录!!!");}else{user=new UserVo();user.setIsLogin(false); //为空则不允许登录logger.info("拒绝登录!!!");}logger.info("把用户数据存Redis中:{},有效时间1小时",JSONObject.toJSONString(user));RedisUtil.put(uuid,JSONObject.toJSONString(user),3600);response.sendRedirect(user.getIsLogin()?"https://XXX.com/fixed/ScanSuccess.html?uuid="+uuid:"https://XXX.com/fixed/ScanFail.html"); // 微信端跳转到响应页面(成功或失败页面)}}else{logger.info("获取微信用户无响应!!!");}}/*** @Desc获取微信授权二维码 此接口只接收前端的请求(监听IP白名单)* @Author LRH* @Date 2020/6/28 17:02*/@ApiOperation("获取微信授权二维码")@RequestMapping(value = "/getWxAuthorizeQR/{handlerType}/{UUID}", method = {RequestMethod.GET})public void getWxAuthorizeQR(@PathVariable("handlerType") String handlerType, @PathVariable("UUID") String uuid, HttpServletResponse response) throws IOException {logger.info("=============获取微信登录授权二维码,handlerType:{},uuid:{}",handlerType,uuid);if (!(handlerType.equals("login"))) {//与Constant中的throw new BusinessException(0, "请求参数错误");}if (uuid.equals("undefined")) {throw new BusinessException(0, "请求参数uuid错误:"+uuid);}// 授权请求地址(系统自定义的)String url = "https://XXX/api/wx/common/send/wxreq/" + handlerType + "/" + uuid;BufferedImage bufferedImage = QRCodeUtil.getBufferedImage(url, 250, "");response.setContentType("image/png");OutputStream os = response.getOutputStream();ImageIO.write(bufferedImage, "png", os);//将url转二维码}@ApiOperation("发起微信网页授权请求")@RequestMapping(value = "/send/wxreq/{handlerType}/{UUID}", method = {RequestMethod.GET})public void push_wx_auth(@PathVariable("handlerType") String handlerType, @PathVariable("UUID") String uuid, HttpServletResponse response) throws IOException {logger.info("发起用户微信认证:parame:{}", "handlerType:" + handlerType + ",uuid:" + uuid);if (!(handlerType.equals("login") || handlerType.equals("bind")||handlerType.equals("myapprove"))) {throw new BusinessException(0, "发起用户微信认证请求参数错误");}if (uuid == null) {throw new BusinessException(0, "发起用户微信认证请求参数错误");}//微信重定向页面String backUrl = "https://XXX/api/wx/common/code?handlerType=" + handlerType + "&uuid=" + uuid;// 授权页面地址String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + (系统appId)WeChatUtil.appId + "&redirect_uri="+ URLEncoder.encode(backUrl) + "&response_type=code" + "&scope=snsapi_userinfo"+ "&state=STATE#wechat_redirect";logger.info("发起用户微信认证url:" + url);response.sendRedirect(url);//重定向微信授权页}//生成关注公众号的二维码@ApiOperation("生成关注公众号的二维码")@RequestMapping(value = "/myGetWxQR/{UUID}", method = {RequestMethod.GET})public void myGetWxQR(@PathVariable("UUID") String uuid,String userId,HttpServletResponse response) throws IOException {logger.info("========调用微信,生成关注公众号的二维码");//获取接口访问凭证tokenString accessToken = WeChatUtil.getAccessToken();JSONObject paramJSON=new JSONObject();paramJSON.put("uuid",uuid);paramJSON.put("userId",userId==null?"":userId);logger.info("paramstr:{}",paramJSON);//凭证不为空则获取微信二维码WxMpQrCodeTicket wxMpQrCodeTicket=new WxMpQrCodeTicket();if(accessToken!=null) {try {wxMpQrCodeTicket= wxMpService.getQrcodeService().qrCodeCreateTmpTicket(paramJSON.toJSONString(), 100000);} catch (WxErrorException e) {logger.info("调用微信,生成关注公众号的二维码失败");e.printStackTrace();}//下面生成图片用的BufferedImage bufferedImage = QRCodeUtil.getBufferedImage(wxMpQrCodeTicket.getUrl(), 250, "");response.setContentType("image/png");OutputStream os = response.getOutputStream();ImageIO.write(bufferedImage, "png", os);}}private final static String MEDIATYPE_CHARSET_JSON_UTF8 = MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8";@RequestMapping(value = "/callback", method = {RequestMethod.GET,RequestMethod.POST}, produces = MEDIATYPE_CHARSET_JSON_UTF8)@ResponseBody@ApiOperation("微信用户操作事件回调")public String callback(HttpServletRequest request, HttpServletResponse response) throws Exception {//先验证是否为微信请求回调if("false".equals(checkSign(request))){logger.info("非微信访问,接口请求失败");throw new BusinessException(0,"非微信访问,接口请求失败");}WxMpXmlMessage message=WxMpXmlMessage.fromXml(request.getInputStream());//获取消息流,并解析xmlString messageType=message.getMsgType(); //消息类型String messageEvent=message.getEvent(); //消息事件String fromUser=message.getFromUser(); //发送者帐号String touser=message.getToUser(); //开发者微信号String text=message.getContent(); //文本消息 文本内容String eventKey=message.getEventKey(); //二维码参数String uuid=""; //从二维码参数中获取uuid通过该uuid可通过websocket前端传数据String userid=""; //从二维码参数中获取用户IDlogger.info("总的message:"+JSONObject.toJSONString(message));logger.info("消息类型:{},消息事件:{},发送者账号:{},接收者微信:{},文本消息:{},二维码参数:{}",messageType,messageEvent,fromUser,touser,text,eventKey);//获取微信服务器的IP地址/*String[] callbackIP = wxMpService.getCallbackIP();for(int i=0;i<callbackIP.length;i++){System.out.println("IP地址"+i+":"+callbackIP[i]);}*//*** 文本消息*/if(messageType.equals("text")){WxMpXmlOutTextMessage texts=WxMpXmlOutTextMessage.TEXT().toUser(fromUser).fromUser(touser).content("欢迎光临,热烈欢迎").build();String result = texts.toXml();System.out.println("响应给用户的消息:"+result);return result;}/*** 图片消息*/
// if(messageType.equals("image")){// String[] imaurl =
// {};
// //创建file对象
// Random rand = new Random();
// int index = rand.nextInt(3);
// File file=new File(imaurl[index]);
// //上传多媒体文件
// WxMediaUploadResult wxMediaUploadResult = wxMpService.getMaterialService().mediaUpload(WxConsts.MediaFileType.IMAGE, file);
// WxMpXmlOutImageMessage images = WxMpXmlOutMessage.IMAGE()
// .mediaId(wxMediaUploadResult.getMediaId())//获取上传到微信服务器的临时素材mediaid.
// .fromUser(touser)
// .toUser(fromUser)
// .build();
// String result = images.toXml();
// System.out.println("响应给用户的消息:"+result);
// return result;
// }//如果是订阅信息UsersRequest us=new UsersRequest();String userName=""; //用户名//查询当前用户是否绑定了系统logger.info("查询当前用户:{}是否绑定了系统",fromUser);UsersModel user = usersService.getUserByOpenId(fromUser);Boolean isbind=false;switch (messageEvent){case "subscribe"://解析二维码携带的参数(未关注)eventKey = eventKey.replace("qrscene_", "");if("subscribe".equals(messageEvent))logger.info("关注时触发订阅消息");case "SCAN":if("SCAN".equals(messageEvent))logger.info("扫描时触发订阅消息");//解析二维码携带的参数(已关注)JSONObject jsonObject = JSONObject.parseObject(eventKey);uuid=jsonObject.getString("uuid");userid=jsonObject.getString("userId");//不管3721直接获取用户头像WxMpUser wxMpUser=wxMpService.getUserService().userInfo(fromUser);logger.info("通过用户openid获取用户信息:"+JSONObject.toJSONString(wxMpUser));//则说明该用户微信账号还从来没绑定过系统//绑定过的就无需再绑,除非先解绑String rsmsg="";if(user==null){if(StringUtils.isNotEmpty(userid)){//如果有传用户idlogger.info("根据用户userid查询"+userid+"用户名称");UsersModel model = usersService.getUserById(userid);//如果用户openId不为空if(model!=null){if(StringUtils.isNotEmpty(model.getWxOpenid())){logger.info("扫码无效,此二维码已被绑定!!!");rsmsg="扫码无效,此二维码已被绑定!!!";}else{userName=model.getName()==null?"":model.getName()+","; //获取用户名us.setId(Integer.parseInt(userid)); //用户IDus.setWxOpenid(fromUser); //用户openIDus.setHeadImg(wxMpUser.getHeadImgUrl()); //用户头像us.setUnionId(wxMpUser.getUnionId()); //公众号加入微信开放平台才会有unionId//修改用户的openidlogger.info("当前正在修改"+userid+"用户的openId:"+fromUser+",用户头像:"+wxMpUser.getHeadImgUrl()+",unionId:"+wxMpUser.getUnionId());usersService.updateOneData(us);isbind=true;rsmsg="您的微信已经绑定您的erp账号,以后可以用微信扫码登录erp";}}}rsmsg=userName+rsmsg;}else{//不可重复绑定,您的账号已绑定到某某用户上rsmsg="不可重复绑定,您的账号已绑定到"+user.getName()+"用户上\n如需绑定,请先解绑!!!";}String result="";if(!StringUtils.isEmpty(rsmsg)){WxMpXmlOutTextMessage texts=WxMpXmlOutTextMessage.TEXT().toUser(fromUser).fromUser(touser).content(rsmsg).build();result= texts.toXml();logger.info("响应给用户的消息:"+result);}//pushMsg(fromUser,userHeadImage,uuid,Constant.SUCCESS);//推送给前端(不用websocket推了)//扫码后的用户数据存入redisUserVo userVo=new UserVo();userVo.setWxOpenid(fromUser);userVo.setUnionId(wxMpUser.getUnionId());userVo.setHeadImg(wxMpUser.getHeadImgUrl());userVo.setCode(Constant.SUCCESS);userVo.setIsbind(isbind);//是否绑定logger.info("把用户数据存Redis中:"+JSONObject.toJSONString(userVo));RedisUtil.put(uuid,JSONObject.toJSONString(userVo),3600);return result;case "unsubscribe":logger.info("取关时触发订阅消息");//其实用户的基本信息可以先放缓存if(user!=null){//绑定过则清除其在系统中的openid,用户头像logger.info("取关时用户数据:{}",JSONObject.toJSONString(user));us.setId(user.getId());us.setWxOpenid("");us.setUnionId("");if(us.getSex().equals("M")){us.setHeadImg("https://XXX/fixed/mhead.png");//男性默认头像}else{us.setHeadImg("https://XXX/fixed/fhead.png");//女性默认头像}usersService.updateOneData(us);}return null;}return null;}/*** @Desc配置到微信后台的地址* 微信事件触发请求回调验证* @Author LRH* @Date 2020/7/15 11:18*/public String checkSign ( HttpServletRequest request) throws Exception {//获取微信请求参数logger.info("接收微信公众号事件触发回调请求");String signature = request.getParameter ("signature");String timestamp = request.getParameter ("timestamp");String nonce = request.getParameter ("nonce");String echostr = request.getParameter ("echostr");//参数排序。 token 就要换成自己实际写的 tokenString [] params = new String [] {timestamp,nonce,token} ;Arrays.sort (params) ;//拼接String paramstr = params[0] + params[1] + params[2] ;//加密//获取 shal 算法封装类MessageDigest Sha1Dtgest = MessageDigest.getInstance("SHA-1") ;//进行加密byte [] digestResult = Sha1Dtgest.digest(paramstr.getBytes ("UTF-8"));//拿到加密结果String mysignature = WebUtils.byte2HexStr(digestResult);logger.info("微信加密,signature:{}",signature);logger.info("本地加密,mysignature:{}",mysignature);//是否正确boolean signsuccess = mysignature.equals(signature);if (!signsuccess) {logger.info("验证失败,signature check fail");return "false" ;//不正确就直接返回失败提示.}else{logger.info("验证成功");return "true" ;//不正确就直接返回失败提示.}}/*** @Desc获取用户扫码后的数据* @Author LRH* @Date 2020/7/16 13:26*/@ApiOperation("获取用户扫码后的数据")@RequestMapping(value = "getWxScanData/{uuid}",method = RequestMethod.GET)public Object getWxScanData(@PathVariable("uuid") String uuid){logger.info("==================从redis中获取用户扫码后的数据==========");//从redis中拿到用户信息String userInfo = RedisUtil.get(uuid);//获取用户对象logger.info("==================返回数据:{}",userInfo);return StringUtils.isNotEmpty(userInfo)?ResultVOUtil.success(userInfo):ResultVOUtil.error(0,"暂无数据,稍后再试!!!");}
}
public class WeChatUtil {private static Logger logger = LoggerFactory.getLogger(WeChatUtil.class);//wxjava所需服务类private static WxMpService wxMpService = null;//微信用户事件回调地址private static String WEIXIN_OAUTH2="https://XXX/api/wx/common/callback";/*** 获取WxMpService*/public static WxMpService getWxMpService() {if (wxMpService == null) {WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage();config.setAppId(appId); // 设置微信公众号的appidconfig.setSecret(appSecret); // 设置微信公众号的app corpSecretconfig.setToken("XXX"); // 设置微信公众号的token//config.setAesKey("..."); // 设置微信公众号的EncodingAESKeyconfig.setOauth2redirectUri(WEIXIN_OAUTH2);wxMpService = new WxMpServiceImpl();wxMpService.setWxMpConfigStorage(config);}return wxMpService;}
}
springboot实现微信扫码登录和绑定相关推荐
- springboot实现微信扫码登录并且绑定
前言:系统中若用到微信扫码登录,则要进行微信公众账号授权,所以在开发功能之前, 需要到微信公众平台申请一个服务号,当然仅仅只是作为开发者,则使用测试公众账号也行. 有了公众号后,则需登录公众后台进行一 ...
- SpringBoot整合微信扫码登录
SpringBoot整合微信扫码登录 准备工作 基本思路流程 搭建SpringBoot 引入依赖 加入配置文件 代码实现 工具类 controller层 结果 准备工作 1.登录官网了解到,学习者想本 ...
- 一篇文章,带你了解微信扫码登录
文章目录 前言 一.功能背景 二.扫码登录原理 1.基本原理 三.实现效果图 1.登录页 2.点击微信账号登录 3.已绑定微信的账号 4.未绑定微信号的账号 四.代码实现 1.准备工作 2.编写代码 ...
- Vue+abp微信扫码登录
最近系统中要使用微信扫码登录,根据微信官方文档和网络搜索相关文献实现了.分享给需要的人,也作为自己的一个笔记.后端系统是基于ABP的,所以部分代码直接使用了abp的接口,直接拷贝代码编译不通过. 注册 ...
- 微信扫码登录网页实现原理
扫码登录操作过程 浏览器输入:https://wx.qq.com/?lang=zh_CN 手机登录微信,利用"扫一扫"功能扫描网页上的二维码 手机扫描成功后,提示"登录网 ...
- (转)微信扫码登录网页实现原理
扫码登录操作过程 浏览器输入:https://wx.qq.com/?lang=zh_CN 手机登录微信,利用"扫一扫"功能扫描网页上的二维码 手机扫描成功后,提示"登录网 ...
- 微信扫码登录_JAVA
一.需求 在PC端的登录页面加个微信扫码的按钮,点击按钮弹出二维码,实现微信扫码登录网站的功能.如图: 二.调研 扫码登录属于微信开放平台提供的API,不是微信公众平台.这里需要注册等配置,暂不赘述. ...
- 通过微信扫码登录剖析 oauth2 认证授权技术
本文目录 前言 趣味解读oauth2 oauth2精髓 oauth2核心概念 结合微信登录深刻理解oauht2 本文小结 前言 相信很多小伙伴在学习 JAVA 的过程中或多或少接触或者开发过类似于 x ...
- Web应用多账号系统设计及微信扫码登录实现
https://www.cnblogs.com/beer/p/5538403.html 1 前言概述 公司对功能测试,性能测试,安全测试等等都做了比较好的自动化后,急需要一个MIS系统来统一管理这些结 ...
最新文章
- 直博清华的小姐姐!本科就发表了SCI,享受朝九晚五的学习生活,做自己的小太阳!...
- Ubuntu 安装任意版本Django
- TCP/IP协议之网络链接的背后故事
- 定时/计数器(定时和计数的功能)、定时器中断
- php右侧弹窗QQ客服,JavaScript_网页右侧悬浮滚动在线qq客服代码示例,网页右侧悬浮滚动QQ在线客服 - phpStudy...
- 【机器学习】总结:线性回归求解中梯度下降法与最小二乘法的比较
- SWF反编神器Action Script Viewer终身免费升级!
- 软件工程大学大三课表_专业选修课 | 面向大二、大三同学的专业选修课全面介绍来啦!...
- 牛客:阶乘结果换算进制后得到数字的尾部有几个0
- OPENCV与OPENCL
- C++/测绘附和导线测量源码
- iOS 出现:不受信任的开发者 弹框
- 携程2021年校招笔试题[2021年10月21日19点-21点]
- C#工作总结(一):Fleck的WebSocket使用
- Mixly电子音乐:蜗牛与黄鹂鸟
- 好的提问和寻找答案的网站(会时常更新)
- Gtest 测试指导 入门基础(A)
- 《弃子长安》第二章 长乐驱蛊
- 清华吴翼学长——ACM参赛故事
- android怎么增量编译,阿里秒级android增量编译工具freeLine的使用入门