Use nginx server WeChat public platform agent written in Lua.
Target:
Use the front-end nginx as a WeChat proxy to reduce the coupling between the internal application layer and WeChat services.
Configure automatic replies for WeChat public accounts and process some user messages in nginx to reduce pressure on the application layer.
Unified management of access_token
used in the WeChat public account API, as a central control server to isolate the business layer and API implementation, reduce access_token
conflict rate and increase service stability.
Deploy WeChat JS-SDK authorization callback page to reduce pressure on the application layer.
config
Public account global configuration data, including interface token and automatic reply settings.
server
Receive requests for ordinary messages and event push requests from WeChat, and respond according to the configuration. If no corresponding configuration is made, success
will be returned according to WeChat requirements.
This part of the core code is refactored and modified from aCayF/lua-resty-wechat.
Use config.autoreplyurl
to configure the background processing service address, forward, process and respond to complex WeChat messages. (Depends on pintsized/lua-resty-http)
proxy_access_token
Use Redis to cache access_token
and jsapi_ticket
, automatically call WeChat service updates regularly, and support distributed updates.
proxy
The agent calls the WeChat public platform API interface and automatically adds the access_token
parameter.
proxy_access_filter
Filter client IP and limit request sources.
oauth
jssdk_config
nginx configuration:
http { lua_package_path 'path to lua files'; resolver 114.114.114.114; lua_shared_dict wechat 1M; # Use shared memory to maintain a single timer init_by_lua ' ngx.shared.wechat:delete("updater") -- Clear the timer identifier require( "resty.wechat.config") '; init_worker_by_lua ' local ok, err = ngx.shared.wechat:add("updater", "1") -- Single process startup timer if not ok or err then return end require("resty.wechat.proxy_access_token")() '; server {location /wechat-server { content_by_lua ' require("resty.wechat.server")() '; }location /wechat-proxy/ { rewrite_by_lua ' require("resty.wechat.proxy")("wechat-proxy") -- the parameter is the location path'; access_by_lua ' require("resty.wechat.proxy_access_filter")() ' ; proxy_pass https://api.weixin.qq.com/; }location /wechat-baseoauth { # param: goto rewrite_by_lua ' require("resty.wechat.oauth").base_oauth("path to /wechat-redirect") '; }location /wechat-useroauth { # param: goto rewrite_by_lua ' require("resty.wechat.oauth").userinfo_oauth("path to /wechat-redirect") '; }location /wechat-redirect { rewrite_by_lua ' require("resty.wechat.oauth").redirect() '; }location /wechat-jssdk-config { # GET/POST, param: url, [api] add_header Access-Control-Allow-Origin "if need cross-domain call"; content_by_lua ' require("resty.wechat.jssdk_config") () '; } } }
Web page injection JS-SDK permissions:
$.ajax({ url: "url path to /wechat-jssdk-config", data: {url: window.location.href,api: "onMenuShareTimeline|onMenuShareAppMessage|onMenuShareQQ|onMenuShareWeibo|onMenuShareQZone" }, success: function(response) {wx.config(response); }});$.ajax({ url: "url path to /wechat-jssdk-config", data: {url: window.location.href }, success: function(response) {wx.config({ appId: response.appId, timestamp: response.timestamp, nonceStr: response.nonceStr, signature: response.signature, jsApiList: [ 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ' , 'onMenuShareWeibo', 'onMenuShareQZone' ]}); }});
Use Java to parse cookies obtained by proxy webpage authorization
Map authInfo = JSON.parseObject(decryptAES(unBase64("cookie value"), getKey("AES key")));//Default AES key: "vFrItmxI9ct8JbAg"//Configured in config.lua -> cookie_aes_key//Dependency method import com.alibaba.fastjson.JSON;import com.google.common.base.Charsets;import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;import java.security.Key;public StringBuilder padding(String s, char letter, int repeats) {StringBuilder sb = new StringBuilder(s); while (repeats-- > 0) {sb.append(letter); }return sb; }public String padding(String s) {return padding(s, '=', s.length() % 4).toString(); }public byte[] unBase64(String value) {return org.apache.commons.codec.binary.Base64.decodeBase64(padding(value)); }public String string(byte[] bytes) {return new String(bytes, Charsets.UTF_8); }public String decryptAES(byte[] value, Key key) {try {Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");cipher.init(Cipher.DECRYPT_MODE, key);byte[] decrypted = cipher. doFinal(value);return string(decrypted); } catch (Exception e) {throw new RuntimeException(e); } }public byte[] bytes(String str) {return str == null ? null : str.getBytes(Charsets.UTF_8); }public Key keyFromString(String keyString) {return new SecretKeySpec(bytes(keyString), "AES"); }public Key getKey(String key) {if (key.length() >= 16) {return keyFromString(key.substring(0, 16)); }StringBuilder sb = new StringBuilder(key);while (sb.length() < 16) {sb.append(key); }return keyFromString(sb.toString().substring(0, 16)); }