我们微信公众号使用的是我的测试号,地址https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
1.微信测试号后台配置
注意,这个配置要成功,否则是会显示配置失败的。后台怎么写,看下面
2.后台代码
2.1验证token的代码
控制器里:
@RequestMapping(value = "/wxcheck")
public void check(Model model, HttpServletRequest request, HttpServletResponse response)throws IOException {
boolean isGet = request.getMethod().toLowerCase().equals("get");
PrintWriter print;
if(isGet){
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (signature != null && WechatUtil.checkSignature(signature, timestamp, nonce)) {
try {
print = response.getWriter();
print.write(echostr);
print.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
WechatUtil这个工具类:
public class WechatUtil {
/**
* 与接口配置信息中的Token要一致
*/
private static String token = "xxx";
/**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
// Arrays.sort(arr);
sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
public static void sort(String a[]) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[j].compareTo(a[i]) < 0) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
}
这么一来,微信通过token验证我们的服务器就能通过了。
2.获取微信授权信息
首先按钮的格式:这个是比较麻烦的地方,格式必须一模一样
{
"button": [
{
"type": "click",
"name": "xxx",
"key": "1"
},
{
"name": "常见问题",
"sub_button": [
{
"type": "view",
"name": "xxx",
"url": "http://www.xxx"
},
{
"type": "view",
"name": "xxx",
"url": "http://www.xxx"
},
{
"type": "view",
"name": "xxx",
"url": "http://www.xxx"
}
]
},
{
"name":"个人中心",
"sub_button":[
{
"type": "view",
"name": "测试",
"url" : "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxXXXXX95&redirect_uri=http%3A%2F%2FXXXXXXXXXXcom%2Flxxxxxxe%2Fauth&response_type=code&scope=snsapi_base&state=123#wechat_redirect"
},
{
"type": "click",
"name": "我要提问",
"key" : "103"
}
]
}
]
}
我使用的是基本方法不获取userinfor,是默认同意授权的,控制器里的写法
@RequestMapping(value = "/auth")
public void wxAuth(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
// 用户同意授权后,能获取到code
String code = request.getParameter("code");
String state = request.getParameter("state");
String url= "";
// 用户同意授权
if (!"authdeny".equals(code)) {
// 获取网页授权access_token
WeixinOauth2Token weixinOauth2Token = WechatUtil.getOauth2AccessToken("id", "密码", code);
// 网页授权接口访问凭证
String accessToken = weixinOauth2Token.getAccessToken();
// 用户标识
String openId = weixinOauth2Token.getOpenId();
String ip = IpUtil.getIpAddr(request);
WxOpenid wxOpenid = new WxOpenid();
wxOpenid.setIp(ip);
wxOpenid.setCreatTime(new Date());
wxOpenid.setOpenid(openId);
int i = wxOpenidService.insertSelective(wxOpenid);
System.out.println(">>>>>>>>>>>>>>>>>>>>"+ip);
// // 获取用户信息
// SNSUserInfo snsUserInfo = AdvancedUtil.getSNSUserInfo(accessToken, openId);
// 设置要传递的参数
// request.setAttribute("snsUserInfo", snsUserInfo);
// request.setAttribute("state", state);
}
url = "http://xxx/xxx/";
// 跳转到xxx前台
response.sendRedirect(url);
}
用到的工具类:
/**
* 获取网页授权凭证
*
* @param appId 公众账号的唯一标识
* @param appSecret 公众账号的密钥
* @param code
* @return WeixinAouth2Token
*/
public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
WeixinOauth2Token wat = new WeixinOauth2Token();
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", appId);
requestUrl = requestUrl.replace("SECRET", appSecret);
requestUrl = requestUrl.replace("CODE", code);
// 获取网页授权凭证
JSONObject jsonObject = HttpCommonUtil.httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
try {
wat.setAccessToken(jsonObject.getString("access_token"));
wat.setExpiresIn(jsonObject.getInteger("expires_in"));
wat.setRefreshToken(jsonObject.getString("refresh_token"));
wat.setOpenId(jsonObject.getString("openid"));
wat.setScope(jsonObject.getString("scope"));
} catch (Exception e) {
// wat = null;
// int errorCode = jsonObject.getInteger("errcode");
// String errorMsg = jsonObject.getString("errmsg");
// log.error("获取网页授权凭证失败 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return wat;
}
还有一个网页授权信息的实体类:
/**
* 类名: WeixinOauth2Token </br>
* 描述: 网页授权信息 </br>
* 开发人员: fr</br>
* 发布版本:V1.0 </br>
*/
public class WeixinOauth2Token {
// 网页授权接口调用凭证
private String accessToken;
// 凭证有效时长
private int expiresIn;
// 用于刷新凭证
private String refreshToken;
// 用户标识
private String openId;
// 用户授权作用域
private String scope;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
还有一个发送http请求的工具类
package com.wolwo.base.util;
import com.alibaba.fastjson.JSONObject;
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.SecureRandom;
import javax.net.ssl.*;
public class HttpCommonUtil {
/**
* 发送https请求
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public 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 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.parseObject(buffer.toString());
} catch (ConnectException ce) {
// log.error("连接超时:{}", ce);
} catch (Exception e) {
// log.error("https请求异常:{}", e);
}
return jsonObject;
}
}
package com.wolwo.base.util;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* 类名: MyX509TrustManager </br>
* 描述:信任管理器 </br>
* 开发人员: fr</br>
* 发布版本:V1.0 </br>
*/
public class MyX509TrustManager implements X509TrustManager {
// 检查客户端证书
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 检查服务器端证书
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
// 返回受信任的X509证书数组
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
IpUtil.java
package com.wolwo.base.util;
import javax.servlet.http.HttpServletRequest;
public class IpUtil {
/**
* 获取登录用户IP地址
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (ip.equals("0:0:0:0:0:0:0:1")) {
ip = "本地";
}
return ip;
}
}
WxOpenid.java
package com.wolwo.wx.domain;
import java.sql.Timestamp;
import java.util.Date;
public class WxOpenid {
private String ip;
private String openid;
private Date createTime;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}
这么一来就行了