package com.leenleda.project.manager.common.utils; import com.alibaba.fastjson.JSONObject; import com.leenleda.project.manager.common.config.LeenledaConfig; import com.leenleda.project.manager.common.dto.response.JsTicketResponseDto; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLDecoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Formatter; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * @author pengbenlei * @company leenleda * @date 2021/3/29 9:49 * @description */ @Component public class WechatUtil { private static final String JSTICKET_CACHE_KEY="leenleda:wechat:jsticket:cache"; private static final int JSTICKET_CACHE_TIME=6200; @Autowired RedisUtil redisUtil; @Resource LeenledaConfig leenledaConfig; private String getAccessToken() { String accessToken = (String) redisUtil.get(leenledaConfig.getWechatAppId()); if (StringUtils.isEmpty(accessToken)) { // 获取缓存 String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + leenledaConfig.getWechatAppId() + "&secret=85e05903abd1e3772e85de160120dc2d"; RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); JSONObject jsonObject = JSONObject.parseObject(response.getBody()); accessToken = jsonObject.getString("access_token"); redisUtil.set(leenledaConfig.getWechatAppId(), accessToken, 7200, TimeUnit.SECONDS); } return accessToken; } /** * 发送公众号模板消息 * @param templeteId * @param pageUrl * @param openId * @param data */ public void sendTemplateMsg(String templeteId, String pageUrl, String openId, String data) { String token = getAccessToken(); String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/bizsend?access_token=" + token; JSONObject jsonObject = new JSONObject(); jsonObject.put("access_token", token); jsonObject.put("touser", openId); jsonObject.put("template_id", templeteId); jsonObject.put("page", pageUrl); jsonObject.put("data", data); String requestJsonStr = JSONObject.toJSONString(jsonObject); connectWeiXinInterface(url, requestJsonStr); } /** * 获取微信js配置 * @param url * @return */ public JsTicketResponseDto getConfig(String url) { try { url= URLDecoder.decode(url,"GBK"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String ticket = getJsapiTicket(); String timestamp = create_timestamp(); String nonceStr = create_nonce_str(); String string1 = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url; String signature = ""; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); // 对string1 字符串进行SHA-1加密处理 signature = byteToHex(crypt.digest()); // 对加密后字符串转成16进制 } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } Assert.isTrue(!StringUtils.isEmpty(signature),"生成js签名出错!"); JsTicketResponseDto jsTicketResponseDto = new JsTicketResponseDto(leenledaConfig.getWechatAppId(), timestamp, nonceStr, signature); return jsTicketResponseDto; } public String getJsapiTicket() { String ticket = redisUtil.getStr(JSTICKET_CACHE_KEY); if (!StringUtils.isEmpty(ticket)) { return ticket.replace("\"", ""); } String token = getAccessToken(); String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token + "&type=jsapi"; RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); JSONObject jsonObject = JSONObject.parseObject(response.getBody()); ticket = jsonObject.getString("ticket"); Assert.isTrue(!StringUtils.isEmpty(ticket), "获取jsticket出错!"); redisUtil.set(JSTICKET_CACHE_KEY, ticket, JSTICKET_CACHE_TIME, TimeUnit.SECONDS); return ticket; } // 生成时间戳字符串 public static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } // 生成随机字符串 public static String create_nonce_str() { return UUID.randomUUID().toString(); } private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } public static void connectWeiXinInterface(String action, String json) { URL url; try { url = new URL(action); HttpURLConnection http = (HttpURLConnection) url.openConnection(); http.setRequestMethod("POST"); http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); http.setDoOutput(true); http.setDoInput(true); System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒 System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒 http.connect(); OutputStream os = http.getOutputStream(); os.write(json.getBytes("UTF-8"));// 传入参数 InputStream is = http.getInputStream(); int size = is.available(); byte[] jsonBytes = new byte[size]; is.read(jsonBytes); String result = new String(jsonBytes, "UTF-8"); os.flush(); os.close(); } catch (Exception e) { e.printStackTrace(); } } }
package com.leenleda.project.manager.wechat.interceptor; import com.alibaba.fastjson.JSONObject; import com.leenleda.project.manager.common.config.LeenledaConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.net.URLDecoder; import java.net.URLEncoder; /** * @author pengbenlei * @company leenleda * @date 2021/3/30 14:36 * @description */ @Component public class WeChatAuthInterceptor implements HandlerInterceptor { @Autowired LeenledaConfig leenledaConfig; // 进入controller方法之前调用 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String code = request.getParameter("code"); if (StringUtils.isEmpty(code)) { // 跳转微信授权页面 String redirect_uri = leenledaConfig.getWechatRdirect() + "?redirect_uri=" + request.getParameter("redirect_uri"); redirect_uri = URLEncoder.encode(redirect_uri, "GBK"); String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + leenledaConfig.getWechatAppId() + "&redirect_uri=" + redirect_uri + "" + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"; response.sendRedirect(url); return false; } else { String redirect_uri = request.getParameter("redirect_uri"); Assert.isTrue(!StringUtils.isEmpty(redirect_uri), "业务回调地址不能为空!"); // 授权成功,跳转指定的回调地址,并拼接openid String url = URLDecoder.decode(redirect_uri, "GBK"); // 解析openid String openId = deCode(code); if (url.indexOf("?") > -1) { url += "&openId=" + openId; } else { url += "?openId=" + openId; } response.sendRedirect(url); return true; } } /** * 通过code解析openid */ private String deCode(String code) { String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + leenledaConfig.getWechatAppId() + "&" + "secret=" + leenledaConfig.getWechatSecret() + "&code=" + code + "&grant_type=authorization_code"; RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); JSONObject jsonObject = JSONObject.parseObject(response.getBody()); String openId = jsonObject.getString("openid"); Assert.isTrue(!StringUtils.isEmpty(openId), "解析微信code出错!"); return openId; } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <wx-open-subscribe template="vk-tuJWjy30BDJvwkf-SQrOvRiLfOc9HExH-wIx_DfE,JXk-rEwyK2-s3P3KLDOb9Y9thx-uge_yWJQX9aqQNBo,x_1oPRm1ykvw08KvTp-A26qlaYmf4vAwh88SRgvpFFw,S8idG88AEwPMFBOrxLP4i6fJNahX5HYjtLgG5fP_DIM" id="subscribe-btn"> <template slot="style"> <style> .subscribe-btn { color: #fff; background-color: #07c160; } </style> </template> <template> <button class="subscribe-btn"> 一次性模版消息订阅 </button> </template> </wx-open-subscribe> </body> </html> <script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script> <script> var url = window.location.href; // 微信jsticket配置 $.get("xxxxx/wechat/get-signature?&url=" + encodeURIComponent(url), function (res) { wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: res.appId, // 必填,公众号的唯一标识 timestamp: res.timestamp, // 必填,生成签名的时间戳 nonceStr: res.nonceStr, // 必填,生成签名的随机串 signature: res.signature,// 必填,签名 jsApiList: [], // 必填,需要使用的JS接口列表 openTagList:[‘wx-open-subscribe‘] }); }); wx.ready(function () { console.log("配置引入成功") var btn = document.getElementById(‘subscribe-btn‘); btn.addEventListener(‘success‘, function (e) { console.log(‘success‘, e.detail); }); btn.addEventListener(‘error‘,function (e) { console.log(‘fail‘, e.detail); }); }); </script>