微信公众号扫码登录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

微信公众号扫码登录PC后台系统_第1张图片

账号和地址配置:

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文件转成易处理的map
            final Map map = ParseXmlUtils.parseXml(request);
            //开发者微信号
            final String toUserName = map.get("ToUserName");
            //OpenId
            final String fromUserName = map.get("FromUserName");
            //消息创建时间 (整型)
            final String createTime = map.get("CreateTime");
            //消息类型,event
            final 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 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 parseXml(HttpServletRequest request) throws Exception {
        //存放所有的子节点
        Map map = new HashMap();
        //获取到文档对象
        InputStream inputStream = request.getInputStream();
        SAXReader reader = new SAXReader();
        //读取输入流,获取文档对象
        Document document = reader.read(inputStream);
        //根据文档对象获取根节点
        Element root = document.getRootElement();
        //获取根节点的所有子节点
        List 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 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 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 parseXml, String content) {
        BaseMessage msg = null;
        //获取到消息的类型,如果还有其他类型的消息直接加case就可以
        String type = parseXml.get("MsgType");
        //调用处理文本消息的方法
        if (type.equals("text") || type.equals("event")) {
            msg = dealTextMessage(parseXml, content);
        }
        //如果该消息不为null
        if (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 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 data = new HashMap<>();
        data.put("expire_seconds", 3600);
        data.put("action_name", "QR_STR_SCENE");
        Map actionInfo = new HashMap<>();
        Map 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_credential
        String 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 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(() => {});
      },

你可能感兴趣的:(微信,微信,java,spring)