摘要:準備工作申請服務器公眾號基本配置這些微信公眾平臺上都有,就不介紹了,接下來進入正題。隨著微信管控越發嚴厲,像一些最基本的網頁轉發都需要授權處理才能獲取到圖片和描述,描述審查也是相當嚴格。
準備工作:
申請服務器 公眾號 基本配置 這些微信公眾平臺上都有,就不介紹了,接下來進入正題。
? 微信網頁授權node js-sdk 授權
公眾平臺的技術文檔目的為了簡明扼要的交代接口的使用,語句難免晦澀,這里寫了些了我所理解的微信開放平臺中關于利用node.js使用授權和js-sdk的一些方法,詳情請見微信公眾平臺.如果用戶在微信客戶端中訪問第三方網頁,公眾號可以通過微信網頁授權機制,來獲取用戶基本信息,進而實現業務邏輯。隨著微信管控越發嚴厲,像一些最基本的網頁轉發都需要授權處理才能獲取到圖片和描述,描述審查也是相當嚴格。#
在微信公眾號請求用戶網頁授權之前,開發者需要先到公眾平臺官網中的“開發 - 接口權限 - 網頁服務 - 網頁帳號 - 網頁授權獲取用戶基本信息”的配置選項中,修改授權回調域名。請注意,這里填寫的是域名(是一個字符串),而不是URL,因此請勿加 http:// 等協議頭;
授權回調域名配置規范為全域名,比如需要網頁授權的域名為:www.qq.com,配置以后此域名下面的頁面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以進行OAuth2.0鑒權。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com無法進行OAuth2.0...
網頁授權的兩種scope的區別(snsapi_base snsapi_userinfo)以snsapi_base為scope發起的網頁授權,是用來獲取進入頁面的用戶的openid的,并且是靜默授權并自動跳轉到回調頁的。用戶感知的就是直接進入了回調頁(往往是業務頁面)
以snsapi_userinfo為scope發起的網頁授權,是用來獲取用戶的基本信息的。但這種授權需要用戶手動同意,并且由于用戶同意過,所以無須關注,就可在授權后獲取該用戶的基本信息。
網頁授權access_token和普通access_token的區別微信網頁授權是通過OAuth2.0機制實現的,在用戶授權給公眾號后,公眾號可以獲取到一個網頁授權特有的接口調用憑證(網頁授權access_token),通過網頁授權access_token可以進行授權后接口調用,如獲取用戶基本信息;
其他微信接口,需要通過基礎支持中的“獲取access_token”接口來獲取到的普通access_token調用。
? 具體步驟: * 代碼配置:package.json { "name": "js-sdk", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "babel-runtime": "^6.26.0", "body-parser": "^1.18.2", "cheerio": "^1.0.0-rc.2", "connect-mongo": "^2.0.1", "connect-redis": "^3.3.3", "cookie-parser": "^1.4.3", "crypto": "^1.0.1", "ejs": "^2.5.7", "express": "^4.16.2", "express-session": "^1.15.6", "fs": "^0.0.1-security", "mongoose": "^5.0.16", "morgan": "^1.9.0", "redis": "^2.8.0", "request": "^2.83.0", "sha1": "^1.1.1", "util": "^0.10.3", "utility": "^1.13.1" }, "devDependencies": { "babel-core": "^6.26.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-es2015": "^6.24.1", "gulp": "^3.9.1", "gulp-autoprefixer": "^4.1.0", "gulp-babel": "^7.0.0", "gulp-concat": "^2.6.1", "gulp-connect": "^5.2.0", "gulp-imagemin": "^4.1.0", "gulp-minify-css": "^1.2.4", "gulp-minify-html": "^1.0.6", "gulp-px2rem-plugin": "^0.4.0", "gulp-uglify": "^3.0.0", "gulp-util": "^3.0.8" } } app.js const express = require("express"); const bodyParser = require("body-parser"); const path = require("path"); const logger = require("morgan"); const cookieParser = require("cookie-parser"); const indexRoute = require("./app/routes/index.route"); const app = express(); app.set("views", path.join(__dirname, "app/views")); app.set("view engine", "ejs"); /*配置靜態文件路徑*/ app.use(express.static(path.join(__dirname, "public"))); /*配置請求日志*/ app.use(logger("dev")); /*解析application/json格式數據*/ app.use(bodyParser.json()); /*解析application/www-x-form-urlencoded格式數據*/ app.use(bodyParser.urlencoded({extended: false})); /*解析cookie*/ app.use(cookieParser()); /*解析session*/ const session = require("express-session"); app.use(session({ secret: "123456", //建議使用隨機字符串 resave: true, saveUninitialized: true, cookie: {maxAge: 24 * 60 * 60 * 1000} })); /*配置路由*/ app.use("/", indexRoute); app.use((req,res,next)=>{ let err = new Error("Error 404, the source is not found!"); err.status = 404; next(err); }); app.use((err, req, res, next)=>{ console.log(err); res.status(err.status || 500).send(err.message); next(); }); module.exports = app; config/env.config.js module.exports = { port:"80", "token":"yourtoken", "appID":"***", "appsecret":"***", "userAppID": "***", "userAppSecret": "***" } app/routes/index.routes.js const express = require("express"); const path = require("path"); const authMiddleware = require("../middlewares/auth.middleware"); const router = express.Router(); const querystring = require("querystring"); const url = require("url"); const cheerio = require("cheerio") router.get("/", authMiddleware.getCode, (req,res,next)=>{ res.sendFile(path.join(__dirname, "../views/index.html")); }) app/views/index.htmlDocument 這里只是測試getCode成功與否
新建 app/config.access_token.json待用
新建 app/config.ticket.json待用
app/middlewares/auth.middlewares.js exports.getUserInfo = (req,res,next)=>{ console.log("<-----------------獲取getUserInfo--------------------->") console.log("----->req.access_token : "+req.access_token); 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, (err,httpResponse,body)=>{ console.log("---->--通過access_token和openid獲取到的用戶個人信息 :") console.log(body); let result = JSON.parse(body); res.cookie("openid", result.openid, {maxAge: 24 * 60 * 60 * 1000, httpOnly: false}); res.cookie("nickname", result.nickname, {maxAge: 24 * 60 * 60 * 1000, httpOnly: false}); res.cookie("headimgurl", result.headimgurl, {maxAge: 24 * 60 * 60 * 10000, httpOnly: false}); res.cookie("unionid", result.unionid, {maxAge: 24 * 60 * 60 * 1000, httpOnly: false}) next(); }) }* 以snsapi_base為scope發起的授權 第一步:用戶同意授權,獲取code
app/middleares/auth.middlewares.js const config = require("../../config/env.config"); const request = require("request"); const appid = config.appID; const appsecret = config.appsecret; /*獲取code*/ exports.getCode = function(req,res,next){ console.log("--|cookies : "+ JSON.stringify(req.cookies)); if(req.cookies.openid){ next(); }else{ let back_url = escape(req.url);//解碼,解決url?后面參數返回消失問題 2.req.url 獲取URL console.log("獲取的url路由參數為 :"+back_url) let redirect_uri = `{你的域名}/getUserInfo?back_url=${back_url}`; //注意這里執行了getUserInfo路由 let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect `; console.log("重定向的url : "+url); //next(); res.redirect(url);//res.redirect()重定向跳轉 參數僅為URL時和res.location(url)一樣 }; };第二步:通過code換取網頁授權access_token
/*獲取access_token*/ exports.getAccess_token = (req,res,next)=>{ console.log("<------------------獲取snsapi_base access_token----------------------->") console.log(JSON.stringify(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, (err, httpResponse, body)=>{ console.log(err); console.log("--||--code換取的所有信息 :"+body); let result = JSON.parse(body); req.access_token = result.access_token; req.openid = result.openid; next(); }) };第三步:拉取用戶信息(需scope為 snsapi_userinfo)
/getUserInfo使用了getAccess_token getUserInfo 中間件 在code沒過期的情況下可以進一步獲取access_token 和個人信息 router.get("/getUserInfo", authMiddleware.getAccess_token, authMiddleware.getUserInfo, function (req, res, next) { console.log("<------------------"/getUserInfo"----------------------->"); console.log("----->|查詢的url字符串參數 :" + JSON.stringify(req.query)); let back_url = req.query.back_url; for (let item in req.query) { if (item !== "back_url" && item !== "code" && item !== "state") { back_url += "&" + item + "=" + req.query[item]; }; }; console.log("---->|重新篩選路徑back_url : " + back_url); res.redirect(back_url); }); # * 以snsapi_userinfo為scope發起的授權 app/middlewares/accessToken.middlesware.js let weixinConfig = require("../../config/env.config.js"); let request = require("request"); let fs = require("fs"); //獲取accessToken exports.accessToken = function (req, res, next) { console.log("<------------------"獲取snsapi_userinfo accessToken"----------------------->"); let valide = isValide(); //{ code: 0, result: result.access_token } or{code:1001} if (valide.code === 0) { //access_token還沒過期,用以前的 req.query.access_token = valide.result; next(); } else { //重新獲取access_token && expire_in let appid = weixinConfig.appID; let secret = weixinConfig.appsecret; let url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret; request(url, function (error, response, body) { let result = JSON.parse(body); let now = new Date().getTime(); //new Date().getTime() 獲得的是毫秒 result.expires_in = now + (result.expires_in - 20) * 1000; //expire_in一般是7200s 提前20毫秒 req.query.access_token = result.access_token; //new access_token req.query.tokenExpired = result.expires_in; // 7200s next(); }); }; }; //獲取ticket exports.ticket = function (req, res, next) { console.log("<------------------"獲取ticket"----------------------->"); let ticketResult = isTicket(); if (ticketResult.code === 0) { console.log("已經有了ticket : " + JSON.stringify(ticketResult)); req.query.ticket = ticketResult.result; next(); } else { console.log("開始獲取ticket"); let access_token = req.query.access_token; let _tokenResult = { access_token: req.query.access_token, expires_in: req.query.tokenExpired }; let url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi"; request(url, function (err, response, body) { let result = JSON.parse(body); console.log(result); if (result.errcode == "0") { let now = new Date().getTime(); result.expires_in = now + (result.expires_in - 20) * 1000; // 改變時間為當前時間的兩小時后 fs.writeFileSync("./config/access_token.json", JSON.stringify(_tokenResult)); //fs.writeFileSync:以同步的方式將data寫入文件,文件已存在的情況下,原內容將被替換。 fs.writeFileSync("./config/ticket.json", JSON.stringify(result)); console.log("異步寫入access_token ticket.json"); req.query.ticket = result.ticket; next(); }; }); }; }; function isValide() { //有效 let result = fs.readFileSync("./config/access_token.json").toString(); //同步讀取json文件 //這里用toString的原因:讀出來的數據是一堆包含著16進制數字的對象,必須通過toString轉為字符串形式 if (result) { result = JSON.parse(result); let now = new Date().getTime(); if (result.access_token && result.expires_in && now < result.expires_in) { console.log("access_token 還在7200s以內,沒有過期"); //access_token有效 expires_in應該指的是距離生成時間的7200秒后 return { code: 0, result: result.access_token }; } else { console.log("access_token 失效"); return { code: 1001 }; } } else { return { code: 1001 }; }; }; function isTicket() { let result = fs.readFileSync("./config/ticket.json").toString(); console.log("result:", result); if (result) { result = JSON.parse(result); console.log(result); let now = new Date().getTime(); if (result.ticket && result.expires_in && now < result.expires_in) { console.log("ticket有效,沿用當前ticket.json里的ticket"); return { code: 0, result: result.ticket }; } else { console.log("ticket無效需要獲取"); return { code: 1001 }; } } else { return { code: 1001 }; }; }
accessToken.middlesware.js寫了關于獲取以snsapi_userinfo為scope發起的網頁授權的access_token ticket,并用fs以json字符串的形式存到本地,并檢測過期時間,如果沒過期就繼續讀取使用,如果過期就重新獲取并儲存在心的access_token ticket到本地
app/routes/index.routes.js const crypto = require("crypto"); const sha1 = require("sha1"); const accessTokenMiddle = require("../middlewares/accessToken.middleware.js"); const weixin = require("../../config/env.config"); router.get("/weixin", accessTokenMiddle.accessToken, accessTokenMiddle.ticket, function (req, res, next) { console.log("<------------------"/weixin"----------------------->"); console.log("----->| req.query : " + JSON.stringify(req.query)); crypto.randomBytes(16, function (ex, buf) { let appId = weixin.appID; let noncestr = buf.toString("hex"); let jsapi_ticket = req.query.ticket; let timestamp = new Date().getTime(); timestamp = parseInt(timestamp / 1000); let url = req.query.url; console.log("參數 :"); console.log(noncestr); console.log(jsapi_ticket); console.log(timestamp); console.log(url); let str = ["noncestr=" + noncestr, "jsapi_ticket=" + jsapi_ticket, "timestamp=" + timestamp, "url=" + url].sort().join("&"); console.log("待混淆加密的字符串 : "); console.log(str); let signature = sha1(str); console.log("微信sdk簽名signature :"); console.log(signature); let result = { code: 0, result: { appId: appId, timestamp: timestamp, nonceStr: noncestr, signature: signature } }; res.json(result); //res.json 等同于將一個對象或數組傳到給res.send() }); });
在html頁面使用微信公眾平臺提供的API 需要引用 http://res.wx.qq.com/open/js/...
在靜態文件中調用分享功能的api 更多API請打開 # 微信JS-SDK說明文檔
public/index.html注釋: 微信開發必須在微信開發者工具上開發,且只能是默認80端口,在開發中經常有80端口被占用的情況,如果有請使用Document userList....
public/js/userList.js let signatureUrl = url.split("#")[0]; let URL = encodeURIComponent(signatureUrl); let title = "這是分享的表標題"; let desc = "this is description"; let shareUrl = window.location.href; let logo = "http://yizhenjia.com/dist/newImg/logo.png"; SHARE(title, desc, shareUrl, logo); $.get("/weixin?url=" + URL, function(result) { if (result.code == 0) { wx.config({ debug: false, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。 appId: result.result.appId, // 必填,公眾號的唯一標識 timestamp: result.result.timestamp, // 必填,生成簽名的時間戳 nonceStr: result.result.nonceStr, // 必填,生成簽名的隨機串 signature: result.result.signature, // 必填,簽名,見附錄1 jsApiList: ["onMenuShareAppMessage", "onMenuShareTimeline", "chooseImage", "scanQRCode", "getLocation", "openLocation"] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2 }); }; }); function SHARE(title, desc, shareUrl, logo) { wx.ready(function() { // config信息驗證后會執行ready方法,所有接口調用都必須在config接口獲得結果之后,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對于用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。 //分享 wx.onMenuShareAppMessage({ title: title, // 分享標題 desc: desc, // 分享描述 link: shareUrl, // 分享鏈接 imgUrl: logo, // 分享圖標 type: "", // 分享類型,music、video或link,不填默認為link dataUrl: "", // 如果type是music或video,則要提供數據鏈接,默認為空 success: function() { 用戶確認分享后執行的回調函數 alert("分享成功!"); }, cancel: function() { // 用戶取消分享后執行的回調函數 }, fail: function(err) { alert("分享失敗"); } }); }); wx.error(function(res) { // config信息驗證失敗會執行error函數,如簽名過期導致驗證失敗,具體錯誤信息可以打開config的debug模式查看,也可以在返回的res參數中查看,對于SPA可以在這里更新簽名。 //alert("Error"); }); }
lsof -i tcp:80
kill -9 進程
打開charles 點擊Proxy setting 設置 port
保證手機和電腦處于同一Wi-Fi下,配置手動代理 輸入IP和端口 查看ip地址 :charles上可查看 或者終端輸入ifconfig (cmd:ipconfig)
掃碼或使用地址即可訪問
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100544.html
摘要:參考鏈接微信小程序七日談第五天你可能要在登錄功能上花費大力氣理解認證及實踐網站微信登錄實現最后,感謝女朋友支持。 開發微信小程序時,接入小程序的授權登錄可以快速實現用戶注冊登錄的步驟,是快速建立用戶體系的重要一步。這篇文章將介紹 python + sanic + 微信小程序實現用戶快速注冊登錄全棧方案。 微信小程序登錄時序圖如下: showImg(https://segmentfaul...
摘要:參考鏈接微信小程序七日談第五天你可能要在登錄功能上花費大力氣理解認證及實踐網站微信登錄實現最后,感謝女朋友支持。 開發微信小程序時,接入小程序的授權登錄可以快速實現用戶注冊登錄的步驟,是快速建立用戶體系的重要一步。這篇文章將介紹 python + sanic + 微信小程序實現用戶快速注冊登錄全棧方案。 微信小程序登錄時序圖如下: showImg(https://segmentfaul...
摘要:今天聊一下微信公眾號開發在授權網頁中的支付流程。前端獲得簽名后,再請求微信服務器,下面的支付流程就可以繼續下去了。 今天聊一下微信公眾號開發在授權網頁中的支付流程。 微型公眾號開發有以下幾個步驟:1.獲取全局access_token2.獲取網頁授權的access_token和refresh_token3.獲取網頁授權的簽名(前端用于獲取調用JSSDK的權限)4.公眾號支付-調用統一下單...
摘要:協議由來已久,是公司推出的視頻播放協議,穩定性和安全性較更好,應用廣泛。但是加密算法過于簡單,通過解密,即可實現本地觀看。可以看到視頻采用了的加密算法。目前尚無解密方案出現,安全級別極高。 信息化時代,多媒體的應用日漸成為人們生活中不可或缺的部分,無論是獲取最新資訊還是教育學習,視頻都是直觀高效的媒介之一。 基于互聯網的快速傳播,眾多培訓機構也逐漸將線下原創版權課程遷移到在線平臺中,一...
閱讀 2986·2023-04-26 02:25
閱讀 2258·2023-04-25 18:05
閱讀 650·2021-09-30 09:57
閱讀 2946·2021-09-27 14:10
閱讀 1656·2019-08-30 15:44
閱讀 1005·2019-08-29 15:28
閱讀 2531·2019-08-29 14:10
閱讀 2265·2019-08-29 13:30