聊一聊小程序第三方登录

     微信小程序在一般情况下,是免登录的,秉承了小龙的用完即走的理念。除非我们需要获取用户的信息,如用户微信昵称头像地理位置 等,需要我们获取用户的一些信息。微信官方提供了相关的API

  但是在某些特殊的运用中,仅仅只获取登录用户的信息是远远不够的。一般稍微有点复杂商业逻辑的小程序都会有自身的用户体系,并且前后端通信时需要对用户进行认证或者权限的判断。小程序必须要经过微信账户的验证授权,然后再与第三方服务器(也就是我们自己的服务器)通信实现用户的登录。这里面就涉及到微信账户信息与第三方用户信息的耦合

登录原理

  首先,我们总结一下其实登录就是要保证2个问题:

  • 登录状态保存

  • 安全验证

  登录状态保存 就是登录成功后请求站内数据接口时无需再次登录,客户端与服务器按照既定的规则进行用户有效性验证。

  安全验证 是为了应对流量劫持,防止中间人攻击,在我们的页面中插入乱七八糟的内容。https可以应对绝大部分的应用场景,有效的防止流量劫持,所以微信小程序的前后端通信微信官方强制大家使用https,也是基于这方面的思考。但道高一尺,魔高一丈,程序猿的智慧是无穷的。市场上层出不穷的黑科技软件,如 Fiddle , Charles2017 年初风靡的刷跳一跳的得分,就是靠着这些工具获取到用户的隐私信息的。其实就是利用了工具作为中间人,抓取了隐秘数据。话说,我们可以利用这些工具破解小程序源码的。这个我可以再写一篇文章和大家分享分享。

  好了,明白了上面的个概念。我们回顾一下传统基于浏览器的应用是如何解决这个问题的。浏览器环境下的登录状态保存通常使用cookie实现,这种方案的实现原理是浏览器发出的http请求的header中会携带客户端的cookie。如下图:
 

  自定义安全验证 通常的方案是客户端与服务器约定好一个验证签名,客户端在发出http请求之前按照约定好的算法计算出一个签名字符串,并且在http请求中将计算签名的参数传递给服务器。服务器接收到请求之后,解析出签名字符串和客户端传递的计算参数,然后按照同样的算法计算出签名字符串,并将其与客户端的签名进行对比,完全一致则验证通过,否则返回验证未通过的数据。如下图:
  

  我们来看一个例子,先简单点,就不加上图中加密算法之类的。我们直接生成令牌,作为通信的签名,来,我们一起撸一遍:

  @GetMapping("/login")    public ModelAndView login(HttpServletRequest request,
                                 HttpServletResponse response,
                              Map<String, Object> map) {        //1. 验证,获取用户的登录信息     ...do something        //2. 生成用户登录状态的token,存储至分布式存储系统,如Redis
        String token = UUID.randomUUID().toString();
        Integer expire = RedisConstant.EXPIRE;

        redisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_PREFIX, token), openid, expire, TimeUnit.SECONDS);        //3. 设置token至cookie,返回给用户
        CookieUtil.set(response, CookieConstant.TOKEN, token, expire);        return new ModelAndView("redirect:" + projectUrlConfig.getSell() + "/sell/seller/order/list");

    }复制代码

  通过上一轮的操作,我们就在服务器端生成了用户登录认证信息,并将此信息保存到了客户端的cookie中。之后我们就可以通过这个认证信息在服务端进行判断,来,翠花,上代码:

  @Before("verify()")    public void doVerify() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();        //1.查询cookie
        Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);        if (cookie == null) {
            log.warn("【登录校验】Cookie中查不到token");            throw new SellerAuthorizeException();
        }        //2.去分布式存储系统第二次验证
        String tokenValue = redisTemplate.opsForValue().get(String.format(RedisConstant.TOKEN_PREFIX, cookie.getValue()));        if (StringUtils.isEmpty(tokenValue)) {
            log.warn("【登录校验】Redis中查不到token");            throw new SellerAuthorizeException();
        }复制代码

  这样,运用面向切面编程或者在filter在之后的用户请求中,我们就可以判断这是否是当前真实用户的真实请求。如此,我们可以过滤掉恶意攻击和一些爬虫啦。

  通过上面的实例,我们了解了基于浏览器应用的用户登录机制原理。上述流程成立的前提条件是:

  • 浏览器可以获取到UA信息

  • 浏览器发出的http请求header中会携带UA信息,服务器可以获取

  那么,我们是否可以将这套机制复用到小程序开发中去呢?很不幸,答案是否定的。因为,小程序存在以下限制

  • 不支持cookie,所以使用cookie储存登录状态的方案不可行;

  • http请求header不携带设备信息,服务器无法获取。

  但是当我吐槽小程序重重限制的同时又有一个好消息:http请求可以自定义header

  登录识别信息在无法使用cookie传递的限制下我们有两种传输途径:

  • url query

  • http header

  其实使用任何一种途径都可以完成需求,但是url query(通常称为data)的语义应该是与接口功能紧密相关的数据,并且http请求的headerurl query数据更保密,所以我最终采用header传递验证信息的方案。

登录实现方案

  确定好了技术解决方案,小程序平台下实现自己的用户登录仍然有很多细节需要琢磨。我们首先看一下官方文档给出的第三方登录流程图

  官方文档给出的流程是实现第三方登录的基本流程,但是具体的登录功能中仍然有一些细节上的不同,比如:

  • 手机验证码登录;

  • 3rd_sessionopenId不能明文暴露给客户端,需要进行加密;

  • 登录状态保存的有效期;

  • 用户登录的服务器与基础服务的服务器并不是同一台。

   在小程序登录机制的基础上,最终制定了如下方案:

 对比微信官方的登录流程图,有以下几个细节

  • 3rd_sessionopenId不直接暴露给客户端,而是通过可逆的加密算法进行加密后,组合成token暴露给客户端;

  • 第一次请求基础功能服务器时并不验证签名,此次请求的目的是从微信服务器获取3rd_sessionopenId并且加密后返回客户端,以便后续请求使用;

  • 第二次请求的目标是用户服务器,携带tokensign。用户服务器首先会进行签名验证手机验证码校验;

  • 验证通过后解析token获取3rd_sessionopenId,然后与uid结合重新计算token。最后将uidtoken一并返回给客户端。

  • 用户登录成功后,客户端将uidtoken储存在本地,以便后续请求数据接口使用。

  • 客户端储存tokenuid的方式可以用storage或者app.globalData

总结

 想在微信小程序中实现自己的用户体系需要花费一些力气,每个公司都有自身的用户登录验证体系,同时还要考虑安全性状态保存问题。在小程序不支持cookiehttp请求header不携带设备信息等限制下,实现登录功能的各种细节都需要采用一些折中的手段。而且往往这些方案显得有些臃肿并且难以维护,非常考验开发者的抽象能力


愿意与大家分享交流各种技术,个人公众账号[mindev],以及知识星球[极客世界]


                                       欢迎订阅公众账号,日更哟~~~


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值