微信授权问题复盘

最近做的项目中需要使用到微信支付,从而涉及到拉取微信授权,因为测试环境域名切换的问题,导致测试环境上的微信支付是不可用的,想着之前生产环境一直是ok的,于是抱着这种心理,直接发布生产环境做验证,结果就悲剧了······

下面着重讲解一下这次事故的整个解决过程和思路。

【bug现象描述】

扫描二维码进入程序首页,此时会拉起用户微信授权,以获取用户信息,正常情况下,新用户会弹框请求同意授权,然后成功获取授权。而现状是获取授权失败,导致页面不断重复闪屏刷新去调用授权接口。

 

【问题定位过程】

  • 初次定位:

因为近期公司刚做过域名切换,现在处于新旧两个域名同时使用的阶段,第一反应是不是公众号后台配置的域名地址有问题,于是去检查公众号后台的配置,发现确实没有新域名。配置上新域名后再次测试,发现问题还是存在,说明引起问题的原因与域名配置无关。

  • 再探究竟:

通过查看服务器日志记录,发现导致页面重复闪屏刷新原因是因为后台没有获取到HttpServletRequest中的cookie,该cookie中保存的信息为用户信息token。以该cookie为切入点反向推进,发现cookie的写入点是在某个filter中,然后查看该filter打印的关键日志,未果。

  • 详细探究:

  1. 因为该系统filter众多,而且由于历史原因,导致其中绝大部分的filter日志关键字全部是一样的,完全没法区分当前接口到底进的是哪个filter,于是对日志关键字做了下调整,加上了filter的类名用作区分;
  2. 再次测试时发现某台服务器上调用微信报了连接超时,经排查发现这台机器是后面扩容时新增加的机器,未开通外网访问权限,调整安全组后访问外网正常。同时由于做了负载均衡,多台服务器查看日志不便,在这里将流量全部调整到一台机器上。
  3. 接着测试时候,找到了该filter的入口日志,但逻辑处理部分的代码未能执行,继续深挖原因。阅读祖传代码,发现这个filter里面有个校验URI是否需要拦截的方法,如下:
/**
 * 验证URI是否需要拦截
 * @param request
 * @return
 */
public static Boolean verifyUri(HttpServletRequest request) {
    String uri = request.getRequestURI();
    String uris = PropertiesUtil.getPropertyValues("wechat.filter_uri_list", "");
    if (uris.indexOf(uri + ",") != -1) {
        return true;
    }
    return false;
}

这里使用了一个配置项,读取配置项里的内容得到一个uris的字符串,然后取当前调用的uri进行比对,如果该uri存在于uris中,则调用微信api获取网页授权access_token;如果该uri不在uris中,则filter直接放行。

乍一看,这逻辑没毛病啊,然后我去看了一下生产环境配置中心该配置项的值,哦豁,豁然开朗茅塞顿开醍醐灌顶······原来都是(uris.indexOf(uri + ",") != -1)这个骚操作在搞事情!加上这个","之后,那么要求你的uris这个字符串的格式必须是"/a/b/c,/a/b/d,/a/b/e,"这种,而配置中心上配置的却是"/a/b/c,/a/b/d,/a/b/e"······

    4.改配置项,再次测试,正常拉起授权,微信支付ok。

【反思总结】

  1. 排查问题时要由表象倒推深层次根本原因,及时查看日志;
  2. 平常编码时一定要注意编码规范,不同接口日志关键字要差异化,方便后续调试和排查生产问题;
  3. 服务器扩容时要注意网络访问权限和文件访问权限等问题;
  4. 校验配置项中是否包含某段字符串,最好是使用转化为list然后再进行比较的这种方式,一一举例说明优缺点,如下:

方法一:

private static boolean verifyUri1(String uri) {
    String uris = "/a/b/c,/a/b/d,/a/b/e";
    if (StringUtils.contains(uris, uri)) {
        return true;
    }
    return false;
}

优点:编码简单,几乎不需要任何思考

缺点:方法粗暴,如果有类似于/a/b这样的接口出现,会被误杀

 

方法二:

private static boolean verifyUri2(String uri) {
    String uris = "/a/b/c,/a/b/d,/a/b/e,";
    if (uris.indexOf(uri + ",") != -1) {
        return true;
    }
    return false;
}

优点:解决了方法一中可能误判的问题

缺点:不能区分大小写(自行补充contains方法和indexOf方法的区别),配置项看着有点奇怪,不能一目了然的理解(拼接逗号的问题)

 

方法三(推荐):

private static boolean verifyUri3(String uri) {
    String uris = "/a/b/c,/a/b/d,/a/b/e";
    String[] array = uris.split(",");
    List<String> list = new ArrayList<>(array.length);
    Collections.addAll(list, array);
    for (String s : list) {
        if (StringUtils.equals(uri, s)) {
            return true;
        }
    }
    return false;
}

优点:代码结构清晰易懂,不存在方法一中的问题

缺点:大批量数据处理时效率较低(字符串——>数组——>List——>for循环)

 

【延伸拓展】

经过此次排查问题的过程,把整个对接微信授权的流程又梳理了一遍,大致流程如下(详细对接方案参考微信开放文档):

  1. 第一步:用户同意授权,获取code
  2. 第二步:通过code换取网页授权access_token
  3. 第三步:刷新access_token(如果需要)
  4. 第四步:拉取用户信息(需scope为 snsapi_userinfo)
  5. 附:检验授权凭证(access_token)是否有效

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值