流程:

  1. 前端请求后台接口,传入url
  2. 后端接收请求,处理业务逻辑,返回签名等参数

    1.获取token
    2.拿token获取ticket
    3.获取随机字符串(用自带的UUID就行)
    4.获取当前时间戳,记得除以1000(10位数) 例如:1626139722
    5.拼接参数(url是前端传过来的)

    1
    String context = "jsapi_ticket=xxxx&noncestr=xxxx&timestamp=xxxx&url=xxxx"

    6.参数SHA1加密
    7.封装,返回前端

分步骤解析:

如果不想了解,直接去最下面Cope完整代码

获取token

appid、secret、grant_type这三个值都是固定值
前两个值是微信公众号开发者平台上的凭据,AppId 和 AppSecret
微信公众平台凭据调试工具:微信公众平台接口调试工具
appid:公众号的id
secret:公众号的秘钥
grant_type:授权码发放类型(固定值:client_credential) grant_type=client_credential

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

private static JsonParser parse = new JsonParser();

/**
* 获取 token
* @return
*/
public static String getToken() {
// https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxxx&secret=xxxx
String url = ACCESS_TOKEN_URL + "?grant_type=" + GRANT_TYPE + "&appid=" + APPID + "&secret=" + SECRET;
//请求链接获取token,这个httpGetForJson()方法去完整代码中找
String response = httpGetForJson(url);
return ((JsonObject) parse.parse(response)).get(ACCESS_TOKEN_KEY).getAsString();
}

拿token获取ticket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

private static JsonParser parse = new JsonParser();

/**
* 获取 ticket
* @param token
* @return
*/
public static String getTicket(String token) {
// https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=xxxx&type=jsapi
String url = TICKET_URL + "?access_token=" + token + "&type=" + TYPE;
//请求链接获取token,这个httpGetForJson()方法去完整代码中找
String response = httpGetForJson(url);
return ((JsonObject) parse.parse(response)).get(TICKET_KEY).getAsString();
}

获取随机字符串

用自带的UUID就行

1
2
import java.util.UUID;
String nonceStr = UUID.randomUUID().toString();

获取当前时间戳

记得除以1000(10位数) 例如:1626139722

1
String timestamp = Long.toString(System.currentTimeMillis() / 1000);

拼接参数,并加密

微信 JS 接口签名校验工具:【微信 JS 接口签名校验工具

1
2
3
4
// 值是前几步骤中获取到的,url是前端传过来的
String context = "jsapi_ticket=xxxx&noncestr=xxxx&timestamp=xxxx&url=xxxx";
//加密
String signature = SHA1(context);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* SHA1加密
* @param value
* @return
*/
public static String SHA1(final String value) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
digest.update(value.getBytes());
byte messageDigest[] = digest.digest();
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 "";
}

完整代码:

直接拿来用,前端传入url直接请求
返回类型自己封装个就行,不一定直接返回Map<String, String>
WxSignatureUtil是自己封装的微信生成签名工具类
微信 JS 接口签名校验工具:【微信 JS 接口签名校验工具


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import com.smart.wechat.config.WxSignatureUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

@RestController
@RequestMapping("/wx/config")
public class WxConfigController {

private final Logger logger = LoggerFactory.getLogger(this.getClass());

/**
* 获取微信配置
* params:前端传过来的,包含url
*/
@RequestMapping("/gerWxConfig")
public Map<String, String> gerWxConfig(@RequestBody Map<String,Object> params) {
try {
String url = String.valueOf(params.get("url"));
Map<String, String> returnMap = WxSignatureUtil.getWechatSignature(url);
return returnMap;
} catch (Exception e) {
return null;
}
}


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Component
public class WxSignatureUtil {

/**
* 开发者ID,必填
* 微信公众号开发者平台上的凭据 AppId
*/
private static fianl String APPID = "xxxxxxxx";
/**
* 开发者秘钥,必填
* 微信公众号开发者平台上的凭据 AppSecret
*/
private static fianl String SECRET = "xxxxxxxx";
/**
* 授权码发放类型
*/
private static final String GRANT_TYPE = "client_credential";
/**
* 授权码类型
*/
private static final String TYPE = "jsapi";
/**
* 获取微信 TOKEN 接口
*/
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
/**
* 获取微信令牌接口
*/
private static final String TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
/**
* 请求参数名
*/
private static final String ACCESS_TOKEN_KEY = "access_token";
/**
* TICKET_KEY
*/
private static final String TICKET_KEY = "ticket";
/**
* JSON 解析
*/
private static JsonParser parse = new JsonParser();

/**
* 获取签名
* 注意 URL 一定要动态获取,不能 hardcode
* @param url
* @return
*/
public static Map<String, String> getWechatSignature(String url) {
// 获取token
String token = getToken();
// 获取ticket
String ticket = getTicket(token);
// 随机字符串
String nonceStr = UUID.randomUUID().toString();
// 时间戳
String timestamp = Long.toString(System.currentTimeMillis() / 1000);
//注意这里参数名必须全部小写,且必须有序
String context = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
//加密
String signature = SHA1(context);
//封装
Map<String, String> result = new HashMap<String, String>();
result.put("url", url);
result.put("jsapi_ticket", ticket);
result.put("nonceStr", nonceStr);
result.put("timestamp", timestamp);
result.put("signature", signature);
result.put("appId", APPID);
return result;
}

/**
* 获取 token
* @return
*/
public static String getToken() {
// https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=&secret=xxxxxxxxx
String url = ACCESS_TOKEN_URL + "?grant_type=" + GRANT_TYPE + "&appid=" + APPID + "&secret=" + SECRET;
//请求链接获取token
String response = httpGetForJson(url);
return ((JsonObject) parse.parse(response)).get(ACCESS_TOKEN_KEY).getAsString();
}

/**
* 获取 ticket
* @param token
* @return
*/
public static String getTicket(String token) {
// https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=xxxx&type=jsapi
String url = TICKET_URL + "?access_token=" + token + "&type=" + TYPE;
//请求链接获取token
String response = httpGetForJson(url);
return ((JsonObject) parse.parse(response)).get(TICKET_KEY).getAsString();
}

/**
* SHA1加密
* @param value
* @return
*/
public static String SHA1(final String value) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
digest.update(value.getBytes());
byte messageDigest[] = digest.digest();
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 "";
}

/**
* 发送请求
* @param url
* @return
*/
public static String httpGetForJson(String url) {
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
// 创建 HTTPGET
HttpGet httpget = new HttpGet(url);
// 发送 GET 请求
CloseableHttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
return entity == null ? "" : EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return "";
}
}

总结

需要前端多配合,校验工具不一定准确,仅供参考,避坑!!!

注意:还有一个前端的坑:微信公众平台得先添加配置(JS接口安全域名),微信扫一扫才能拉起来