微信大众号获取openid(1)
微信公众号获取openid(1)
最近公司要用微信公众号开发关于停车场的功能(基本都是网页形式),官方文档毕竟很官方,读起来必然没有大白话通俗易懂,现整理整理流程和期间遇到的一些问题、注意事项记录下来,要是有同等需要的人可以参考下(以下操作是在申请测试号的前提下,正式号没什么差别,我还没有配置正式号,如果有差别回来再进行说明)。
- 去微信公众平台用邮箱注册一个测试号,网址为https://mp.weixin.qq.com。注册完成后微信平台会自动分配一个appID和appsecret,这两个参数是后续配置菜单,获取token等操作需要的凭证。
- 填写接口配置信息,此信息需要你有自己的服务器资源,填写的URL需要正确响应微信发送的Token验证,简单来说就是这个配置的url,就是你在服务器工程上写的接口,用户在微信上的一些操作(比如关注你的公众号、取关等),微信就会调用这个接口,然后你需要将微信发送过来的参数进行校验,校验成功,则返回给微信echostr,校验失败,微信没有接收到echostr回传字符串,就会终止用户的操作。
其中的URL用域名,Token是与微信自定义的,看你心情定义了,保存配置的时候,微信会调用你填写的URL接口地址,校验成功了方能保存成功,否则保存配置失败。示例代码:
- /**
* 正确响应微信发送的token验证
* @param request
* @param response
* @return
*/
@RequestMapping(value="/tokenVerify")
@ResponseBody
public String tokenVerify(HttpServletRequest request,HttpServletResponse response){
try {
// 微信加密签名
String signature = request.getParameter("signature");
System.out.println("微信加密签名signature:-----------------------"+signature);
// 时间戳
String timestamp = request.getParameter("timestamp");
System.out.println("时间戳timestamp:-----------------------"+timestamp);
// 随机数
String nonce = request.getParameter("nonce");
System.out.println("随机数nonce:-----------------------"+nonce);
// 随机字符串
String echostr = request.getParameter("echostr");
System.out.println("随机字符串echostr:-----------------------"+echostr);
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(WEI_XIN_TOKEN, signature, timestamp, nonce)) {
return echostr;
}
} catch (Exception e) {
System.out.println("/weixin/login异常");
e.printStackTrace();
}
return "";
} - 请求校验工具类示例代码:
* 微信请求校验工具类
*/
public class SignUtil {
/**
* 验证签名
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String token, 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;
}
} - 设置JS接口安全域后,通过关注该测试号,开发者即可在该域名下调用微信开放的JS接口,这个只需要填写服务器域名即可。
- 在体验接口权限表里面,找‘网页服务’类目下的第一条功能‘网页账号’,‘网页授权获取用户基本信息’接口,进行修改,填写服务器域名。用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。沙盒号回调地址支持域名和ip,正式公众号回调地址只支持域名。官方写的很清楚了,测试号也最好是写域名而不是ip,否则会有问题。
- 利用微信公众平台接口调试工具,进行access_token的获取,以及菜单的生成。调试工具地址:https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type基础支持&form=获取access_token接口/token。首先获取access_token,填写第一步生成的appID和appsecret,然后‘接口类型’选择‘自定义菜单’,利用刚获取的access_token,进行菜单的添加,示例代码:
- {
"button":[
{
"type": "view",
"name": "智慧停车",
"url": "http://域名/工程名/weiXinFront/loginPark",
"sub_button": [ ]
},
{
"type": "view",
"name": "我的车辆",
"url": "http://域名/工程名/weiXinFront/loginMycar",
"sub_button": [ ]
},
{
"name":"测试1",
"sub_button":[
{
"type":"click",
"name":"子菜单名1",
"key":"name1"
},
{
"type":"click",
"name":"子菜单名2",
"key":"name2"
}
]
},
{
"type":"view",
"name":"百度",
"url":"http://www.baidu.com"
}
]
},其中‘智慧停车’和‘我的车辆’是我需要的菜单,其他就是测试的包括子级菜单,用户在点击‘智慧停车’菜单时微信会访问在你生成菜单时填写的url地址,"url": "http://域名/工程名/weiXinFront/loginPark", 在loginPark接口里设置redirecrtUrl,用户同意授权后,微信会将code(用于获取用户openId)传至redirecrtUrl你设置的接口里,在这个接口里我们可以利用收到的code获取用户openId和跳转页面。示例代码: -
@RequestMapping(value="loginPark")
@ResponseBody
public void loginPark(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException{
//跳转接口地址
String redirecrtUri = Host+"weiXinFront/openidPark";
String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+ appID +"&redirect_uri="+URLEncoder.encode(redirecrtUri, "utf-8")+"&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
try {
System.out.println("loginPark==url============="+redirecrtUri);
response.sendRedirect(url);
} catch (IOException e) {
e.printStackTrace();
}
} - @RequestMapping(value="openidPark")
public String openidPark(HttpServletRequest request, HttpServletResponse response){
//微信发送过来的code
String code = request.getParameter("code");
//获取access_token、get请求
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appID+"&secret="+ appsecret +"&code="+code+"&grant_type=authorization_code";
String result = HttpUtil.sendGet(url, "utf-8", 30000);//发送get请求
if(StringUtils.isNotBlank(result)){
JSONObject jo = JSONObject.fromObject(result);
try{
HttpSession session = request.getSession();
String openid = jo.optString("openid");
synchronized (this) {
//查找该用户是否已经存在,不存在则新增用户信息
HUser huser = huserService.getByOpenId(openid);
if(huser == null){
huser = new HUser();
huser.setOpenId(openid);
huserService.save(huser);
}
}
session.setAttribute("openId", openid);
//重定向到访问页
return "redirect:"+Host+"static/weixin/zhihui_tingche.jsp";
}catch(Exception e) {
e.printStackTrace();
return "";
}
}
//异常页
return "modules/weixin/NewFile";
} - HttpUtil发送get请求工具类示例代码:public class HttpUtil {
/***
* 模拟get请求
* @param url
* @param charset
* @param timeout
* @return
*/
public static String sendGet(String url, String charset, int timeout)
{
String result = "";
BufferedReader in = null;
try
{
URL u = new URL(url);
try
{
URLConnection conn = u.openConnection();
conn.connect();
conn.setConnectTimeout(timeout);
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
String line="";
while ((line = in.readLine()) != null)
{
result = result + line;
}
//in.close();
} catch (IOException e) {
return result;
}
}
catch (MalformedURLException e)
{
return result;
}
finally{
try {
if(in!=null){
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/*
* post
* url:请求地址
* param:参数
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection conn = realUrl.openConnection();
// 设置通用的请求属性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
// conn.setRequestProperty("user-agent",
// "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 发送POST请求必须设置如下两行
conn.setDoOutput(true);
conn.setDoInput(true);
// 获取URLConnection对象对应的输出流
out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(param);
// flush输出流的缓冲
out.flush();
// 定义BufferedReader输入流来读取URL的响应
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
}
//使用finally块来关闭输出流、输入流
finally{
try{
if(out!=null){
out.close();
}
if(in!=null){
in.close();
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
return result;
}
} - 在你自己的页面里(zhihui_tingche.jsp)就可以进行功能操作了(ajax获取数据,需要openId的话则在后台接口里,用session获取)。