阅读 282
基于Spring Boot实现电脑端网页微信扫码授权登录方式一(附带完整源码)

基于Spring Boot实现电脑端网页微信扫码授权登录方式一(附带完整源码)

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

简介

电脑端微信网页扫码授权登录有2种方式:

  • 第一种:基于微信公众号,单独获取登录二维码扫码,然后扫码登录,程序控制跳转逻辑,例如CSDN:

在这里插入图片描述

  • 第二种:基于微信开放平台,跳转到微信二维码页面进行扫码登录,重定向到成功页面,例如有道笔记:

在这里插入图片描述 注: 本文记录第一种方式,只需通过微信测试公众号即可完成完整测试,即所有人本地都可以完整运行;第二种需有通过认证资质的开发者账号,后续再记录。

本地完整运行环境准备

内网渗透=>生成本地指定端口映射的外网域名

传送门:内网渗透工具Natapp使用详解 域名生成之后修改配置文件: 在这里插入图片描述

注册并配置微信测试公众号

  1. 注册地址:微信公众平台 测试账号申请,扫码登录,并关注该测试号

在这里插入图片描述 在这里插入图片描述

  1. 获取测试号appid和appsecret

在这里插入图片描述

3.接口配置信息修改(此处会回调后台签名验证方法,配置时需启动后台在这里插入图片描述 4.配置网页授权域名,用于获取微信用户信息 在这里插入图片描述 在这里插入图片描述

流程

文字简述

  1. 页面点击获取微信登录二维码按钮
  2. 后台以appid和appsecret为参数调用微信api获取access_token
  3. 以access_token为参数调用微信api获取本地微信登录二维码ticket,同时传随机字符串参数scene_str,该参与可用于程序验证是否扫码成功
  4. 通过ticket获取二维码成功,页面显示,同时前台开始进行js定时任务(此处可优化成后台通知),监听是否扫码成功
  5. 用户微信扫码成功
  6. 微信服务器端回调本地服务器签名验证接口验证签名和回调处理
  7. 回调处理确定扫码成功(此处还可进行关注验证),数据库或缓存插入本地扫码成功记录
  8. 前台定时任务获取到扫码成功记录或者后台主动通知,进行扫码成功后业务处理

代码

1.获取微信二维码和启动js定时任务

前台html

<h1>微信扫码登录方式一</h1>
    <button onclick="getQrCode()" style="width: 100px;height: 50px;">获取二维码</button>
    <br>
    <img src="" id="qrCodeImgId"  style="width: 300px;height: 300px;display: none">
<hr>
复制代码

前台js

//======================================微信扫码登录方式一=========================================================
    // 存储二维码标识,用于验证是否扫码成功
    var sceneStr;
    var t;
    // 获取登录二维码
    function getQrCode(){
        $.get('qrCodeFirstLogin/getQrCode',function (data) {
            console.log("=============getQrCode=======================");
            console.log(data);
            if(data.code == 200){
                sceneStr = data.data.sceneStr;
                // 二维码获取
                $('#qrCodeImgId').attr('src',"https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+data.data.ticket);
                $('#qrCodeImgId').show();
                // 定时任务监听是否扫码成功
                t = window.setInterval(getOpenId,3000);
            }else{
                alert(data.msg);
            }

        });
    }

复制代码

后台获取二维码ticket接口

    /**
     *  获取accessToken
     * @return
     */
    public String getAccessToken(){
        String accessToken = null;
        String getTokenUrl = wxConfig.getTokenUrl().replace("APPID", wxConfig.getAppId()).replace("SECRET", wxConfig.getAppSecret());
        String result = HttpClientUtil.doGet(getTokenUrl);
        JSONObject jsonObject = JSONObject.parseObject(result);
        accessToken = jsonObject.getString("access_token");
        return accessToken ;
    }
    /**
     *  获取登录二维码
     * @return
     */
    @GetMapping("/getQrCode")
    private ResultJson getQrCode(){
        try {
            // 获取token开发者
            String accessToken =getAccessToken();
            String getQrCodeUrl = wxConfig.getQrCodeUrl().replace("TOKEN", accessToken);
            // 这里生成一个带参数的二维码,参数是scene_str
            String sceneStr = CodeLoginUtil.getRandomString(8);
            String json="{\"expire_seconds\": 604800, \"action_name\": \"QR_STR_SCENE\"" +", \"action_info\": {\"scene\": {\"scene_str\": \""+sceneStr+"\"}}}";
            String result  = HttpClientUtil.doPostJson(getQrCodeUrl,json);
            JSONObject jsonObject = JSONObject.parseObject(result);
            jsonObject.put("sceneStr",sceneStr);
            return ResultJson.ok(jsonObject);
        } catch (Exception e) {
            e.printStackTrace();
            return ResultJson.error(e.getMessage());
        }
    }
复制代码

2.签名验证和回调处理

/**
     *  验证签名
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping("/checkSign")
    public String checkSign ( HttpServletRequest request) throws Exception {
        log.info("===========>checkSign");
        //获取微信请求参数
        String signature = request.getParameter ("signature");
        String timestamp = request.getParameter ("timestamp");
        String nonce = request.getParameter ("nonce");
        String echostr = request.getParameter ("echostr");
        //参数排序。 token 就要换成自己实际写的 token
        String [] params = new String [] {timestamp,nonce,"123456"} ;
        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 = CodeLoginUtil.bytes2HexString(digestResult);
        mysignature=mysignature.toLowerCase(Locale.ROOT);
        //是否正确
        boolean signsuccess = mysignature.equals(signature);
        //逻辑处理
        if (signsuccess && echostr!=null) {
            //peizhi  token
            return echostr  ;//不正确就直接返回失败提示.
        }else{
            JSONObject jsonObject = callback(request);
            return jsonObject.toJSONString();
        }
    }


    /**
     *  回调方法
     * @param request
     * @return
     * @throws Exception
     */
    public JSONObject callback(HttpServletRequest request) throws Exception{
        log.info("===========>callback");
        //request中有相应的信息,进行解析
        WxMpXmlMessage message= WxMpXmlMessage.fromXml(request.getInputStream());//获取消息流,并解析xml
        String messageType=message.getMsgType();								//消息类型
        String messageEvent=message.getEvent();								    //消息事件
        // openid
        String fromUser=message.getFromUser();									//发送者帐号
        String touser=message.getToUser();										//开发者微信号
        String text=message.getContent();										//文本消息  文本内容
        // 生成二维码时穿过的特殊参数
        String eventKey=message.getEventKey();									//二维码参数

        String uuid="";															//从二维码参数中获取uuid通过该uuid可通过websocket前端传数据
        String userid="";

        //if判断,判断查询
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code","200");
        if(messageType.equals("event")){
             jsonObject = null;
            //先根据openid从数据库查询  => 从自己数据库中查取用户信息 => jsonObject 
            if(messageEvent.equals("SCAN")){
                //扫描二维码
                //return "欢迎回来";
            }
            if(messageEvent.equals("subscribe")){
                //关注
                //return "谢谢您的关注";
            }
            //没有该用户
            if(jsonObject==null){
                //从微信上中拉取用户信息
                String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" +getAccessToken() +
                        "&openid=" + fromUser +
                        "&lang=zh_CN";
                String result = HttpClientUtil.doGet(url);
                jsonObject = JSONObject.parseObject(result);
                /**
                 * 用户信息处理....
                 */
            }
            // 扫码成功,存入缓存
            loginMap.put(eventKey,new CodeLoginKey(eventKey,fromUser));
            return jsonObject;
        }
        return jsonObject;
        //log.info("消息类型:{},消息事件:{},发送者账号:{},接收者微信:{},文本消息:{},二维码参数:{}",messageType,messageEvent,fromUser,touser,text,eventKey);
    }
复制代码

3.扫码成功获取用户信息取消定时任务

js定时任务监听

    // 扫码成功,获取用户openId=>为获取用户信息做准备
    function getOpenId() {
        $.get("qrCodeFirstLogin/getOpenId",{
            "eventKey":sceneStr
        },function (data) {
            if(data.code == 200){
                console.log("========getOpenId==========");
                console.log(data.data);
                window.clearInterval(t);
                alert("登录成功openId:"+data.data.openId);
                /**
                 * 1、第一次扫码登录进行账号绑定
                 * 2、以后根据openId获取用户信息
                 */
            }
        });
    }
复制代码

后台验证接口

    /**
     *  根据二维码标识获取用户openId=>获取用户信息
     * @param eventKey
     * @return
     */
    @RequestMapping("getOpenId")
    public ResultJson getOpenId(String eventKey){
        if(loginMap.get(eventKey) == null){
            return ResultJson.error("未扫码成功!") ;
        }
        CodeLoginKey codeLoginKey = loginMap.get(eventKey);
        loginMap.remove(eventKey);
        return ResultJson.ok(codeLoginKey);
    }
复制代码

结果演示

获取二维码

在这里插入图片描述

扫码成功

在这里插入图片描述

源码

传送门:微信pc扫码登录

文章分类
后端