Vue微信公众号开发踩坑记录

88

需求

  • 微信授权登录(基于公众号的登录方案)
  • 接入JS-SDK实现图片上传,分享等功能

现状及难点

  • 采用的Vue框架,前后端分离模式(vue工程仅作为客户端),用户通过域名访问的是客户端,但是微信授权中涉及签名和token校验依赖服务端
  • JS-SDK需要向服务端获取签名,且获取签名中需要的参数包括所在页面的url,但由于单页应用的路由特殊,其中涉及到iOS和android微信客户端浏览器内核的差异性导致的兼容问题

解决方案

授权登录

本人将授权流程设计如下:
此处输入图片的描述
详细说明:

  1. 用户访问网站主域名
  2. vue客户端(domain/)接收请求,在路由解析前判断用户是否登录(比如检查cookie);
  3. 如果没有登录,则通过api获取微信授权地址,获取后跳转到微信授权页面;
  4. 用户确认授权,微信服务器发起回调请求,这时回调到服务器端(domain/api/xxx)
  5. 服务器端保存用户信息,进行注册登录操作(记录cookie),重定向到vue客户端(domain/)
  6. 重复第一步,授权登录成功

踩坑记录:
以下是另一个授权方案
此处输入图片的描述
其实如果只实现授权登录到话,这个方案是可以的,而且也很清晰,vue客户端单方面在服务器和微信服务器之间进行通信,微信服务器不能直接和服务器通信。这种方案的坑在于当微信授权回调时会携带一个code参数,该参数会污染vue路由导致ios上进行JS-SDK签名时失败(后续会具体描述这个问题)

JS-SDK签名

对于签名,官方是这么说的

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用

