背景

​ 这里微信扫码登录,没有采用轮询、没有采用WebSocket推送,而是由: 扫码后重定向 -> 微信服务器 -> 我方统一认证服务器 -> 指定业务服务器 -> 返回code前端处理结果执行登录。

​ 因为微信PC网站登录,回调合法域名、不管是子域名还是主域名仅能填写一个,所以如果有多个登录环境的情况下,就需要业务方有一台统一的服务来处理分发扫码请求。

​ 如需进行二维码过期、自动刷新二维码等操作,可自行实现,不在本文介绍范围内。

效果预览

image-20210926143053685

流程介绍

登录过程客户端请求情况

image-20210926151833965

从上述网络资源请求记录中可以看到,从扫码确认登陆后,至少经历了两次302(重定向)

  • 一次是由微信产生code后重定向到Js中配置的redirect_uri去并携带code及state字段。
  • 二次是由Nginx服务器判断该请求属于什么业务的,并将Host替换为指定配置服务,重定向过去。
  • 最后由业务服务器接收到请求后处理完毕,重定向到前端新的页面(登录、绑定)并在URL中携带参数供前端做后续业务处理。

前端部分

生成微信登录二维码

这里不使用跳转到微信自身的扫码授权页面,而是嵌入我们自身的登录页面,所以需要自行通过Js获取到微信的二维码数据,展示到我们的页面。

参考文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html

微信 wxLogin.js

如果有其他修改需求,可以自行修改后传入自己的云存储,或者直接使用微信文档中的访问链接。

! function (a, b, c) {
    function d(a) {
        var c = "default";
        a.self_redirect === !0 ? c = "true" : a.self_redirect === !1 && (c = "false");
        var d = b.createElement("iframe"),
            e = "https://open.weixin.qq.com/connect/qrconnect?appid=" + a.appid + "&scope=" + a.scope +
            "&redirect_uri=" + a.redirect_uri + "&state=" + a.state + "&login_type=jssdk&self_redirect=" + c +
            '&styletype=' + (a.styletype || '') + '&sizetype=' + (a.sizetype || '') + '&bgcolor=' + (a.bgcolor || '') +
            '&rst=' + (a.rst || '');
        e += a.style ? "&style=" + a.style : "", e += a.href ? "&href=" + a.href : "", d.src = e, d.frameBorder = "0",
            d.allowTransparency = "true", d.scrolling = "no", d.width = "160px", d.height = "160px";
        var f = b.getElementById(a.id);
        f.innerHTML = "", f.appendChild(d)
    }
    a.WxLogin = d
}(window, document);
创建方法
/** 生成微信登录二维码 */
createWeChat() {
  const s = document.createElement("script");
  s.type = "text/javascript";
  // 微信提供的访问地址 或 自定义地址
  s.src = "https://h5.xxxxxx.com/h5/java-script/wxLogin.js";
  const wxElement = document.body.appendChild(s);
  wxElement.onload = function () {
    // 扫描二维码后重定向地址(这里重定向地址中的/test/可根据不同的环境进行不同的配置)
    const redirectUri = `http://auth.xxxxxx.com/test/callback/wx/login`
    // 微信登录实例JS对象
    const object = new WxLogin({
      self_redirect: false,
      // 页面中对应展示二维码的容器ID
      id: "wxMaContainer",
      appid: "xxxxxxxxxx",
      scope: "snsapi_login",
      redirect_uri: encodeURIComponent(redirectUri),
      // 若在业务中需要有多个判定参数 可以通过拼接的方式放在 state 中
      state: "login,0",
      style: "black",
      // 二维码样式及其它(关于样式自定义可在网上查阅到相关的实现)
      href: "data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDE0MHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7ZGlzcGxheTogbm9uZTt9DQouc3RhdHVzX2ljb24ge2Rpc3BsYXk6IG5vbmV9DQouaW1wb3dlckJveCAuc3RhdHVzIHt0ZXh0LWFsaWduOiBjZW50ZXI7fQ=="
    });
  };
},
  • 关于重定向地址:当创建微信二维码时,前端会将上面的参数全部传给微信服务器,用户扫码确认后,会重定向到设置的redirectUri中,并且携带code参数,

后端部分

认证服务器Nginx配置
server {
    listen 80;
    server_name auth.xxxxxx.com;
    # 根据Host后的Path匹配到Test重定向到如下服务中
    location /test/ {
        rewrite ^/(.*) http://test.xxxxxx.com/$1 redirect;
    }
    location /prod/ {
        rewrite ^/(.*) http://creator.xxxxxx.com/$1 redirect;
    }
}
重定向到Java程序处理

仅展示主要逻辑代码,其他不予展示。

/**
 * 微信登陆授权回调
 *
 * @author zhengshangjin
 * created on 2021-03-31
 */
@GetMapping(value = "/callback/wx/login")
public void wxLogin(HttpServletRequest request, HttpServletResponse response) {
    oauthService.handleWxWebBack(request, response);
}

/**
 * 处理微信回调业务情况
 *
 * @param request  请求
 * @param response 响应
 * @author zhengshangjin
 * created on 2021-04-01
 */
@Override
public void handleWxWebBack(HttpServletRequest request, HttpServletResponse response) {
    String code = request.getParameter("code");
    String state = request.getParameter("state");
    log.info("handleWxWebBack code:{}, state:{}", code, state);
  
    if (StringUtils.isEmpty(code) || StringUtils.isEmpty(state)) {
        throw new BaseException("微信回调参数错误");
    }
  
    // 若在业务中需要有多个判定参数 可以通过拼接的方式放在 state 中
    String[] stateArr = state.split(",");
    if (stateArr.length < 2) {
        throw new BaseException("微信回调参数错误");
    }
    String type = stateArr[0];
    String userId = stateArr[1];
    log.info("handleWxWebBack type:{}, userId:{}", type, userId);
  
    // 绑定
    if (WX_BIND.equals(type)) {
        // 绑定状态
        int status = handleWxBind(code, userId);
        log.info("status:{}", status);
        try {
            // 重定向到前端绑定页面处理结果
            response.sendRedirect(wxBindNotify + "?status=" + status);
        } catch (IOException e) {
            log.error("ex msg:{}", e.getMessage());
        }
    }
    // 登录
    if (WX_LOGIN.equals(type)) {
        try {
            // 重定向到前端登录页面处理结果
            response.sendRedirect(wxLoginNotify + "?code=" + code);
        } catch (IOException e) {
            log.error("ex msg:{}", e.getMessage());
        }
    }
}

至此,就完成了登录或者绑定微信的逻辑处理,

Logo

前往低代码交流专区

更多推荐