微信服务号Java开发(一)
发送模板消息
1. 服务器配置
-
解析:微信发送一个get请求,并携带4个参数:signature、timestamp、nonce、echostr。开发者需要验证该请求是否来源于微信,验证方法:
1)将token、timestamp、nonce三个参数进行字典序排序
2)将三个参数字符串拼接成一个字符串进行sha1加密
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
1.1. 配置官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319
1.2. sha1 加密:(Service)
/**
* sha1加密
* @param str
* @return
*/
private static String sha1(String str) {
try {
//获取一个加密对象
MessageDigest md = MessageDigest.getInstance("sha1");
//加密
byte[] digest = md.digest(str.getBytes());
//处理加密结果
char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
StringBuffer sb = new StringBuffer();
for (byte b : digest) {
sb.append(chars[(b>>4) & 15]);
sb.append(chars[b & 15]);
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
1.3. 检验signature:(Service)
/**
* 配置服务器时检验signature
* @param timestamp
* @param nonce
* @param signature
* @return
*/
private static final String TOKEN = "bpa";
public static boolean check(String timestamp,String nonce,String signature) {
//1. 将token、timestamp、nonce三个参数进行字典排序
String[] strs = new String[] {TOKEN, timestamp, nonce };
Arrays.sort(strs);
//2.将三个参数字符串拼接成一个字符串进行sha1加密
String str = strs[0] + strs[1] + strs[2];
String mysig = sha1(str);
//3.开发者获得加密后的字符串可与signature对比,标识该请求源于微信
return mysig.equals(signature);
}
1.6. servlet的get方法:处理微信发送的数据(servlet)
//配置服务器
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp 时间戳
nonce 随机数
echostr 随机字符串
*/
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
System.out.println(signature);
System.out.println(timestamp);
System.out.println(nonce);
System.out.println(echostr);
//验证请求来自微信
if(WxService.check(timestamp,nonce,signature)){
System.out.println("接入成功!");
PrintWriter out = response.getWriter();
//原样返回echostr参数
out.print(echostr);
out.flush();
out.close();
}
else{
System.out.println("接入失败!");
}
}
1.7. 提交
2. 获取AccessToken
-
解析:向微信发送一个get请求,微信返回一个携带AccessToken的JSON数据包
-
请求地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
-
请求携带的数据:
grant_type 获取access_token填写client_credential
appid 第三方用户唯一凭证
secret 第三方用户唯一凭证**,即appsecret
-
正常情况下返回的JSON包:
{"access_token":"ACCESS_TOKEN","expires_in":7200}![172119738]
2.1. 向指定地址发送get请求(util)
/**
* 向指定的地址发送get请求
*
* @param url
* @return
*/
public static String get(String url) {
try {
URL urlObj = new URL(url);
// 开链接
URLConnection connection = urlObj.openConnection();
InputStream is = connection.getInputStream();
byte[] b = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = is.read(b)) != -1) {
sb.append(new String(b, 0, len));
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
2.2. AccessToken类
package entity;
public class AccessToken {
private String accessToken;
private long expireTime;//过期时间
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public long getExpireTime() {
return expireTime;
}
public void setExpireTime(long expireTime) {
this.expireTime = expireTime;
}
public AccessToken(String accessToken, String expireIn) {
super();
this.accessToken = accessToken;
this.expireTime = System.currentTimeMillis()+Integer.parseInt(expireIn)*1000;
}
/**
* 判断token是否过期
* @return
*/
public boolean isExpire() {
return System.currentTimeMillis() > expireTime;
}
}
2.3. 导json jar包
2.4. 获取AccessToken (service):
private static final String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
private static final String APPID = "wx8820c0d640b18647";
private static final String APPSECRET = "2bb4d6c310395e17c47e75384ba80c43";
// 用于存储token
private static AccessToken at;
/**
* 获取token
*/
private static void getToken() {
String url = GET_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
String tokenStr = Util.get(url);
// System.out.println(tokenStr);//获取到的是一个JSON数据
// 将JSON数据的值取出来,封装到AccessToken类中
JSONObject jsonObject = JSONObject.fromObject(tokenStr);
String token = jsonObject.getString("access_token");
String expiresIn = jsonObject.getString("expires_in");
// 创建token对象,并存起来
at = new AccessToken(token, expiresIn);
}
/**
* 向外暴露的获取token的方法
*
* @return
*/
public static String getAccessToken() {
if (at == null || at.isExpire()) {
getToken();
}
return at.getAccessToken();
}
3.获取用户信息
4. 发送模板消息
3.1.首先看一下模板消息运营规范
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751288
3.2.新增模板
3.3. 设置行业信息
-
向指定地址发送一个携带json数据的post请求
-
JSON数据格式
{ "industry_id1":"1", "industry_id2":"4" }
-
向指定地址发送post请求(util)
/** * 向指定的地址发送一个post请求,带着json数据 * * @param url * @param data * @return */ public static String post(String url, String data) { try { URL urlObj = new URL(url); URLConnection connection = urlObj.openConnection(); // 要发送数据出去,必须设置为可发送数据状态 connection.setDoOutput(true); OutputStream os = connection.getOutputStream(); // 写出数据 os.write(data.getBytes(Charset.forName("utf-8"))); os.close(); // 获取输入流 InputStream is = connection.getInputStream(); byte[] b = new byte[1024]; int len; StringBuilder sb = new StringBuilder(); while ((len = is.read(b)) != -1) { sb.append(new String(b, 0, len)); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return null; }
-
设置行业信息以及获取行业信息
/** * 设置行业 */ @Test public void setHy() { String at = WxService.getAccessToken(); String url = "https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token=ACCESS_TOKEN"; url = url.replace("ACCESS_TOKEN", at); String data = "{\r\n" + " \"industry_id1\":\"1\",\r\n" + " \"industry_id2\":\"4\"\r\n" + "}"; String result = Util.post(url, data); System.out.println(result); } /** * 获取行业信息 */ @Test public void getHy() { String at = WxService.getAccessToken(); String url = "https://api.weixin.qq.com/cgi-bin/template/get_industry?access_token=ACCESS_TOKEN"; url = url.replace("ACCESS_TOKEN", at); String result = Util.get(url); System.out.println(result); }
3.4.发送模板消息:
3.4.1.解析
-
向微信地址发送一个携带JSON数据的post请求
https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
-
JSON数据格式
{ "touser":"o8WIn57l7aU_km1WGlYo64CPB0rM", "template_id":"fnuSPVY0RYEYxmKbmj9aL6VVU1A1LYKKUBZmaCZz9rY", "url":"http://weixin.qq.com/download", "data":{ "first": { "value":"您好!您投递的简历有新的反馈!", "color":"#173177" }, "company":{ "value":"北京58同城信息技术有限公司", "color":"#173177" }, "time": { "value":"2014-06-24", "color":"#173177" }, "result": { "value":"产品经理", "color":"#173177" }, "remark":{ "value":"反馈结果请和本公司人事联系!", "color":"#173177" } } }
3.4.2.向指定地址发送post 请求
/**
* 向指定的地址发送一个post请求,带着json数据
*
* @param url
* @param data
* @return
*/
public static String post(String url, String data) {
try {
URL urlObj = new URL(url);
URLConnection connection = urlObj.openConnection();
// 要发送数据出去,必须设置为可发送数据状态
connection.setDoOutput(true);
OutputStream os = connection.getOutputStream();
// 写出数据
os.write(data.getBytes(Charset.forName("utf-8")));
os.close();
// 获取输入流
InputStream is = connection.getInputStream();
byte[] b = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = is.read(b)) != -1) {
sb.append(new String(b, 0, len));
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3.4.2 封装该POST数据
-
导jar包:应用@XStreamAlias(“data”)注解。
-
将value、color属性封装成一个类 DataBase
public class DataBase { @XStreamAlias("value") private String value; @XStreamAlias("color") private String color; public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public DataBase(Map<String, String> map) { this.value = map.get("value"); this.color = map.get("color"); } }
-
将first、company、time、result、remark封装成类,类继承DataBase
@XStreamAlias("first") public class First extends DataBase{ public First(Map<String, String> map) { super(map); } } @XStreamAlias("company") public class Company extends DataBase{ public Company(Map<String, String> map) { super(map); } } @XStreamAlias("time") public class Time extends DataBase{ public Time(Map<String, String> map) { super(map); } } @XStreamAlias("result") public class Result extends DataBase{ public Result(Map<String, String> map) { super(map); } } @XStreamAlias("remark") public class Remark extends DataBase{ public Remark(Map<String, String> map) { super(map); } }
-
封装data类:类属性为:First、Company、Time、Result、Remark类的对象
@XStreamAlias("data") public class Data { private First first; private Company company; private Time time; private Result result; private Remark remark; public First getFirst() { return first; } public void setFirst(First first) { this.first = first; } public Company getCompany() { return company; } public void setCompany(Company company) { this.company = company; } public Time getTime() { return time; } public void setTime(Time time) { this.time = time; } public Result getResult() { return result; } public void setResult(Result result) { this.result = result; } public Remark getRemark() { return remark; } public void setRemark(Remark remark) { this.remark = remark; } public Data(First first, Company company, Time time, Result result, Remark remark) { super(); this.first = first; this.company = company; this.time = time; this.result = result; this.remark = remark; } }
-
整个要发送的数据对象Message
@XStreamAlias("xml") public class Message { @XStreamAlias("touser") private String touser; @XStreamAlias("template_id") private String template_id; @XStreamAlias("url") private String url; private Data data; public String getTouser() { return touser; } public void setTouser(String touser) { this.touser = touser; } public String getTemplate_id() { return template_id; } public void setTemplate_id(String template_id) { this.template_id = template_id; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Data getData() { return data; } public void setData(Data data) { this.data = data; } public Message(String touser, String template_id, String url, Data data) { super(); this.touser = touser; this.template_id = template_id; this.url = url; this.data = data; } }
3.4.3.配置文件template.properties
post_url=https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
touser=o8WIn57l7aU_km1WGlYo64CPB0rM
template_id=fnuSPVY0RYEYxmKbmj9aL6VVU1A1LYKKUBZmaCZz9rY
click_url=https://www.baidu.com
first_value=您好!你有新的报平安任务!
first_color=#173177
company_value=任务名称
company_color=#173177
time_value=发布时间
time_color=#173177
result_value=发布人
result_color=#173177
remark_value=请及时上报任务!
remark_color=#173177
3.4.4 .实现将xml转为json的方法
-
导包
-
方法
/**
* 将xml数据转为JSON数据字符串
*
* @param xml
* @return
* @throws JDOMException
* @throws IOException
*/
public static String xmlToJSON(String xml) {
XMLSerializer xmlSerializer = new XMLSerializer();
// 将xml转为json(注:如果是元素的属性,会在json里的key前加一个@标识)
JSON jsonObject = xmlSerializer.read(xml);
return jsonObject.toString();
}
3.4.5.发送模板消息
public void sendTemplateMessage() {
try {
// 加载模板
InputStream is = SendMsg.class.getResourceAsStream("/template.properties");
Properties pro = new Properties();
pro.load(is);
// 获取AccessToken
String at = WxService.getAccessToken();
System.out.println(at);
// 从模板中获取post的参数url
String url = pro.getProperty("post_url");
url = url.replace("ACCESS_TOKEN", at);
// 获取post参数:data
// first
Map<String, String> map_first = new HashMap<>();
map_first.put("value", pro.getProperty("first_value"));
map_first.put("color", pro.getProperty("first_color"));
First first = new First(map_first);
// company
Map<String, String> map_company = new HashMap<>();
map_company.put("value", pro.getProperty("company_value"));
map_company.put("color", pro.getProperty("company_color"));
Company company = new Company(map_company);
// time
Map<String, String> map_time = new HashMap<>();
map_time.put("value", pro.getProperty("time_value"));
map_time.put("color", pro.getProperty("time_color"));
Time time = new Time(map_time);
// result
Map<String, String> map_result = new HashMap<>();
map_result.put("value", pro.getProperty("result_value"));
map_result.put("color", pro.getProperty("result_color"));
Result result = new Result(map_result);
// remark
Map<String, String> map_remark = new HashMap<>();
map_remark.put("value", pro.getProperty("remark_value"));
map_remark.put("color", pro.getProperty("remark_color"));
Remark remark = new Remark(map_remark);
// data
Data dataMsg = new Data(first, company, time, result, remark);
// touser:先从配置文件获取,之后要获取用户openid存到数据库
String touser = pro.getProperty("touser");
// template_id
String template_id = pro.getProperty("template_id");
// 点击后跳转的链接
String click_url = pro.getProperty("click_url");
// 整个要发送的数据
Message msg = new Message(touser, template_id, click_url, dataMsg);
// XStream stream = new XStream();当属性带有下划线"_"转成XML时会变成双下划线"__"
XStream stream = new XStream(new Xpp3Driver(new NoNameCoder()));
// 处理需要@XStream注解的类
stream.processAnnotations(Message.class);
stream.processAnnotations(Data.class);
stream.processAnnotations(First.class);
stream.processAnnotations(Company.class);
stream.processAnnotations(Time.class);
stream.processAnnotations(Result.class);
stream.processAnnotations(Remark.class);
// 将Java类转为xml
String xml = stream.toXML(msg);
System.out.println(xml);
// 将xml数据转为json
String data = WxUtils.xmlToJSON(xml);
System.out.println(data);
// 发送post请求
String rst = WxUtils.post(url, data);
System.out.println(rst);
} catch (IOException e) {
e.printStackTrace();
}
}