微信扫描自定义二维码关注公众号并登录(二)移动端/微信端的设计与实现

上篇介绍了《微信扫描自定义二维码关注公众号并登录(一)PC端网站的设计与实现》   

很多情况下我们的网上是响应式的,如果用户在移动端访问或者微信里面访问,该如何实现微信扫码关注公众号并登录!?  这是个好问题。

PC端用户扫带参数的码,微信打开的是公众号,是不是还有回退返回打开页面!? 这样基本是不可以的,当用户离开关闭页面时,websocket 就关闭了,不会再收到消息; 如轮询也是页面关闭,js就不再执行。那我们就要另想解决办法。

当我们扫码关注公众号登录时,不管用户之前是否关注过公众号,我们的 服务都会给用户微信发一条消息,这里可不可给顺带用户发一个登录链接,用户收到消息点击链接完成登录??? 答案是,当然可以!

这里首先我们网站在移动端访问时,要判断是不是移动端访问,或者微信访问。

这里我们js 已经实现 , 移动端访问,不会使用websocket。

https://love.daquexing.com/theme/default/js/modules/authc.js

var from ='p';
    if(/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
        from ='m';
        if((/MicroMessenger/i).test(navigator.userAgent)){
          // 微信浏览器
            $('#hi_message').html('请长按图片,识别图中二维码登录');
        }
        $('#hi_message').show();
    } 
    var websocket = null;
    var Authc = {
        isAuthced: function () {
            return (typeof(window.app.LOGIN_TOKEN) != 'undefined' && window.app.LOGIN_TOKEN.length > 0);
        },
        showLogin : function () {
            var that = this;
             // 获取二维码链接
            jQuery.get(app.base + '/oauth2/qrcode?f='+from,  function (ret) {
                if (ret && ret.code == 0) {
                   // 给二维码赋值 和 timestamp
                    $('#qrcode').attr("src",qrcodeurl+ret.data.ticket) ;
                    $('#timestamp').val(ret.data.timestamp) ;
                    if (from =='p'){
                    // 建立socket
                     that.initWebSocket(ret.data.timestamp );
                     // 模式框 关闭的时候 关闭socket
                    $('#login_alert').on('hidden.bs.modal', function () {
                            that.closeWebSocket();
                     });
                    }
                    
                    
                } else {
                    $('#ajax_login_message').text(ret.message).show();
                }
            });


            $('#login_alert').modal();

并且我们在获取二维码链接的时候,带了一个参数f,区分是移动端还是pc端, (此处不考虑用户任务修改参数)。这样针对移动端和PC端生成不同的timestamp 参数,当用户扫码关注后,公众平台会给我们后台服务发送消息和用户信息,这里判断返回的timestamp 参数何种类型,如果是移动端,则在通知的消息里添加登录逻辑的链接。

if (timestamp.startsWith("m")) {
    loginStr="\n \n" +
            "==《大确幸网站》==\n" +
            "》》》点击 <a href=\""+website+"/oauth2/login?username="+userWxInfo.getNickname()+"&timestamp="+timestamp+"\">完成登录</a>";
}else{
    // socket 通知客户端
    JSONObject object = JSONObject.fromObject(userWxInfo);
    object.put("equipmentType", timestamp);
    //调用socket进行通信
    this.logger.info("调用socket进行通信 object: " + object.toString());
    webSocketController.onMessage(object.toString(), null);
}

实现效果如下:

 

!!!好了,移动端(微信里)微信扫码关注公众号登录基本就实现了。 重点是前端判断用户访问来源是移动端还是PC,生成不同类型timestamp参数,从而实现登录。

最后,希望本文对你有帮助,欢迎在微信里扫码体验一下

https://love.daquexing.com/login

如果你还有其他疑问,可以加我微信咨询

 


	

微信扫描自定义二维码关注公众号并登录(一)PC端网站的设计与实现

业务需求

大确幸网站实现扫描二维码关注微信公众号,如果已经关注公众号就自动登陆网站并获取其微信昵称,头像等信息,如果用户未关注就等用户关注公众号后自动登陆网站

–如果用户已关注公众号,网站端直接自动登陆,如果没有关注,就等用户关注公众号之后网站端自动登陆

(目前已经完成了这个功能,示例网址:https://love.daquexing.com/  大确幸-新社交,新生活,一个聚合百万单身年青的社群。大确幸是一个有温度的社区,宗旨是帮更多适龄单身青年解决单身问题。)

做微信扫码登陆,生成二维码必须是微信公众号中绑定的域这个域名,网站生成不了二维码(网站与微信服务器不是同一个域名) ,而是调用微信系统的接口获取二维码,用户扫码后也是请求微信服务器 。

ok 以上内容参考引用出自:

网站实现扫描二维码关注微信公众号,自动登陆网站并获取其信息

方案优化:网站实现扫描二维码关注微信公众号,自动登陆网站并获取其信息

网上还有一篇文章推荐:

微信扫描自定义二维码关注公众号

如果以上三篇你都看懂了, 下面?就简单, 或者你都不用看了。

微信公众平台技术文档:

获取access_token

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183

生成带参数的二维码

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542

前端页面实现相关js

https://love.daquexing.com/theme/default/js/modules/authc.js

前端获取二维码方法:

@RequestMapping(value = "/qrcode", method = RequestMethod.GET)
public @ResponseBody
Data getqrcode(@RequestParam(value = "f") String f) {
Data data = Data.failure("操作失败");

Map<String, String> map = new HashMap<>();
String timestamp = System.currentTimeMillis() + "";
if ("m".equals(f)) {
timestamp = "m" + timestamp;
}
map.put("timestamp", timestamp);

// 获取二维码链接
String result = HttpKit.get(env.getProperty("api.url.wx_get_qrcode") + timestamp);
if (StringUtils.isNotEmpty(result)) {
JSONObject res = JSON.parseObject(result);
map.put("ticket", res.get("ticket").toString());
}

return Data.success(map);
}


/**
 * 登录
 *
 * @param username
 * @param timestamp
 * @return
 */
@RequestMapping(value = "/wechat", method = RequestMethod.POST)
public @ResponseBody
Data wechatlogin(String username, String timestamp) throws Exception {
    Data data = Data.failure("操作失败");

    //重置序列化
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

    if (StringUtils.isBlank(username) || StringUtils.isBlank(timestamp)) {
        return data;
    }
    // 获取用户
    if (redisTemplate.opsForValue().get("wechat_" + timestamp) == null) {
        data.setMessage("获取用户失败");
        return data;
    }
   //  其他登录逻辑
  。。。。。。。

}

后台服务的websocket 实现

package com.lotres.mp.controller;

import net.sf.json.JSONObject;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 * 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 */
@Component
@ServerEndpoint("/websocket/wechat/{timestamp}")
public class WebSocketController {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
    //private static CopyOnWriteArraySet<WebSocketController> webSocketSet = new CopyOnWriteArraySet<WebSocketController>();
    private static ConcurrentMap<String, WebSocketController> webSocketMap = new ConcurrentHashMap<String, WebSocketController>();
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(@PathParam("timestamp") String timestamp, Session session) {
        this.session = session;
        webSocketMap.put(timestamp, this);
        //在线数加1
        System.out.println("唯一key为:" + timestamp);
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("timestamp") String timestamp) {
        webSocketMap.remove(timestamp);//从map中删除
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端的消息:" + message);

        JSONObject jsonobject = JSONObject.fromObject(message);
        Hashtable params = (Hashtable) JSONObject.toBean(jsonobject, Hashtable.class);

        //群发消息
        WebSocketController webSocketController = webSocketMap.get(params.get("equipmentType"));
        try {
            webSocketController.sendMessage((String) params.get("nickname"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }
}

公众号后台配置

后台服务程序参考

https://github.com/binarywang/weixin-java-mp-demo-springboot

!!!好了,微信扫码关注公众号登录基本就实现了。 本系统实现直接使用了websocket 机制, 没有使用js 轮询检测用户是否已经扫码关注的方法。 轮询方法实现就是当系统生成带参数二维码后,获取到了后台生成的timestamp参数,前端js就开始轮询(例如:每三秒请求一次)。

致谢: 前面参考的几篇文章作者。

顺便提一下,我们系统使用了开源 https://gitee.com/mtons/mblog.  在此表示感谢!

最后,希望本文对你有帮助,欢迎体验一下

https://love.daquexing.com/login

如果你还有其他疑问,可以加我微信咨询

预告:《微信扫描自定义二维码关注公众号并登录(二)移动端/微信端的设计与实现

【填个坑】微信Token验证失败原因及解决方案

微信公众平台修改服务器配置时token验证失败

网上很多都是php教程,
看这里

https://blog.csdn.net/sinat_22878395/article/details/69258165

还有下面这篇说的比较有高度

https://www.zkii.net/tech/php/813.html

我的项目用的Spring-boot 框架,使用了

班纳睿 

的全能微信Java开发工具包
https://gitee.com/binary/weixin-java-tools/tree/master

微信公众号demo,使用weixin-java-tools,基于Spring Boot

https://gitee.com/binary/weixin-java-mp-demo-springboot

其中处理微信服务器认证的方法如下,(仔细看,感觉没啥毛病)

 @GetMapping(produces = "text/plain;charset=utf-8")

  public String authGet(

      @RequestParam(name = "signature",  required = false) String signature,

      @RequestParam(name = "timestamp", required = false) String timestamp,

      @RequestParam(name = "nonce", required = false) String nonce,

      @RequestParam(name = "echostr", required = false) String echostr) {


    this.logger.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature, timestamp, nonce, echostr);

    if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
      throw new IllegalArgumentException("请求参数非法,请核实!");
    }

    if (this.wxService.checkSignature(timestamp, nonce, signature)) {
      return echostr;
    }
    return "非法请求";
  }

最好修改如下:

@GetMapping(produces = "text/plain;charset=UTF-8")
public void authGet(
    @RequestParam(name = "signature",
        required = false) String signature,
    @RequestParam(name = "timestamp",
        required = false) String timestamp,
    @RequestParam(name = "nonce", required = false) String nonce,
    @RequestParam(name = "echostr", required = false) String echostr,HttpServletResponse response ) throws IOException {
   

  this.logger.info("\n接收到来自微信服务器的认证消息:[{}, {}, {}, {}]", signature,
      timestamp, nonce, echostr);

  if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
    throw new IllegalArgumentException("请求参数非法,请核实!");
  }

  if (this.wxService.checkSignature(timestamp, nonce, signature)) {
    //------------------------------
    //处理业务完毕
    //------------------------------
    BufferedOutputStream out = new BufferedOutputStream(
            response.getOutputStream());
    out.write(echostr.getBytes());
    out.flush();
    out.close();
  } else {
    logger.info("非法请求");
  }

}

问题解决!  也许其他人 用weixin-java-tools 没有遇到这样的问题, 这里也可能跟服务器环境配置有关,具体详情暂不深挖(这个坑日后再填)。

 

GitHub交友

重点解决程序员交友、程序员恋爱、程序员相亲、程序员找对象的问题,真正开源交友。我是细心姐姐。 微信搜索关注《大确幸》,有爱有行动。http://daquexing.cn

自我介绍

南京创业团队,目前产品方向是婚恋社交;我们有公众号《大确幸》,小程序《单身相亲助手》、《大确幸日历》, 我们的愿景是让适龄的单身青年找到合适的人并快速线下见面。 为了让程序员找到合适的对象,更快的脱单,我们特别开了「GitHub交友」,为大家服务。 在这里,你可以遇到你的Mr.right 。

如何GitHub交友 ?

个人介绍模板详情 –>> 点击下载

GitHub交友安全问题

第一位女生

第一位男生

当前分区

北京

上海

广州

深圳

杭州 (1)

成都

南京 (1)

武汉

西安

郑州

厦门

苏州

天津

国内其他城市

海外

订阅

  • 服务号:大确幸 ,定期组织线下活动

    hidaquexing

  • 订阅号:细心姐姐 ,每天都有丰富的话题内容

    xixinjiejie

  • 知识星球:单身星球, 一个单身青年聚集的地方

    单身星球

赞赏是最好的支持

赞赏我们