1、首先需要去微信申请账号
2、微信会分配appid和secret
3、由于微信的接口需要获取token才行
4、获取token的URL:get方式请求(https请求)
https://api.weixin.qq.com/cgi-bin/token
5、获取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 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 getSign(String url) {
Map 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 sign(String jsapi_ticket, String url) {
Map ret = new HashMap();
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("%02x", 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;
}
}