微信JSSDK使用
完整的使用方法,请耐心查看。
前言
我也是在一次偶然的项目中碰到了这个需求,所以自己研究了一下,看了不少大佬的文章总结归纳了一下,我反正是好使了,到你那能不能用就看天命了。对了在使用一下代码之前,千万别忘了,不管是你自己还是你的经理谁都好,把你的 IP 添加到微信公众号开发的白名单里!!!!
1、获取access_token
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。
但是,获取jsapi_ticket之前必须先获取access_token,下面就让我们获取一下这个:
/**
* 获取接口访问凭证
*
* @return
*/
@Scheduled(fixedRate = 1000*7200)
private static AccessToken getAccessToken()
{
String requestUrl = String.format(BasicSysCodeService.getAccessTokenUrl(),
BasicSysCodeService.getAppId(),
BasicSysCodeService.getSecret());
// 发起GET请求获取凭证
JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
if (null != jsonObject)
{
try
{
accessToken = new AccessToken();
accessToken.setAccessToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
}
catch (Exception e)
{
accessToken = null;
// 获取token失败
log.error(e.getMessage());
}
}
return accessToken;
}
其中@Scheduled(fixedRate = 1000*7200)表示每2个小时获取一次,因为微信官网中access_token失效的时间是两个小时。
当然获取access_token之前你先要指导自己的appId和secret这两个自己去微信公众号里获取就好。
实在不懂的可以来这里看看:https://jingyan.baidu.com/article/22fe7ced23fa183002617fa1.html
2、编写获取配置信息的类
我们不可能每次写这个的时候都改一次代码,这样是会被祭天的,所以下面的方法可以是代码稍微灵活一些。
package com.zhonghe.yaoqinghan.user.entity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author 阿萨
*/
@Component
@ConfigurationProperties(prefix = "basicsyscodeservice")
public class BasicSysCodeService
{
/**
* 获取access_token请求的url
*/
private static String accessTokenUrl;
private static String appId;
private static String secret;
private static String jsapiTicketUrl;
private static String yqhUrl;
public static String getYqhUrl() {
return yqhUrl;
}
@Value("${basicsyscodeservice.yqhUrl}")
public void setYqhUrl(String yqhUrl) {
BasicSysCodeService.yqhUrl = yqhUrl;
}
public static String getJsapiTicketUrl() {
return jsapiTicketUrl;
}
@Value("${basicsyscodeservice.jsapiTicketUrl}")
public void setJsapiTicketUrl(String jsapiTicketUrl) {
BasicSysCodeService.jsapiTicketUrl = jsapiTicketUrl;
}
public static String getAccessTokenUrl()
{
return accessTokenUrl;
}
@Value("${basicsyscodeservice.accessTokenUrl}")
public void setAccessTokenUrl(String accessTokenUrl)
{
BasicSysCodeService.accessTokenUrl = accessTokenUrl;
}
public static String getAppId()
{
return appId;
}
@Value("${basicsyscodeservice.appId}")
public void setAppId(String appId)
{
BasicSysCodeService.appId = appId;
}
public static String getSecret()
{
return secret;
}
@Value("${basicsyscodeservice.secret}")
public void setSecret(String secret)
{
BasicSysCodeService.secret = secret;
}
}
再项目的appliation.yml文件中添加以下信息,具体内容根据项目需要来填写。
basicsyscodeservice:
# 获取jsapi_ticket的接口链接
jsapiTicketUrl: https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=%s
# 获取access_token的接口链接
accessTokenUrl: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s
# 微信公众号里提供 需要这个才能获取access_token
appId: 自己的qppid
secret: 自己的secret
# 网页链接(可以没有)
yqhUrl: 自己的网站链接
3、别忘了写实体类
这里我把需要的实体类全部都放在这了,左右都得建,干脆一次整完。
access_token的实体类
package com.zhonghe.yaoqinghan.user.entity;
import lombok.Data;
/**
* @author 阿萨
*/
public class AccessToken {
private String accessToken;
private Integer expiresIn;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public Integer getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Integer expiresIn) {
this.expiresIn = expiresIn;
}
}
jsapi_ticket实体类
package com.zhonghe.yaoqinghan.user.entity;
/**
* @author 阿萨
*/
public class JsapiTicket {
private Integer errCode;
private String errMsg;
private String ticket;
private Integer expiresIn;
public Integer getErrCode() {
return errCode;
}
public void setErrCode(Integer 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 Integer getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Integer expiresIn) {
this.expiresIn = expiresIn;
}
@Override
public String toString() {
return "JsapiTicket{" +
"errCode=" + errCode +
", errMsg='" + errMsg + '\'' +
", ticket='" + ticket + '\'' +
", expiresIn=" + expiresIn +
'}';
}
}
这是最后返回给前台的实体类。
package com.zhonghe.yaoqinghan.user.entity;
import lombok.Data;
import org.springframework.stereotype.Component;
@Data
public class WXEntity {
//appid
private String appId;
//时间戳
private String timestamp;
//随机数
private String nonceStr;
//签名
private String signature;
//url
private String url;
}
4、获取jsapi_ticket
在上面我们获取完了access_token下面我们改获取重要得jsapi_ticket了,废话不多说上代码。
这个jsapi_ticket也是2个小时获取一次,不多解释了,别问,问就是官方文档这么写的。
/**
* 获取Jsapi_ticke
*
* @return
*/
@Scheduled(initialDelay=1000,fixedRate = 1000*7200)
private static JsapiTicket getJsapiTicket()
{
String requestUrl = String.format(BasicSysCodeService.getJsapiTicketUrl(),
accessToken.getAccessToken());
// 发起GET请求获取凭证
JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
if (null != jsonObject)
{
try
{
jsapiTicket = new JsapiTicket();
jsapiTicket.setErrCode(jsonObject.getInt("errcode"));
jsapiTicket.setErrMsg(jsonObject.getString("errmsg"));
jsapiTicket.setTicket(jsonObject.getString("ticket"));
jsapiTicket.setExpiresIn(jsonObject.getInt("expires_in"));
}
catch (Exception e)
{
jsapiTicket = null;
// 获取token失败
log.error(e.getMessage());
}
}
return jsapiTicket;
}
这样我们就获取到了jaspi_ticket了。
5、不要问问我httpsRequest()从哪里来,我给你。
这就是httpsRequest()方法了,请拿去玩耍。
对了,这个方法是我在这个大佬里看到的:https://blog.csdn.net/dfgakldsjkl/article/details/99671431?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param
链接真的长。。。。。。
/**
* 发送https请求
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
private static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr)
{
JSONObject jsonObject = null;
try
{
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new MyX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 当outputStr不为null时向输出流写数据
if (null != outputStr)
{
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null)
{
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
}
catch (ConnectException ce)
{
log.error("连接超时:{}", ce);
}
catch (Exception e)
{
log.error("https请求异常:{}", e);
}
System.out.println(jsonObject);
return jsonObject;
}
以上我们就获取到了access_token和jsapi_ticket下面我们就该生成我们的签名了!!!!!
6、生成签名
1,生成随机数、时间戳和获取url
我的随机数就偷懒用UUID了,你们也可以自己写。
/**获取随机数*/
public static String getNoncestr(){
return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
}
/**获取时间戳*/
public static String getTimestamp(){
return String.valueOf(System.currentTimeMillis() / 1000);
}
/**获取url*/
public static String getUrl(){
return BasicSysCodeService.getYqhUrl();
}
2,根据微信文档按顺序拼接字符串
注意:一定要注意,拼接顺序不可变,除非是官方文档里改了,不然拼接顺序一定不要改变!!!!!!!!!!
/**
* @Param [signature, timestamp, ticket, echostr]
* @Return java.lang.String
* @Description: 对所有待签名参数按照字段名的ASCII 码从小到大排序
*/
public static String sort(String url, String timestamp, String ticket, String noncestr) {
return "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;
}
3,将拼接后的字符串进行SHA1加密
这个咱也不知道为啥,反正官方文档里让这么干,咱就这么干。
/**
* @Param [str]
* @Return java.lang.String
* @Description: 将字符串进行sha1加密
*/
public static String sha1(String str){
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.getBytes());
byte[] messageDigest = digest.digest();
// 创建 16进制字符串
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
7、Controller里怎么用!!!
这里就不多做解释了,就是调用工具类,在最后我会把工具类分享给大家。
@RequestMapping("/token")
public Result token(){
WXEntity wxEntity = new WXEntity();
//获取appid
//String appId = BasicSysCodeService.getAppId();
wxEntity.setAppId(BasicSysCodeService.getAppId());
//获取时间戳
String timestamp = CommonUtil.getTimestamp();
wxEntity.setTimestamp(timestamp);
//获取随机数
String nonceStr = CommonUtil.getNoncestr();
wxEntity.setNonceStr(nonceStr);
//获取jsapi_ticket码
String ticket = CommonUtil.jsapiTicket.getTicket();
//获取网页连接
String url = CommonUtil.getUrl();
wxEntity.setUrl(url);
//拼接
String str = CommonUtil.sort(url,timestamp,ticket,nonceStr);
//获取密钥
String signature = CommonUtil.sha1(str);
wxEntity.setSignature(signature);
return Result.ok(wxEntity);
}
最后献上完整的工具类。
package com.zhonghe.yaoqinghan.utils;
import com.zhonghe.yaoqinghan.user.entity.AccessToken;
import com.zhonghe.yaoqinghan.user.entity.BasicSysCodeService;
import com.zhonghe.yaoqinghan.user.entity.JsapiTicket;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
/**
*
* <p>
* Description: 微信工具类
* </p>
*
* @author 阿萨
* @version v1.0.0
* @since 2020-08-13 13:09:05
* @see com.zhonghe.yaoqinghan.utils
*
*/
@Component
public class CommonUtil
{
private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
public static AccessToken accessToken = null;
public static JsapiTicket jsapiTicket = null;
/**
* 获取接口访问凭证
*
* @return
*/
@Scheduled(fixedRate = 1000*7200)
private static AccessToken getAccessToken()
{
String requestUrl = String.format(BasicSysCodeService.getAccessTokenUrl(),
BasicSysCodeService.getAppId(),
BasicSysCodeService.getSecret());
// 发起GET请求获取凭证
JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
if (null != jsonObject)
{
try
{
accessToken = new AccessToken();
accessToken.setAccessToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
}
catch (Exception e)
{
accessToken = null;
// 获取token失败
log.error(e.getMessage());
}
}
return accessToken;
}
/**
* 获取Jsapi_ticke
*
* @return
*/
@Scheduled(initialDelay=1000,fixedRate = 1000*7200)
private static JsapiTicket getJsapiTicket()
{
String requestUrl = String.format(BasicSysCodeService.getJsapiTicketUrl(),
accessToken.getAccessToken());
// 发起GET请求获取凭证
JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
if (null != jsonObject)
{
try
{
jsapiTicket = new JsapiTicket();
jsapiTicket.setErrCode(jsonObject.getInt("errcode"));
jsapiTicket.setErrMsg(jsonObject.getString("errmsg"));
jsapiTicket.setTicket(jsonObject.getString("ticket"));
jsapiTicket.setExpiresIn(jsonObject.getInt("expires_in"));
}
catch (Exception e)
{
jsapiTicket = null;
// 获取token失败
log.error(e.getMessage());
}
}
return jsapiTicket;
}
/**
* 发送https请求
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
private static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr)
{
JSONObject jsonObject = null;
try
{
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new MyX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 当outputStr不为null时向输出流写数据
if (null != outputStr)
{
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null)
{
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
}
catch (ConnectException ce)
{
log.error("连接超时:{}", ce);
}
catch (Exception e)
{
log.error("https请求异常:{}", e);
}
System.out.println(jsonObject);
return jsonObject;
}
/**获取随机数*/
public static String getNoncestr(){
return UUID.randomUUID().toString().replace("-", "").substring(0, 16);
}
/**获取时间戳*/
public static String getTimestamp(){
return String.valueOf(System.currentTimeMillis() / 1000);
}
/**获取url*/
public static String getUrl(){
return BasicSysCodeService.getYqhUrl();
}
/**
* @Param [signature, timestamp, ticket, echostr]
* @Return java.lang.String
* @Description: 对所有待签名参数按照字段名的ASCII 码从小到大排序
*/
public static String sort(String url, String timestamp, String ticket, String noncestr) {
return "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url=" + url;
}
/**
* @Param [str]
* @Return java.lang.String
* @Description: 将字符串进行sha1加密
*/
public static String sha1(String str){
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
digest.update(str.getBytes());
byte[] messageDigest = digest.digest();
// 创建 16进制字符串
StringBuffer hexString = new StringBuffer();
// 字节数组转换为 十六进制 数
for (int i = 0; i < messageDigest.length; i++) {
String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
if (shaHex.length() < 2) {
hexString.append(0);
}
hexString.append(shaHex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}
在这里对所有对我有帮助的文章和文档表示感谢,感谢大佬,给跪一个。由于文章太多就不把链接都写上了,(os:大佬不要骂我啊,我真的是忘记保存你们链接了,对不起!!!)
最后希望我这篇不成熟的文章可以对大家有所帮助,再次感谢观看。