需求:客户登录微信公众号进行账号注册,绑定微信openid,下次根据openid验证自动登录。
详细代码:/download/yaya_jn/74920268
一、微信公众平台配置。
1、配置业务域名
2、查看公众号appid
3、开通获取用户基本信息的权限
二、接口开发对接
1、微信用户实体类,用来接收昵称 openid等用户信息
import com.fasterxml.jackson.annotation.JsonProperty;import java.io.Serializable;/*** 微信用户实体类*/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;}@Overridepublic String toString() {return "openId:" + this.getOpenId() + ",nikename:" + this.getNickName();}}
2、微信工具类
import com.fasterxml.jackson.core.JsonParseException;import com.fasterxml.jackson.databind.JsonMappingException;import com.fasterxml.jackson.databind.ObjectMapper;import .ssl.HttpsURLConnection;import .ssl.SSLContext;import .ssl.SSLSocketFactory;import .ssl.TrustManager;import java.io.*;import .ConnectException;import .URL;/*** 微信工具类*/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 {// 测试号信息里的appIdString appId = "wxde2a7fa4a2f8cf81";//String appId = "wx7bb68b0432c87b0e";log.debug("appId:" + appId);// 测试号信息里的appsecretString appsecret = "aabd5ba75129c3986b544e1a925eacd0";//String appsecret = "370bd77c35322176f3410809d7a56c3c";log.debug("secret:" + appsecret);// 根据传入的code,拼接出访问微信定义好的接口的URLString url = "https://api./sns/oauth2/access_token?appid=" + appId + "&secret=" + appsecret+ "&code=" + code + "&grant_type=authorization_code";System.out.println("getUserAccessToken ---------------------------------------------------------------------------"+url);// 向相应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拼接出访问微信定义的端口并获取用户信息的URLString url = "https://api./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();}}
3、用户AccessToken实体类,用来接收accesstoken以及openid等信息
import com.fasterxml.jackson.annotation.JsonProperty;/*** 用户授权token**/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;}@Overridepublic String toString() {return "accessToken:" + this.getAccessToken() + ",openId:" + this.getOpenId();}}
4、证书信任管理器(用于https请求)
import .ssl.X509TrustManager;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;/*** 证书信任管理器(用于https请求)**/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;}}
5、微信回调接口
import com.ruoyi.project.api.controller.weixin.UserAccessToken;import com.ruoyi.project.api.controller.weixin.WechatUser;import com.ruoyi.project.api.controller.weixin.WechatUtil;import com.ruoyi.project.bis.member.domain.Member;import com.ruoyi.project.bis.member.service.IMemberService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.ui.ModelMap;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** 获取关注公众号之后的微信用户信息的接口,如果在微信浏览器里访问* https://open./connect/oauth2/authorize?appid=您的appId&redirect_uri=/o2o/wechatlogin/logincheck&role_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect* 则这里将会获取到code,之后再可以通过code获取到access_token 进而获取到用户信息*/@Controller@RequestMapping("/api/wechatlogin")public class WechatLoginController {@Autowiredprivate IMemberService memberService;@Autowiredprivate MemberCenterController memberCenterController;//private static Logger log = LoggerFactory.getLogger(WechatLoginController.class);@GetMapping(value = "/logincheck")public String doGet(HttpServletRequest request, HttpServletResponse response,ModelMap modelMap,String userCode) {//log.debug("weixin login get...");modelMap.put("userCode",userCode);System.out.println("weixin login get... --------");// 获取微信公众号传输过来的code,通过code可获取access_token,进而获取用户信息String code = request.getParameter("code");System.out.println("weixin login get... code--------------"+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_tokentoken = WechatUtil.getUserAccessToken(code);//log.debug("weixin login token:" + token.toString());// 通过token获取accessTokenString accessToken = token.getAccessToken();// 通过token获取openIdopenId = token.getOpenId();// 通过access_token和openId获取用户昵称等信息user = WechatUtil.getUserInfo(accessToken, openId);//log.debug("weixin login user:" + user.toString());//request.getSession().setAttribute("openId", openId);modelMap.put("openId",openId);System.out.println("openIdkais---------------"+openId);} catch (IOException e) {//log.error("error in getUserAccessToken or getUserInfo or findByOpenId: " + e.toString());e.printStackTrace();}}else{//memberCenterController.center(openId,modelMap);}// ======todo begin======// 前面咱们获取到openId后,可以通过它去数据库判断该微信帐号是否在我们网站里有对应的帐号了,// 没有的话这里可以自动创建上,直接实现微信与咱们网站的无缝对接。// ======todo end======if (user != null) {// 获取到微信验证的信息后返回到指定的路由(需要自己设定)//Member member = memberService.selectMemberByOpenId(openId);if(member!=null){//return memberCenterController.center(openId,modelMap);}else{return "h5/register";}} else {return "h5/userCenter";}}
}
6、前端界面编辑回调接口
function weixinApply() {//验证是否需要登录window.location.href="https://open./connect/oauth2/authorize?appid=*********&redirect_uri=http://域名/api/wechatlogin/logincheck&role_type=1&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect";}
界面优先加载此方法。
7、请求校验接口
import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;/*** 描述:请求校验工具类.* @author zrxJuly* @createDate -12-9* @since 1.0**/public class SignUtil {// 与开发模式接口配置信息中的Token保持一致.private static String token = "weixinCourse";/*** 校验签名* @param signature 微信加密签名.* @param timestamp 时间戳.* @param nonce 随机数.* @return*/public static boolean checkSignature(String signature, String timestamp, String nonce) {// 对token、timestamp、和nonce按字典排序.String[] paramArr = new String[] {token, timestamp, nonce};Arrays.sort(paramArr);// 将排序后的结果拼接成一个字符串.String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);String ciphertext = null;try {MessageDigest md = MessageDigest.getInstance("SHA-1");// 对拼接后的字符串进行sha1加密.byte[] digest = md.digest(content.toString().getBytes());ciphertext = byteToStr(digest);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}// 将sha1加密后的字符串与signature进行对比.return ciphertext != null ? ciphertext.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;}}
三、电脑线下测试方法。
1、申请公众平台测试账号
2、注册小米球登录小米球Ngrok后台系统,可以将本机配置域名,然后和微信进行通信。
根据windows版本下载运行,解压版本的不需要安装(下载密码:小米球拼音)
注册账号,会获得小米球Token,此处我花了5毛钱实名认证了一下,因为后续连接的时候会连接不上。
按照教程更改token启动小米球获取域名
3、微信测试账号配置及认证
appID:开发者ID,是公众号开发识别码,配合开发者密码可以调用微信公众号接口,如获取微信昵称等
appsecret:开发者密码,是检验公众号开发者身份的密码,具有极高的安全性。切记不要把密码交给第三方开发者或者编写到代码里
【接口配置信息】
URL: 是开发者用来接收微信消息和事件的接口URL,要用域名不能用ip
Token:由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)
4、当你填完URL和Token点击提交后,微信会访问你填写的URL,所以要在后台写一个servlet来处理这个请求(上代码)
(1)、工具类 请求校验,token值和微信配置一致
import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;/*** 描述:请求校验工具类.* @author zrxJuly* @createDate -12-9* @since 1.0**/public class SignUtil {// 与开发模式接口配置信息中的Token保持一致.private static String token = "weixinCourse";/*** 校验签名* @param signature 微信加密签名.* @param timestamp 时间戳.* @param nonce 随机数.* @return*/public static boolean checkSignature(String signature, String timestamp, String nonce) {// 对token、timestamp、和nonce按字典排序.String[] paramArr = new String[] {token, timestamp, nonce};Arrays.sort(paramArr);// 将排序后的结果拼接成一个字符串.String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);String ciphertext = null;try {MessageDigest md = MessageDigest.getInstance("SHA-1");// 对拼接后的字符串进行sha1加密.byte[] digest = md.digest(content.toString().getBytes());ciphertext = byteToStr(digest);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}// 将sha1加密后的字符串与signature进行对比.return ciphertext != null ? ciphertext.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;}}
(2)处理请求Controller
@Controller
@RequestMapping("wechat")
public class WeiXinController {
@RequestMapping(method = { RequestMethod.GET })
public void doGet(HttpServletRequest request, HttpServletResponse response) {
// 微信加密签名
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();
}
}
}
请求的处理程序写完后启动项目,再提交你填写的URL和Token,接口配置信息就Ok啦
5、配置微信获取用户信息域名(直接配置小米球的域名就ok)
6、配置完成然后和第二步的代码层结合,下载在线调试工具访问就可以调试了。
只能帮到这了,如果还不会此处是代码,自己下载看看。/download/yaya_jn/74920268