·前言
小程序後端大部分同公衆號開發,小程序相對簡單,且獲取用戶openid不需要微信網頁授權
·後端配置詳解
微信公衆平臺文檔地址:https://developers.weixin.qq.com/miniprogram/dev/framework/
1.yml常量配置
#小程序
wxapplet:
appid: x
secret: x
grant_type: authorization_code
url: https://api.weixin.qq.com/sns/jscode2session
template_id:
submission: x #模板id
received: x
reply: x
#rediskey
rediskey:
access_token: WechatApplet:access_token #redis的key名
2.1 獲取access_token
原理同公衆號獲取access_token.
若是不同業務場景每次拿access_token都去請求微信,重複獲取將導致上次獲取的access_token失效,需定時刷新,保證一致性。access_token默認有7200秒(倆小時)過期時間,所以採取redis將其緩存起來,並設置少於7200秒的時間。每次取用access_token之前都先從redis中獲取,若已過期再重新請求。
//============小程序配置參數
@Resource
private RedisTemplate<String, Object> stringRedisTemplate;
@Value("${wxapplet.appid}")
private String appletAppId;
@Value("${wxapplet.secret}")
private String appletAppSecret;
@Value("${wxapplet.grant_type}")
private String grantType;
@Value("${wxapplet.url}")
private String validateUrl;
@Value("${wxapplet.rediskey.access_token}")
private String appletAccessTokenRediskey;
@Value("${wxapplet.template_id.submission}")
private String submissionNotice;
@Value("${wxapplet.template_id.received}")
private String receivedNotice;
@Value("${wxapplet.template_id.reply}")
private String replyNotice;
public ResultVO<?> getAppletAccessToken() {
//redis中獲取access_token
String tokenRedis = (String) stringRedisTemplate.opsForValue().get(appletAccessTokenRediskey);
if (tokenRedis == null) {
//若沒有(從未,或者已過期),請求微信獲取
//請求微信
JSONObject myAccessToken = WechatApiUtil.getMyAccessToken(appletAppId, appletAppSecret);
if (myAccessToken.containsKey("access_token")) {
String newAccessToken = myAccessToken.getString("access_token");
stringRedisTemplate.opsForValue().set(appletAccessTokenRediskey, newAccessToken, 7000, TimeUnit.SECONDS);
log.info("請求wechat request獲取access_token");
//請求成功將記錄保存到數據庫中
WxApplet entity = new WxApplet();
entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
entity.setCategory(WeChatRequestTypeEnum.GET_APPLET_ACCESS_TOKEN.getCode());
entity.setAccessToken(newAccessToken);
entity.setReturnResult(myAccessToken.toJSONString());
wxAppletMapper.insert(entity);
return ResultVOUtil.returnSuccess(newAccessToken);
} else {
//請求有誤,返回錯誤信息
//請求有無也將記錄保存到數據庫中
log.info("獲取access_token失敗");
WxApplet entity = new WxApplet();
entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
entity.setCategory(WeChatRequestTypeEnum.GET_APPLET_ACCESS_TOKEN.getCode());
entity.setReturnResult(myAccessToken.toJSONString());
wxAppletMapper.insert(entity);
return ResultVOUtil.returnFail(myAccessToken.getString("errmsg"));
}
} else {
log.info("redis正常獲取access_token");
return ResultVOUtil.returnSuccess(tokenRedis);
}
}
封裝的請求微信方法(使用hutool工具包):
工具類方法:
//獲取AccessToken
public static JSONObject getMyAccessToken(String appId, String appSecret) {
String apiUrl = StrUtil.format(
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}",
appId, appSecret
);
String body = HttpRequest.get(apiUrl).execute().body();
JSONObject jsonObject = myThrowErrorMessageIfExists(body);
return jsonObject;
}
2.2根據前端提供code 換取小程序用戶openid及相關信息
小程序消息推送以及涉及到用戶的操作均需要用戶openid,需要請求微信獲取。
爲保持session_key的。最新時效性,也將獲取到的用戶openid存入redis,但是每次取用之前進行判斷,若不存在就存入redis,若存在,先刪除然後重新獲取對其更新。
public ResultVO<?> getOpenIdByCode(WxAppletDTO dto) {
//請求微信獲得openid和sessionkey
JSONObject myOpenIdByCode = WechatApiUtil.getMyOpenIdByCode(appletAppId, appletAppSecret, dto.getCode());
if (myOpenIdByCode.containsKey("errcode")) {
//若請求微信失敗(無效code情況)
//失敗也要每次請求記錄到數據庫中
WxApplet entity = new WxApplet();
entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
entity.setWechatCode(dto.getCode());
entity.setCategory(WeChatRequestTypeEnum.GET_APPLET_OPENID.getCode());
entity.setReturnResult(myOpenIdByCode.toJSONString());
wxAppletMapper.insert(entity);
return ResultVOUtil.returnFail(-1, "fail", myOpenIdByCode);
}
//正常請求到了微信
//用戶唯一標識
String openid = myOpenIdByCode.getString("openid");
//臨時會話祕鑰
String session_key = myOpenIdByCode.getString("session_key");
//每次成功請求記錄到數據庫中
WxApplet entity = new WxApplet();
entity.setNo(CodeNoEnum.WX_APPLET.getTableNO() + CommentUtil.createNo());
entity.setWechatCode(dto.getCode());
entity.setCategory(WeChatRequestTypeEnum.GET_APPLET_OPENID.getCode());
entity.setOpenid(openid);
entity.setSessionKey(session_key);
entity.setReturnResult(myOpenIdByCode.toJSONString());
wxAppletMapper.insert(entity);
//根據openid爲key查詢skey_redis是否存在
String skey_redis = (String) stringRedisTemplate.opsForValue().get(openid);
if (!StringUtils.isEmpty(skey_redis)) {
//存在,刪除此數據,重新獲取並返回(保持session_key的最新時效性)
stringRedisTemplate.delete(skey_redis);
}
//緩存一份新的
//uuid生成唯一key
String skey = UUID.randomUUID().toString();
JSONObject sessionObj = new JSONObject();
sessionObj.put("openId", openid);
sessionObj.put("sessionKey", session_key);
//[更新]以openid爲key,唯一sky爲value,存redis
stringRedisTemplate.opsForValue().set(openid, skey);
//重新生成一份帶有openid和session_key的數據
stringRedisTemplate.opsForValue().set(skey, sessionObj.toJSONString());
return ResultVOUtil.returnSuccess(myOpenIdByCode);
}
工具類方法:
//微信公衆號通過網頁授權code獲取openid和session_key
public static JSONObject wechatPbulicGetOpenIdByCode(String appId, String appSecrct, String code) {
String apiUrl = StrUtil.format(
" https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code",
appId, appSecrct, code
);
String body = HttpRequest.get(apiUrl).execute().body();
return throwErrorMessageIfExists(body);
}
工具類打印請求日誌方法:
//微信請求異常處理
public static JSONObject throwErrorMessageIfExists(String body) {
String callMethodName = (new Throwable()).getStackTrace()[1].getMethodName();
log.info("#請求微信方法名:{},body={}", callMethodName, body);
JSONObject jsonObject = JSON.parseObject(body);
return jsonObject;
}
附:通用vo類,詳見公衆號後端開發博客。