Lua로 작성된 nginx 서버 WeChat 공개 플랫폼 에이전트를 사용하세요.
목표:
프런트 엔드 nginx를 WeChat 프록시로 사용하여 내부 애플리케이션 계층과 WeChat 서비스 간의 결합을 줄입니다.
WeChat 공개 계정에 대한 자동 회신을 구성하고 nginx에서 일부 사용자 메시지를 처리하여 애플리케이션 계층에 대한 부담을 줄입니다.
WeChat 공용 계정 API에 사용되는 access_token
중앙 제어 서버로 통합 관리하여 비즈니스 계층과 API 구현을 분리하고 access_token
충돌률을 줄이고 서비스 안정성을 높입니다.
WeChat JS-SDK 인증 콜백 페이지를 배포하여 애플리케이션 계층에 대한 부담을 줄입니다.
구성
인터페이스 토큰 및 자동 응답 설정을 포함한 공용 계정 전역 구성 데이터입니다.
섬기는 사람
WeChat에서 일반 메시지 및 이벤트 푸시 요청을 수신하고 구성에 따라 응답합니다. 해당 구성이 이루어지지 않으면 WeChat 요구 사항에 따라 success
반환됩니다.
핵심 코드의 이 부분은 aCayF/lua-resty-wechat에서 리팩터링 및 수정되었습니다.
config.autoreplyurl
사용하여 백그라운드 처리 서비스 주소를 구성하고 복잡한 WeChat 메시지를 전달, 처리 및 응답합니다(pintized/lua-resty-http에 따라 다름).
프록시_액세스_토큰
Redis를 사용하여 access_token
및 jsapi_ticket
캐시하고 정기적으로 WeChat 서비스 업데이트를 자동으로 호출하며 분산 업데이트를 지원합니다.
대리
에이전트는 WeChat 공개 플랫폼 API 인터페이스를 호출하고 자동으로 access_token
매개변수를 추가합니다.
프록시_액세스_필터
클라이언트 IP를 필터링하고 요청 소스를 제한합니다.
맹세하다
jssdk_config
nginx 구성:
http { lua_package_path 'lua 파일 경로'; lua_shared_dict wechat 1M; # 단일 타이머를 유지하기 위해 공유 메모리를 사용합니다. init_by_lua ' ngx.shared.wechat:delete("updater") -- 타이머 식별자를 지웁니다. "resty.wechat.config") '; init_worker_by_lua ' local ok, err = ngx.shared.wechat:add("updater", "1") -- 정상이 아니거나 오류가 발생한 경우 단일 프로세스 시작 타이머 반환 end require("resty.wechat.proxy_access_token")() ' ; 서버 {위치 /wechat-server { content_by_lua ' require("resty.wechat.server")() '; }location /wechat-proxy/ { rewrite_by_lua ' require("resty.wechat.proxy")("wechat-proxy") -- 매개변수는 위치 경로입니다.'; access_by_lua ' require("resty.wechat.proxy_access_filter")( ) ' ; 프록시_패스 https://api.weixin.qq.com/; }location /wechat-baseoauth { # param: goto rewrite_by_lua ' require("resty.wechat.oauth").base_oauth("/wechat-redirect 경로") '; }location /wechat-useroauth { # param: goto rewrite_by_lua ' require("resty.wechat.oauth").userinfo_oauth("/wechat-redirect 경로") '; }위치 /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 "도메인 간 호출이 필요한 경우" content_by_lua ' require("resty.wechat.jssdk_config") () '; } } }
웹페이지 삽입 JS-SDK 권한:
$.아약스({ url: "/wechat-jssdk-config의 URL 경로", data: {url: window.location.href,api: "onMenuShareTimeline|onMenuShareAppMessage|onMenuShareQQ|onMenuShareWeibo|onMenuShareQZone" }, 성공: 함수(응답) {wx.config(응답); }});$.ajax({ url: "/wechat-jssdk-config의 URL 경로", 데이터: {url: window.location.href }, 성공: 함수(응답) {wx.config({ appId: response.appId, 타임스탬프: response.timestamp, nonceStr: response.nonceStr, 서명: response.signature, jsApiList: [ 'onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ' , 'onMenuShareWeibo', 'onMenuShareQZone' ]}); }});
Java를 사용하여 프록시 웹페이지 승인을 통해 얻은 쿠키를 구문 분석합니다.
Map authInfo = JSON.parseObject(decryptAES(unBase64("cookie value"), getKey("AES key")));//기본 AES 키: "vFrItmxI9ct8JbAg"//config.lua에서 구성됨 -> cookie_aes_key//Dependency 메서드 com.alibaba.fastjson.JSON 가져오기;가져오기 com.google.common.base.Charsets;import javax.crypto.Cipher;import javax.crypto.spec.SecretKeySpec;import java.security.Key;public StringBuilder 패딩(String s, char 문자, int 반복) {StringBuilder sb = 새로운 StringBuilder(들); while (반복-- > 0) {sb.append(letter); }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(값); 반환 문자열(복호화됨); } catch (예외 e) {throw new RuntimeException(e); } }public byte[] bytes(String str) {return str == null ? null : str.getBytes(Charsets.UTF_8); }공개 키 keyFromString(String keyString) {return new SecretKeySpec(bytes(keyString), "AES"); }공개 키 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)); }