1、首先需要去微信申请账号2、微信会分配appid和secret3、由于微信的接口需要获取token才行4、获取token的URL:get方式请求(https请求)https://api.weixin.qq.com/cgi-bin/token5、获取ticket的URL:get方式请求(https请求)
https://api.weixin.qq.com/cgi-bin/ticket/getticket
6、注意:要是本地测试的话把自己的IP配置到微信的公众平台的白名单里,否则取不到token
Controller层
import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.Map; @Slf4j @RestController @RequestMapping("wechat") @Api(tags = "微信分享") public class WeChatShareController { @Autowired private IWeChatShareService weChatShareService; @PostMapping("/share") @ApiOperation(value = "微信分享") public ControllerResponse share(HttpServletRequest request) { ControllerResponse controllerResponse = new ControllerResponse(); log.info("微信分享请求参数为:" + request.getParameter("url")); if (StringUtils.isBlank(request.getParameter("url"))) { controllerResponse.setStatus_code(Resp.FAIL.getCode()); controllerResponse.setMessage("请求参数为空"); return controllerResponse; } Map<String, String> map = weChatShareService.getSign(request.getParameter("url")); if (null == map) { controllerResponse.setStatus_code(Resp.FAIL.getCode()); controllerResponse.setMessage("获取token失败"); } else { controllerResponse.setBody(map); controllerResponse.setStatus_code(Resp.SUCCESS.getCode()); controllerResponse.setMessage(Resp.SUCCESS.getDesc()); } return controllerResponse; } }Service层
import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @Service @Slf4j public class WeChatShareServiceImpl implements IWeChatShareService { @Value("${wechat.appid}") private String appid; @Value("${wechat.secret}") private String secret; @Value("${wechat.token.url}") private String tokenUrl; @Value("${wechat.ticket.url}") private String ticketUrl; @Value("${wechat.grantType}") private String grantType; @Value("${wechat.type}") private String type; @Value("${wechat.time}") private int time; //创建一个读写锁 private static ReadWriteLock lock = new ReentrantReadWriteLock(); @Autowired private RedisClient redisClient; @Override public TokenJson getAccessToken() { String url = tokenUrl + "?grant_type=" + grantType + "&appid=" + appid + "&secret=" + secret; log.info("微信服务器获取token请求报文为:" + url); try { String result = HttpRequesUtil.sendHttpRequest(true, url,null,"GET"); log.info("微信服务器获取token返回报文为:" + result); Gson gson = new Gson(); TokenJson TokenJson = gson.fromJson(result, TokenJson.class); return TokenJson; } catch (Exception e) { log.info("微信服务器获取token异常:" + e.getMessage()); return null; } } @Override public TicketJson getTicket(String token) { String url = ticketUrl + "?access_token=" + token + "&type=" + type; log.info("微信服务器获取Ticket请求报文为:" + url); try { String result = HttpRequesUtil.sendHttpRequest(true, url,null,"GET"); log.info("微信服务器获取Ticket返回报文:" + result); Gson gson = new Gson(); TicketJson ticketJson = gson.fromJson(result, TicketJson.class); return ticketJson; } catch (Exception e) { log.info("微信服务器获取Ticket异常:" + e.getMessage()); return null; } } @Override public Map<String, String> getSign(String url) { Map<String, String> ret = null; String token = getRedisToken(); if(StringUtils.isNotBlank(token)){ String ticket = getRedisTicket(token); ret = SignUtil.sign(ticket, url); log.info("微信分享计算出的签名为:" + ret.toString()); ret.put("appid",appid); } return ret; } //由于微信每天调用token的上限为2000次,所以采用放置缓存中 //这里用锁的原因是并发:若一个A请求获取token,此时token已失效,需重新获取,这时候token还未放置缓存中,同时B请求过来,发现//token也是失效的,也会重新获取,这时要是B获取的token放置缓存中覆盖A已获取到的token,A获取到的token会失效,导致A请求无法获取到有效的token @Override public String getRedisToken() { String token = ""; try { lock.readLock().lock(); log.info("----------Token获取读锁---------"); String key = ConstantKey.WECHAT_PREIX+ConstantKey.WECHAT_TOKEN; token = redisClient.getStringValue(key); if (StringUtils.isBlank(token)) { lock.readLock().unlock();//释放读锁,获取写锁 log.info("----------Token释放读锁---------"); try { lock.writeLock().lock(); log.info("----------Token获取写锁---------"); //获取写锁后再次判断对象是否为null,方式下一个等待的写线程进入后直接获取数据去 if (StringUtils.isBlank(token)) { TokenJson tokenJson = getAccessToken(); if (null != tokenJson && StringUtils.isNotBlank(tokenJson.getAccess_token())) { redisClient.putStringValue(key, tokenJson.getAccess_token(), tokenJson.getExpires_in()-time); token = tokenJson.getAccess_token(); } } //自身锁降级为读锁 lock.readLock().lock(); log.info("----------Token自身锁降级为读锁---------"); } catch (Exception e) { log.info("获取微信Token异常:" + e.getMessage()); } finally { lock.writeLock().unlock();//释放写锁 log.info("----finally------Token释放写锁---------"); } } } catch (Exception e) { log.info("获取微信Token异常:" + e.getMessage()); } finally { lock.readLock().unlock(); log.info("----finally------Token释放读锁---------"); } return token; } @Override public String getRedisTicket(String token) { String ticket = ""; try { lock.readLock().lock(); log.info("----------Ticket获取读锁---------"); String key = ConstantKey.WECHAT_PREIX+ConstantKey.WECHAT_TICKET+token; ticket = redisClient.getStringValue(key); if (StringUtils.isBlank(ticket)) { lock.readLock().unlock();//释放读锁,获取写锁 log.info("----------Ticket释放读锁---------"); try { lock.writeLock().lock(); log.info("----------Ticket获取写锁---------"); //获取写锁后再次判断对象是否为空,方式下一个等待的写线程进入后直接获取数据去 if (StringUtils.isBlank(ticket)) { TicketJson ticketJson = getTicket(token); if (null != ticketJson && StringUtils.isNotBlank(ticketJson.getTicket())) { redisClient.putStringValue(key, ticketJson.getTicket(), Integer.valueOf(ticketJson.getExpires_in())-time); ticket = ticketJson.getTicket(); } } //自身锁降级为读锁 lock.readLock().lock(); log.info("----------Ticket自身锁降级为读锁---------"); } catch (Exception e) { log.info("获取微信ticket异常:" + e.getMessage()); } finally { lock.writeLock().unlock();//释放写锁 log.info("---finally-------Ticket释放写锁---------"); } } } catch (Exception e) { log.info("获取微信ticket异常:" + e.getMessage()); } finally { lock.readLock().unlock(); log.info("---finally-------Ticket释放读锁---------"); } return ticket; } } 微信分享签名工具类 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.UUID; import java.util.Map; import java.util.HashMap; import java.util.Formatter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.io.UnsupportedEncodingException; /** * 微信分享签名工具类 */ public class SignUtil { private static Logger logger = LoggerFactory.getLogger(SignUtil.class); /** * 生成签名方法 * @param jsapi_ticket * @param url 分享的url * @return map */ public static Map<String, String> sign(String jsapi_ticket, String url) { Map<String, String> ret = new HashMap<String, String>(); String nonce_str = create_nonce_str(); String timestamp = create_timestamp(); String string1; String signature = ""; //注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "×tamp=" + timestamp + "&url=" + url; logger.info("签名参数:" + string1); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes("UTF-8")); signature = byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ret.put("url", url); ret.put("jsapi_ticket", jsapi_ticket); ret.put("nonceStr", nonce_str); ret.put("timestamp", timestamp); ret.put("signature", signature); return ret; } private static String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("x", b); } String result = formatter.toString(); formatter.close(); return result; } private static String create_nonce_str() { return UUID.randomUUID().toString(); } private static String create_timestamp() { return Long.toString(System.currentTimeMillis() / 1000); } } http请求或https请求工具类 import org.apache.commons.lang3.StringUtils; import javax.net.ssl.*; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; public class HttpRequesUtil { /** * http请求或https请求 * * @param isSsl * @param url 请求地址 必传 * @param param 请求参数 可以为空 * @param requestMethod 请求方式:GET or POST 必传 * @return 返回请求报文 * @throws Exception */ public static String sendHttpRequest(boolean isSsl, String url, String param, String requestMethod) throws Exception { int timeOut = 60000; PrintWriter out = null; BufferedReader in = null; URL targetUrl = null; StringBuffer result = new StringBuffer(); try { if (isSsl) { // 信任所有证书 targetUrl = new URL(url); ignoreSsl(); } else { targetUrl = new URL(url); } // 打开和URL之间的连接 HttpURLConnection httpConnection = (HttpURLConnection) targetUrl.openConnection(); // 设置超时 httpConnection.setConnectTimeout(timeOut); httpConnection.setReadTimeout(timeOut); // 设置通用的请求属性 httpConnection.setRequestProperty("connection", "Keep-Alive"); httpConnection.setRequestProperty("Charset", "UTF-8"); httpConnection.setRequestProperty("Content-Type", "application/json"); // 发送POST请求 httpConnection.setRequestMethod(requestMethod); httpConnection.setDoOutput(true); httpConnection.setDoInput(true); out = new PrintWriter(httpConnection.getOutputStream()); if (StringUtils.isNotBlank(param)) { out.print(param); } out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader(new InputStreamReader(httpConnection.getInputStream(), "UTF-8")); String line; while ((line = in.readLine()) != null) { result.append(line); } } //使用finally块来关闭输出流、输入流 finally { if (out != null) { try { out.close(); } catch (Exception e) { } } if (in != null) { try { in.close(); } catch (Exception e) { } } } return result.toString(); } private static void trustAllHttpsCertificates() throws Exception { TrustManager[] trustAllCerts = new TrustManager[1]; TrustManager tm = new GetOriginalData.miTM(); trustAllCerts[0] = tm; SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); } /** * 忽略HTTPS请求的SSL证书,必须在openConnection之前调用 * * @throws Exception */ public static void ignoreSsl() throws Exception { HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String urlHostName, SSLSession session) { return true; } }; trustAllHttpsCertificates(); HttpsURLConnection.setDefaultHostnameVerifier(hv); } static class miTM implements TrustManager, X509TrustManager { public X509Certificate[] getAcceptedIssuers() { return null; } public boolean isServerTrusted(X509Certificate[] certs) { return true; } public boolean isClientTrusted(X509Certificate[] certs) { return true; } public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { return; } } } TokenJson实体类 public class TokenJson { private String access_token; private int expires_in; private String errcode; private String errmsg; public String getErrcode() { return errcode; } public void setErrcode(String errcode) { this.errcode = errcode; } public String getErrmsg() { return errmsg; } public void setErrmsg(String errmsg) { this.errmsg = errmsg; } public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public int getExpires_in() { return expires_in; } public void setExpires_in(int expires_in) { this.expires_in = expires_in; } } TicketJson实体类 public class TicketJson { private int errcode; private String errmsg; private String ticket; private String expires_in; public int getErrcode() { return errcode; } public void setErrcode(int errcode) { this.errcode = errcode; } public String getErrmsg() { return errmsg; } public void setErrmsg(String errmsg) { this.errmsg = errmsg; } public String getTicket() { return ticket; } public void setTicket(String ticket) { this.ticket = ticket; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expires_in) { this.expires_in = expires_in; } }