背景
项目基于angular1.3开发
web服务器用node,提供微信授权和跨域
使用微信jssdk获取经纬度,再利用百度地图API获取用户所在城市
最近在开发一个商城项目,基于angular1.3(算是比较古老的东西,其实用vue或zepto也差不多)。框架啥的都不重要,业务也是没啥意外的写完了,然后上线。 上线之后,发现了一些坑,虽然不影响使用,但是作为一个爱折腾的程序猿,我还是决定该优化优化。
待优化问题:
项目的微信授权基于老的项目,写的有点不够优雅;
自己开发时把授权信息存到了sessionStorage里面,导致每次进入项目都要走一遍授权,浪费时间和影响用户体验;
解决方案:
重写授权,让项目初始化时只请求一个接口,就可以拿到用户的基本信息;将获取的用户基本信息存在cookie里面,并设置一个比较长的过期时间,以便用户在某一段时间内访问可以不用再次授权。然后我就开始写了(这里犯了一个错误,新的内容没有在新的分支或者自己的分支上开发,导致了一些尴尬的问题),拿微信授权的相关代码就贴在下面了.
router.js /** * router */ const express = require("express"); const router = express.Router(); const wxAuth = require("../wxAuth.js"); router.get("/auth",wxAuth.getCode,(req,res,next) => { // 授权调用 }); router.get("/wxAuth/getUserInfo",wxAuth.getAccessToken,wxAuth.getUserInfo,next) => { let back_url = req.query.back_url; console.log('back_url=='+back_url); if (back_url.indexOf('?path=')) { back_url = back_url.replace('?path=','#/') console.log(back_url); } res.redirect(back_url); }); module.exports = router; 授权中间件 wxAuth.js //微信公众号的appId和appSecret配置文件 const weixin = require("../weixin.config.js") const request = require('request') const appId = weixin.appID; const appSecret = weixin.appsecret; const Host = 'http://example.com' exports.getCode = (req,next) => { if (req.cookies && req.cookies.openid) { next(); } else { console.log(req) let back_url = escape(req.query.back_url); console.log(req.query.back_url) let redirect_url = `http://mall.yizhenjia.com/wxAuth/getUserInfo?back_url=${back_url}`; let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_url}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect `; console.log(url); res.redirect(url); } } exports.getAccessToken = (req,next) => { console.log('====accessToken') // console.log(req.query) let code = req.query.code; let url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${appSecret}&code=${code}&grant_type=authorization_code `; request(url,(error,response,body) => { let result = JSON.parse(body); console.log(result) req.access_token = result.access_token; req.openid = result.openid; next(); }); } exports.getUserInfo = (req,next) => { console.log('====getUserInfo') let access_token = req.access_token; let openid = req.openid; let url = `https://api.weixin.qq.com/sns/userinfo?access_token=${access_token}&openid=${openid}&lang=zh_CN ` request(url,body) => { console.log(body) let result = JSON.parse(body); res.cookie("openid",result.openid,{ maxAge: 24 * 60 * 60 * 1000,httpOnly: false }); res.cookie("unionid",result.unionid,httpOnly: false }); res.cookie("nickname",result.nickname,httpOnly: false }); res.cookie("headimgurl",result.headimgurl,httpOnly: false }); next(); }); }
在angular项目启动的时候,就判断有没有用户union的cookie存在,如果不存在就去授权,并阻止视图渲染
获取授权,因为微信授权的时候,会忽略angular路由的哈希值后面的内容,所以把哈希值做了转换,在服务端的router.js里面,又把哈希值还原,在重定向的时候写在url里
//放在commonUtil服务里面 转换url,将路由参数传给服务器,然后在授权结束后,在重定向的url里获取路由的哈希值 self.getAuth = function() { var hash = window.location.hash.replace('#/','?path='); var origin = window.location.origin; var pathname = window.location.pathname; var bcakUrl = origin + pathname + hash; window.location.href = "/auth?back_url=" + bcakUrl; } 判断授权,没有授权情况下阻止默认渲染,并请求授权 $rootScope.$on('$stateChangeStart',function(event) { if (!cookieUtil.hasCookie("unionid") || !cookieUtil.hasCookie("openid")) { commonUtil.getAuth(); event.preventDefault(); } }); //cookieUtil判断cookie是否存在,设置cookie和获取cookie值(angular service)
视图加载后,修改路由(解决微信支付的bug)
这一步操作主要是因为微信支付的bug,在ios系统中,当调用微信支付的时候,微信会判断当前页面和微信公众号后台设置的支付授权目录是否一致,他会把页面最后一次刷新的url作为判断依据(如果用户刷新了任何页面,这个页面就是支付页面),这个时候,‘#’后面的内容也会被传递过去,在微信的判断流程里,这个url和设置的目录是不匹配的,因为涉及到多个页面都会发起支付请求,所有设置多个带页面参数的url是不合理的,所以这里在‘#’前面添加了‘?’,让微信忽略‘?’后面的内容。
例如我设置的支付目录如下 http://www.test.com/app/ 我的发起支付的界面如下 http://www.test.com/app/#/pay1 http://www.test.com/app/#/pay2 http://www.test.com/app/#/pay3
当我在'#'前面添加'?',这个时候微信会把'?'后面的内容当做参数而angular可以识别'?#',这样既可以避免出现出现提示当前页面url未注册的错误
在视图加载后,修改url
$rootScope.$on('$viewContentLoaded',function(event) { if (window.location.href.indexOf('?#') < 0) { window.location.href = window.location.href.replace('#','?#') } });