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

JillianHlf 7年前
   <h2>需求</h2>    <ul>     <li>微信授权登录(基于公众号的登录方案)</li>     <li>接入JS-SDK实现图片上传,分享等功能</li>    </ul>    <h2>现状及难点</h2>    <ul>     <li>采用的Vue框架,前后端分离模式(vue工程仅作为客户端),用户通过域名访问的是客户端,但是微信授权中涉及签名和token校验依赖服务端</li>     <li>JS-SDK需要向服务端获取签名,且获取签名中需要的参数包括所在页面的url,但由于单页应用的路由特殊,其中涉及到IOS和android微信客户端浏览器内核的差异性导致的兼容问题</li>    </ul>    <h2>解决方案</h2>    <h3>授权登录</h3>    <p>授权流程如下:</p>    <p><img src="https://simg.open-open.com/show/bf027cf9667b9263627be013704a71ff.jpg"></p>    <p>详细说明:</p>    <ol>     <li>用户访问网站主域名</li>     <li>vue客户端(domain/)接收请求,在路由解析前判断用户是否登录(比如检查cookie);</li>     <li>如果没有登录,则通过api获取微信授权地址,获取后跳转到微信服务端授权页面;</li>     <li>用户确认授权,微信服务器发起回调请求,这时需要回调到服务器端(domain/api/xxx)</li>     <li>服务器端保存用户信息,进行注册登录操作(记录cookie),重定向到vue客户端(domain/)</li>     <li>重复第一步,授权登录成功</li>    </ol>    <p>踩坑记录:</p>    <p>以下是一个错误授权方案</p>    <p><img src="https://simg.open-open.com/show/03074dab793703650580095a75228b85.jpg"></p>    <p>这个方案为什么错误呢?其实如果只实现授权登录到话,这个方案是可以的,而且也很清晰,vue客户端单方面在服务器和微信服务器之间进行通信,微信服务器不能直接和服务器通信。这种方案的坑在于当微信授权回调时会携带一个code参数,该参数会污染vue路由导致ios上进行JS-SDK签名时失败(后续会具体描述这个问题)</p>    <h3>JS-SDK签名</h3>    <p>对于签名,官方是这么说的</p>    <p>所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用</p>    <p>vue中路由有history和hash两种模式;在history模式下,理想的设计方案是,当进入到需要用到JS-SDK组件时,获取以下当前url(也就是通过 location.href.split(‘#’)[0]获得到的地址)传递到服务端进行签名,应该就没问题了,但是 IOS获取的url并不是调用微信js的时候所在页面的地址,而是进入到网站第一个页面的地址。</p>    <p>网上查询到一个方案是针对ios设备进入页面时先将当前url记录下来,到授权页面时将记录的url传递给服务端进行签名。该方案经实践是可行的,妈妈再也不用担心我的网址很丑很难看啦。</p>    <p>另外一个方案就是使用hash模式,这种模式下,url永远都只是主域名地址,省去了传递url的烦恼,也没必要处理兼容,所以如果不建议路由中有 # 的话,该方案应该是首选方案。</p>    <p>这里还有一个深坑,那就是如果授权方案采用了上述中的vue客户端处理回调的方式,那么ios将永远无法签名成功,为什么呢,因为这种方案路由通常是这样子的:</p>    <pre>  <code class="language-javascript">http://domain.com/?code=xxxxxx&stat=#/xxx</code></pre>    <p>这种路由中带了参数的url是没法签名校验成功的!</p>    <p>这种路由中带了参数的url是没法签名校验成功的!</p>    <p>这种路由中带了参数的url是没法签名校验成功的!</p>    <p>重要的事情得说三遍啊</p>    <h2>Coding</h2>    <p>任何不上代码的吹逼都是耍流氓,这里笔者分享下在vue中具体怎么coding的。</p>    <h3>微信授权登录</h3>    <p>笔者在项目中使用的vue-router进行路由控制,使用了vuex记录用户登录信息,但是由于vuex中存储的内容在页面刷新后会丢失,所以服务端同时也写了用户登录状态到cookie中,vue中需要通过这两个条件进行登录判断,不多BB,直接看代码吧</p>    <pre>  <code class="language-javascript">// ... 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</code></pre>    <h3>history模式下的JS-SDK签名</h3>    <p>在入口文件中将当前url存入vuex</p>    <pre>  <code class="language-javascript">// ... 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</code></pre>    <p>在需要获取签名的组件中获取并进行配置</p>    <pre>  <code class="language-javascript">// ... 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</code></pre>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000010753247</p>    <p> </p>