时间:2022-08-06 21:27:01 | 来源:网站运营
时间:2022-08-06 21:27:01 来源:网站运营
阿里云部署的Java网站和微信开发调试心得技巧(下) 五,微信测试号的申请与连接以电子杂志微信用户信息 在咱们自己的程序里面关系编写的servlet以响应微信号 在接下来的步骤中,我们将在测试号里面设置接口配置信息的网址,一经设置,微信公众号便会发请求到我们设置好的URL去,我们必须编写程序应答才能顺利连通微信公众号,因此咱们需要关系编写相应的响应程序 需要关系编写类两个 【SignUtil】package com.imooc.o2o.util.weixin;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;/** * 微信请求校验工具类 */public class SignUtil { // 与接口配置信息中的Token要一致 private static String token = "myo2o"; /** * 验证签名 * * @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); 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; }}
【WechatController】package com.imooc.o2o.web.wechat;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import com.imooc.o2o.util.wechat.SignUtil;@Controller//一会在设置的URL里面就设置上这个路由@RequestMapping("wechat")public class WechatController { private static Logger log = LoggerFactory.getLogger(WechatController.class); @RequestMapping(method = { RequestMethod.GET }) public void doGet(HttpServletRequest request, HttpServletResponse response) { log.debug("weixin get..."); // 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 String signature = request.getParameter("signature"); // 时间戳 String timestamp = request.getParameter("timestamp"); // 随机数 String nonce = request.getParameter("nonce"); // 随机字符串 String echostr = request.getParameter("echostr"); // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 PrintWriter out = null; try { out = response.getWriter(); if (SignUtil.checkSignature(signature, timestamp, nonce)) { log.debug("weixin get success...."); out.print(echostr); } } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) out.close(); } }}
之后重新部署一版最新的程序package com.imooc.o2o.web.wechat;import java.io.IOException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import com.imooc.o2o.dto.UserAccessToken;import com.imooc.o2o.dto.WechatUser;import com.imooc.o2o.util.wechat.WeiXinUserUtil;@Controller@RequestMapping("wechatlogin")/** * 获取关注公众号之后的微信用户信息的接口,如果在微信浏览器里访问 * https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的appId&redirect_uri=http://o2o.yitiaojieinfo.com/o2o/wechatlogin/logincheck&role_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect * 则这里将会获取到code,之后再可以通过code获取到access_token 进而获取到用户信息 * * @author xiangze * */public class WechatLoginController { private static Logger log = LoggerFactory.getLogger(WechatLoginController.class); @RequestMapping(value = "/logincheck", method = { RequestMethod.GET }) public String doGet(HttpServletRequest request, HttpServletResponse response) { log.debug("weixin login get..."); // 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息 String code = request.getParameter("code"); // 这个state可以用来传我们自定义的信息,方便程序调用,这里也可以不用 // String roleType = request.getParameter("state"); log.debug("weixin login code:" + code); WechatUser user = null; String openId = null; if (null != code) { UserAccessToken token; try { // 通过code获取access_token token = WeiXinUserUtil.getUserAccessToken(code); log.debug("weixin login token:" + token.toString()); // 通过token获取accessToken String accessToken = token.getAccessToken(); // 通过token获取openId openId = token.getOpenId(); // 通过access_token和openId获取用户昵称等信息 user = WeiXinUserUtil.getUserInfo(accessToken, openId); log.debug("weixin login user:" + user.toString()); request.getSession().setAttribute("openId", openId); } catch (IOException e) { log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString()); e.printStackTrace(); } } // ======todo begin====== // 前面咱们获取到openId后,可以通过它去数据库判断该微信帐号是否在我们网站里有对应的帐号了, // 没有的话这里可以自动创建上,直接实现微信与咱们网站的无缝对接。 // ======todo end====== if (user != null) { // 获取到微信验证的信息后返回到指定的路由(需要自己设定) return "frontend/index"; } else { return null; } }}
【UserAccessToken】用户的accessToken实体类,用来接收的accessToken以及OpenID的等信息package com.imooc.o2o.dto;import com.fasterxml.jackson.annotation.JsonProperty;/** * 用户授权token * * @author xiangze * */public class UserAccessToken { // 获取到的凭证 @JsonProperty("access_token") private String accessToken; // 凭证有效时间,单位:秒 @JsonProperty("expires_in") private String expiresIn; // 表示更新令牌,用来获取下一次的访问令牌,这里没太大用处 @JsonProperty("refresh_token") private String refreshToken; // 该用户在此公众号下的身份标识,对于此微信号具有唯一性 @JsonProperty("openid") private String openId; // 表示权限范围,这里可省略 @JsonProperty("scope") private String scope; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public String getExpiresIn() { return expiresIn; } public void setExpiresIn(String 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; } @Override public String toString() { return "accessToken:" + this.getAccessToken() + ",openId:" + this.getOpenId(); }}
【WechatUser】微信用户实体类,用来接收昵称openid等用户信息package com.imooc.o2o.dto;import java.io.Serializable;import com.fasterxml.jackson.annotation.JsonProperty;/** * 微信用户实体类 * * @author xiangze * */public class WechatUser implements Serializable { /** * */ private static final long serialVersionUID = -4684067645282292327L; // openId,标识该公众号下面的该用户的唯一Id @JsonProperty("openid") private String openId; // 用户昵称 @JsonProperty("nickname") private String nickName; // 性别 @JsonProperty("sex") private int sex; // 省份 @JsonProperty("province") private String province; // 城市 @JsonProperty("city") private String city; // 区 @JsonProperty("country") private String country; // 头像图片地址 @JsonProperty("headimgurl") private String headimgurl; // 语言 @JsonProperty("language") private String language; // 用户权限,这里没什么作用 @JsonProperty("privilege") private String[] privilege; public String getOpenId() { return openId; } public void setOpenId(String openId) { this.openId = openId; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getHeadimgurl() { return headimgurl; } public void setHeadimgurl(String headimgurl) { this.headimgurl = headimgurl; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } public String[] getPrivilege() { return privilege; } public void setPrivilege(String[] privilege) { this.privilege = privilege; } @Override public String toString() { return "openId:" + this.getOpenId() + ",nikename:" + this.getNickName(); }}
【WechatUtil】主要用来提交HTTPS请求给微信获取用户信息package com.imooc.o2o.util.wechat;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.ConnectException;import java.net.URL;import javax.net.ssl.HttpsURLConnection;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSocketFactory;import javax.net.ssl.TrustManager;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.fasterxml.jackson.core.JsonParseException;import com.fasterxml.jackson.databind.JsonMappingException;import com.fasterxml.jackson.databind.ObjectMapper;import com.imooc.o2o.dto.UserAccessToken;import com.imooc.o2o.dto.WechatUser;/** * 微信工具类 * * @author xiangze * */public class WechatUtil { private static Logger log = LoggerFactory.getLogger(WechatUtil.class); /** * 获取UserAccessToken实体类 * * @param code * @return * @throws IOException */ public static UserAccessToken getUserAccessToken(String code) throws IOException { // 测试号信息里的appId String appId = "您的appId"; log.debug("appId:" + appId); // 测试号信息里的appsecret String appsecret = "您的appsecret"; log.debug("secret:" + appsecret); // 根据传入的code,拼接出访问微信定义好的接口的URL String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret + "&code=" + code + "&grant_type=authorization_code"; // 向相应URL发送请求获取token json字符串 String tokenStr = httpsRequest(url, "GET", null); log.debug("userAccessToken:" + tokenStr); UserAccessToken token = new UserAccessToken(); ObjectMapper objectMapper = new ObjectMapper(); try { // 将json字符串转换成相应对象 token = objectMapper.readValue(tokenStr, UserAccessToken.class); } catch (JsonParseException e) { log.error("获取用户accessToken失败: " + e.getMessage()); e.printStackTrace(); } catch (JsonMappingException e) { log.error("获取用户accessToken失败: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { log.error("获取用户accessToken失败: " + e.getMessage()); e.printStackTrace(); } if (token == null) { log.error("获取用户accessToken失败。"); return null; } return token; } /** * 获取WechatUser实体类 * * @param accessToken * @param openId * @return */ public static WechatUser getUserInfo(String accessToken, String openId) { // 根据传入的accessToken以及openId拼接出访问微信定义的端口并获取用户信息的URL String url = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN"; // 访问该URL获取用户信息json 字符串 String userStr = httpsRequest(url, "GET", null); log.debug("user info :" + userStr); WechatUser user = new WechatUser(); ObjectMapper objectMapper = new ObjectMapper(); try { // 将json字符串转换成相应对象 user = objectMapper.readValue(userStr, WechatUser.class); } catch (JsonParseException e) { log.error("获取用户信息失败: " + e.getMessage()); e.printStackTrace(); } catch (JsonMappingException e) { log.error("获取用户信息失败: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { log.error("获取用户信息失败: " + e.getMessage()); e.printStackTrace(); } if (user == null) { log.error("获取用户信息失败。"); return null; } return user; } /** * 发起https请求并获取结果 * * @param requestUrl * 请求地址 * @param requestMethod * 请求方式(GET、POST) * @param outputStr * 提交的数据 * @return json字符串 */ public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) { StringBuffer buffer = new StringBuffer(); 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 httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 设置请求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // 当有数据需要提交时 if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // 注意编码格式,防止中文乱码 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 将返回的输入流转换成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 释放资源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); log.debug("https buffer:" + buffer.toString()); } catch (ConnectException ce) { log.error("Weixin server connection timed out."); } catch (Exception e) { log.error("https request error:{}", e); } return buffer.toString(); }}
【MyX509TrustManager】主要继承X509TrustManager做HTTPS证书信任管理器package com.imooc.o2o.util.wechat;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;import javax.net.ssl.X509TrustManager;/** * 证书信任管理器(用于https请求) * * @author xiangze * */public class MyX509TrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null; }}
之后重新打包一个新的war包并发到服务器tomcat webapps目录下 发布成功后,关注你自己的测试号(即扫描测试号的那个二维码),然后在手机微信里面或者微信开发者工具里访问相应链接: https://open.weixin.qq.com/connect/oauth2/authorize?appid=您的appid&redirect_uri = http://o2o.yitiaojieinfo.com/o2o/wechatlogin/logincheck&role_type=1&response_type=code&scope=snsapi_userinfo&state=1 #wechat_redirect 关键词:技巧,部署