vue中路由有history和hash两种模式;在history模式下,理想的设计方案是,当进入到需要用到JS-SDK组件时,获取以下当前url(也就是通过 location.href.split(‘#’)[0]获得到的地址)传递到服务端进行签名,应该就没问题了,但是IOS获取的url并不是调用微信js的时候所在页面的地址,而是进入到网站第一个页面的地址。

网上查询到一个方案是针对ios设备进入页面时先将当前url记录下来,到授权页面时将记录的url传递给服务端进行签名。该方案经实践是可行的,妈妈再也不用担心我的网址很丑很难看啦。

另外一个方案就是使用hash模式,这种模式下,url永远都只是主域名地址,省去了传递url的烦恼,也没必要处理兼容,所以如果不建议路由中有#的话,该方案应该是首选方案。

这里还有一个深坑,那就是如果授权方案采用了上述中的vue客户端处理回调的方式,那么ios将永远无法签名成功,为什么呢,因为这种方案路由通常是这样子的:

http://domain.com/?code=xxxxxx&stat=#/xxx

这种路由中带了参数的url是没法签名校验成功的!
这种路由中带了参数的url是没法签名校验成功的!
这种路由中带了参数的url是没法签名校验成功的!
重要的事情得说三遍啊
在我另外一篇文章中对js-sdk签名做了更多的介绍,可以移步到vue微信公众号开发踩坑记录(2)

Coding

任何不上代码的吹逼都是耍流氓,这里笔者分享下在vue中具体怎么coding的。

微信授权登录

笔者在项目中使用的vue-router进行路由控制,使用了vuex记录用户登录信息,但是由于vuex中存储的内容在页面刷新后会丢失,所以服务端同时也写了用户登录状态到cookie中,vue中需要通过这两个条件进行登录判断,不多BB,直接看代码吧

// ... other code

router.beforeEach((to, from, next) => {
  if ((!VueCookie.get('user') && !store.state.userInfo)) {
    // 第一次访问
    console.log('授权登录')
    // 跳转到微信授权页面,微信授权地址通过服务端获得
    axios.post('/api/login').then(res => {
      var data = res.data
      if (data.code === 100) {
        window.location.href = data.data
      }
    })
  } else if (!store.state.userInfo) {
    // 刷新页面,获取数据存入vuex
    axios.get('/api/currentuser').then(res => {
      if (res.data.code === 100) {
        store.dispatch('setUserInfo', res.data.data)
        next()
      }
    })
    console.log('cookie生效期内登录')
    next()
  } else {
    // 已经登录
    console.log('已登录')
    next()
  }
})

//... other code

history模式下的JS-SDK签名

在入口文件中将当前url存入vuex

// ... other code
router.beforeEach((to, from, next) => {
  document.title = to.meta.title
  // 处理jssdk签名,兼容history模式
  if (!store.state.url) {
    store.commit('setUrl', document.URL)
  }
  // ... other code

在需要获取签名的组件中获取并进行配置

// ... other code
created () {
      var sef = this
      var url = ''
      // 判断是否是ios微信浏览器
      if (window.__wxjs_is_wkwebview === true) {
        url = this.$store.state.url.split('#')[0]
      } else {
        url = window.location.href.split('#')[0]
      }
      this.$http.get('/api/jssdk?url=' + url).then(function (res) {
        sef.lists = res.data.data
        hmTools.wechact(sef.lists, sef) //js-sdk配置
      })
    }
// ...other code

结语

由于本人文笔一般,思维的表达估计不到位,见解也是浅尝辄止,所以如果看官您有疑惑的地方或者有歧义欢迎来和本人交流。为了方便大家互相沟通,本人也创建了一个vue公众号开发的qq群,欢迎加入和大家一起分享开发心得,qq群号:130903919

你可能感兴趣的

74 条评论
fantasy525 · 2017年09月07日

就是我想问服务端返回到vue客户端后怎么知道已经授权过了?最后一步的时候?还有服务端返回到客户端时我想返回到前端路由离开去授权的那个路由页面怎么做?服务端能重定向到前端路由的某个页面?

+3 回复

0

第一种方法怎么重定向回vue 客户端啊? 本地开发 192.168.xx 啊怎么跳回来呢?

一蓑烟雨 · 9月13日
hsyoml · 2017年10月12日

你好,关于微信网页授权 我有几部不大明白的地方 希望能帮我解答下,谢谢.

  1. !VueCookie.get("user") 这一步 这个是自己封装 存储 cookie 的方法?
  2. 如果1 没错 ,检测到 !VueCookie.get('user') && !store.state.userInfo 去post 请求 /api/login => window.location.href = data.data 跳转到 微信授权页面 => 到这里有个问题是, [用户点击同意授权 ],后端可以检测到这一行为?然后重定向前端页面之前url? 同时携带cookie用做换取用户信息?
  3. 2 成立的话, 这个cookie是在前端存储字段还是默认携带让后台去判断? 这个cookie 的过期时间前后端如何去控制,?

+2 回复

0

邮件已回复

imwty 作者 · 2017年10月13日
0

@hsyoml 我也遇到你提的这些问题了,能给我说说楼主的答案吗

jasonzuo · 2017年10月31日
0

@imwty 有收,谢谢解答

hsyoml · 2017年11月01日
其实孤独 · 2017年11月19日

大神,别人公众号内嵌我的H5页面,安卓分享的页面是当前页面,ios分享出去的是应用的首页,怎么破

+2 回复

0

@其实孤独 最好是路由使用hash模式

imwty 作者 · 2017年11月20日
0

我是用的hash模式,安卓微信分享是好的,别的浏览器还有Safari分享也是好的,就ios微信分享的都是首页,只能通过自己内部代码处理,网上也有一些这种问题,但是都没解决,所以想问下,没有思绪

其实孤独 · 2017年11月20日
0

@其实孤独 这个是ios微信的浏览器机制,记录的状态就是进入位置的地址,spa暴力方法就是需要分享的地址强制刷新,把当前地址变成浏览器的入口地址。

南心之 · 3月3日
tanghong668 · 5月28日

经过爬坑后总结了一下 history模式下微信授权和分享https://github.com/qutz/vue-mall

+1 回复

0

有服务器后端的代码分享吗

爱羽客 · 6月7日
fantasy525 · 2017年09月04日

大神,有没有邮件?想问个自己的坑

回复

0

@fantasy525 1045810154@qq.com,也可以加我qq

imwty 作者 · 2017年09月07日
fantasy525 · 2017年09月05日

第四步怎么保证返回离开时的页面呢?

回复

Chosenwu · 2017年11月03日

你好,在路由拦截那里 if ((!VueCookie.get('user') && !store.state.userInfo)) 这里用 !VueCookie.get('user') 是不是和普通登录一样判断当前客户端是否已经存在token 是一个道理?

回复

0

是的

imwty 作者 · 2017年11月04日
0

好的,谢谢

Chosenwu · 2017年11月04日
EvanWu · 2017年11月28日

你那个正确的授权登录,最后一步,我想知道,怎么把token传给vue客户端?

回复

0

token直接被api服务器(也就是后台)接收了,后台从微信那边取到用户信息,并且进行登录操作,然后重定向(redirect)到vue工程的地址就行了

imwty 作者 · 2017年11月28日
0

redirect怎么把token给vue?

EvanWu · 2017年12月04日
0

兄弟你最后怎么解决的@EvanWu是最后重定向的时候用cookie传的吗

EvanWu · 2017年11月28日

而刚好我用的就是不正确的那种流程,这样可以通过获取code来ajax到后台登录。

回复

0

你有接微信分享什么的吗,你在使用js-sdk的时候会签名不成功的

imwty 作者 · 2017年12月05日
0

我也是用的不正确的,暂时还没用到vue的项目中,分享什么的都没问题啊。不明白第四步中后端重定向是怎么回调到客户端的。像我第一步 地址我写死在本地的,第二三步后面本地服务器就不知道重定向去哪了。。

0

@苍山暮色烟雨迟 当你同意授权后,微信那边会访问你传入的参数redirect_uri所对应的地址,我设置的是服务端地址也就是一个接口地址,然后在接口中进行redirect到vue主页对应的url上来

imwty 作者 · 1月4日
管大侠 · 2017年12月11日

我调用jssdk分享的时候,分享成功了,但是分享出去的页面链接不是我分享时配置的链接,如我分享时配置的链接为: http://www.abc.com/#/releaseBuy, 但是分享完之后的链接就变成了:http://www.abc.com 这个地址了,我想问一下应该怎么样才能正确的分享呢?

回复

0

要接分享且分享指定页面,需要路由用history模式,你可以看下我的另外一篇讲分享的博客

imwty 作者 · 2017年12月12日
Sunshine_Rose_ · 2017年12月26日

你好!最近遇到一些问题,关于微信公众号分享功能(history模式下,SPA),在安卓上是没有问题的,在ios上除了首页的分享不行,其他页面都是可以的,但是在首页手动刷新一下,分享功能就可以了,怀疑是微信签名问题,但是首页的扫一扫功能又是可以的,具体签名代码如下:

router.afterEach((to, from) => {
  // 非ios设备
  if (/(Android)/i.test(window.navigator.userAgent)) {
    store.commit('change_url', `${window.location.origin}${to.fullPath}`)
    // 获取签名
    Common.getSign()
    // 如果签名失败重新签名
    Common.reGetSign()
  } else if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
     store.commit('change_url', `${document.URL.split('#')[0]}`)
    // 获取签名
    Common.getSign()
   // 如果签名失败重新签名
    Common.reGetSign()
  }
})

想问问您有遇到过这个问题吗?有好的解决方法吗?

回复

0

将公众号菜单上的链接写成http://xxx.com/index.html或者...://xxx.com/index试试

变幻不同的我 · 1月9日
0

@Sunshine_Rose_[Sunshine_Rose_] 强制刷新就好

南心之 · 3月3日
0

history模式下,后端要如何写?
那后端如何配合?你访问的http://www.xxx.com/login, /desprition, 等地址时是访问后端的,这些地址需要在后端写?nginx全部地址转发?

Samdyddd · 8月20日
P了个F · 2月28日

我也是这个问题。ios中有问题

回复

Kimstroy · 3月8日

我的就是http://domain.com/?code=xxxxx...,这么做到能签名成功

回复

0

搞定了吗?、

一蓑烟雨 · 9月13日
xzf_fault · 3月27日

您好,想请教您一下:在微信公众号内,点击底部一个按钮,这个按钮的链接的地址中,回调地址是php文件,我的php获取到code后,怎么再跳回到vue的页面啊,按开发的时候,我进入项目先是进入vue项目的首页,不是通过php跳到这个首页的,怎么能进入首页的时候,可以让php文件也去获取code呢,麻烦您了

回复

0

@xzf_fault 你就redirect到前端首页呗

imwty 作者 · 4月10日
悠然见南山 · 4月10日

@imwty 想问下作者,前后端分离你是如何做到同域开发的

回复

0

@悠然见南山 nginx代理呀

imwty 作者 · 4月10日
0

@imwty nginx是架在本地还是服务端呀,也就是说通过nginx才把cookies带过来的?

悠然见南山 · 4月11日
0

@悠然见南山 服务端,本地用webpack

imwty 作者 · 5月11日
爱羽客 · 6月6日

有测试微信号吗?

回复

三顺_黄 · 8月1日

想问一下,我是history,但是我在ios也是提交当前url才能签名正确,但是如果是用第一次进入的页面url,就会签名失败。

回复

Samdyddd · 8月20日

那后端如何配合?你访问的http://www.xxx.com/login, /desprition, 等地址时是访问后端的,这些地址需要在后端写?nginx全部地址转发?

回复

一蓑烟雨 · 9月12日

大佬啊,第四步采用第一种方案,第四步怎么跳回客户端啊?vue-cli 自动生成 npm run dev 跑起来是192.168.X.XX 服务的怎么跳到这个上面来啊

回复

陈勖 · 11月19日

感谢老哥!

回复

载入中...