欢迎大家来到IT世界,在知识的湖畔探索吧!
服务器配置


1.首先根据官方文档得知,
2.将token、timestamp、nonce三个参数进行字典序排序
3.将三个参数字符串拼接成一个字符串进行sha1加密
4.开发者获得加密后的字符串可与signature对比,标识该请求来源于
5.验证url需要开发者登录官方开发平台扫码获得填写。
详细代码如下:
public void validate(String wxToken, CheckModel tokenModel,HttpServletResponse response){
String signature = tokenModel.getSignature();
Long timestamp = tokenModel.getTimestamp();
Long nonce =tokenModel.getNonce();
String echostr = tokenModel.getEchostr();
if(signature!=null&×tamp!=null&nonce!=null) {
String[] str = {wxToken, timestamp+””, nonce+””};
Arrays.sort(str); // 字典序排序
String bigStr = str[0] + str[1] + str[2];
// SHA1加密
String digest = EncoderHandler.encode(“SHA1”, bigStr).toLowerCase();
// 确认请求来至
if (digest.equals(signature)) {
//最好此处将echostr存起来,以后每次校验消息来源都需要用到
try {
System.out.println(“成功返回 echostr:” + echostr);
response.getWriter().write(echostr);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
System.out.println(“no”);
}
}
}
开发者token校验完成之后,获取accesstoken。access_token是的全局唯一票据,调用各接口时都需使用access_token。开发者需要进行妥善保存。
access_token的存储至少要保留512个字符空间。
access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的 access_token失效。
如果第三方不使用中控服务器,而是选择各个业务逻辑点各自去刷新access_token,那么就可能会产生冲突,导致服务不稳定。
可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在获得(需要已经成为开发者,且帐号没有异常状态)。注意调用所有接口时均需使用https协议。
access_token的有效期是7200秒(两小时),在有效期内,可以一直使用,只有当access_token过期时,才需要再次调用接口 获取access_token。在理想情况下,一个7×24小时运行的系统,每天只需要获取12次access_token,即每2小时获取一次。如果在 有效期内,再次获取access_token,那么上一次获取的access_token将失效。
目前,获取access_token接口的调用频率限制为2000次/天,如果每次发送客服消息、获取用户信息、群发消息之前都要先调用获取 access_token接口得到接口访问凭证,这显然是不合理的,一方面会更耗时(多了一次接口调用操作),另一方面2000次/天的调用限制恐怕也不 够用。因此,在实际应用中,我们需要将获取到的access_token存储起来,然后定期调用access_token接口更新它,以保证随时取出的 access_token都是有效的。接口调用请求说明
http请求方式: GET
https://api.&&.》》.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}
|
参数 |
说明 |
|
access_token |
获取到的凭证 |
|
expires_in |
凭证有效时间,单位:秒 |
错误时返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
{“errcode”:40013,”errmsg”:”invalid appid”}
2.获取accesstoken
主要思想:利用httpclient等工具模拟浏览器向服务器发送请求,如果成功,则获取
public static AccessToken getAccessToken(String appID, String appScret) {
AccessToken token = new AccessToken();
// 访问服务器
Stringurl=”https://api..qq.com/cgi-bin/token?grant_type=client_credential&appid=” + appID + “&secret=”+ appScret;
HttpClient httpClient = new DefaultHttpClient();
try {
HttpGet httpget = new HttpGet(url);
HttpResponse response = httpClient.execute(httpget);
HttpEntity httpEntity = response.getEntity();
// String string = httpEntity.toString();
// System.out.println(string);
// JSONObject json = JSONObject.fromObject(string);
//取属性值
String json1=EntityUtils.toString(httpEntity);
JSONObject json = JSONObject.fromObject(json1);
token.setAccess_token(json.getString(“access_token”));
SimpleDateFormat df = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
String save_time=df.format(new Date());
token.setSave_time(save_time);
token.setExpire_time(json.getString(“expires_in”));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
httpClient.getConnectionManager().shutdown();
}
return token;
}
3.自定义菜单
获取accesstoken之后,就可以进行自定义菜单的创建。这里要注意的是要进入官方网站获取appId和appScret。利用这两个字段值进行自定义菜单的创建。
public static int createMenu(Menu menu) {
String jsonMenu = JSONObject.fromObject(menu).toString();
int status = 0;
// 凭证和秘钥
String appID = “########”;
String appScret = “&&&&&&&&&&&&”;
System.out.println(“菜单:” + jsonMenu);
String path = “https://api.weixin.qq.com/cgi-bin/menu/create?access_token=”
+ AccessTokenUtil.getAccessToken(appID, appScret).getAccess_token();
// 修改菜单时只需要修改它的accesstoken,利用accesstoken工具类发送一个http请求
// String
// path=”https://api.&&.qq.com/cgi-bin/menu/create?access_token=qHbUDio1PuPqh-cWLAiRAl7p761_0p8yqJWFIXe9lWwmAynM1oXJ0mctjtPLsf3Ga0gZonoUBZ7JF5KESp9vthgMkEj6FyRILxeC6a9Ll4UfrDIJmJSDbb2dEI6fcEk1JLSeABAVXE”;
try {
URL url = new URL(path);
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setDoOutput(true);
http.setDoInput(true);
http.setRequestMethod(“POST”);
http.setRequestProperty(“Content-Type”, “application/x-www-form-urlencoded”);
http.connect();
OutputStream os = http.getOutputStream();
os.write(jsonMenu.getBytes(“UTF-8”));
os.close();
InputStream is = http.getInputStream();
int size = is.available();
byte[] bt = new byte[size];
is.read(bt);
String message = new String(bt, “UTF-8”);
JSONObject jsonMsg = JSONObject.fromObject(message);
status = Integer.parseInt(jsonMsg.getString(“errcode”));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return status;
}
注意的是菜单的创建有缓存时间,所以要想即时查看,要取消关注再重新关注即可观察。click类型的菜单有key属性,而view类型的菜单没有key属性,与之对应的是url属性
用户订单创建时候注意,view和click的区别。View是可以直接跳转的url,而click是在后台进行判断的,其本身具有key值,开发者在后台进行字符串的处理,一般返回图文消息体,图文消息体中镶嵌跳转url。Ps:最重要的一点,不能请求重定向和请求转发,即跳转权不在开发者手里。
4.自定义回复消息
接入接口,如果是get请求,则进行第一次握手验证ps:第一步进行的验证,如果是post的话,则进行自定义消息回复,通俗讲就是当你第一次握手在网站填写的url,这个url就是一个入口,以后每次在客户端进行的操作的反馈都将发送到这个url上进行验证。
利用注解xml,将受到的消息和要返回的消息都用Xsteam进行构造。接受的消息体xml格式如下:
@XStreamAlias(“xml”)
public class InputMessage implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@XStreamAlias(“ToUserName”)
private String ToUserName;
@XStreamAlias(“FromUserName”)
private String FromUserName;
@XStreamAlias(“CreateTime”)
private Long CreateTime;
@XStreamAlias(“MsgType”)
private String MsgType ;
@XStreamAlias(“MsgId”)
private Long MsgId;
// 文本消息
@XStreamAlias(“Content”)
private String Content;
// 图片消息
@XStreamAlias(“PicUrl”)
private String PicUrl;
// 位置消息
@XStreamAlias(“LocationX”)
private String LocationX;
@XStreamAlias(“LocationY”)
private String LocationY;
@XStreamAlias(“Scale”)
private Long Scale;
@XStreamAlias(“Label”)
private String Label;
// 链接消息
@XStreamAlias(“Title”)
private String Title;
@XStreamAlias(“Description”)
private String Description;
@XStreamAlias(“Url”)
private String URL;
// 语音信息
@XStreamAlias(“MediaId”)
private String MediaId;
@XStreamAlias(“Format”)
private String Format;
@XStreamAlias(“Recognition”)
private String Recognition;
// 事件
@XStreamAlias(“Event”)
private String Event;
@XStreamAlias(“EventKey”)
private String EventKey;
@XStreamAlias(“Ticket”)
private String Ticket;
回复消息要进行CDATA认证,编写一个注解用于实现这个功能。代码如下:
/**
* @author dingjianlei
* 自动回复消息的xml要经过CDATA的验证,此注解实现了CDATA验证
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface XStreamCDATA {
}
ps:
自动回复消息格式体。跟inputMessage一样,利用注解进行xml格式的设置,回复的xml格式信息要经过CDATA 验证,
详细参考文档http://blog.csdn.net/lyq8479/article/details/8952173
5.绑定用户信息 用OpenID绑定即可
接口中虽然没给用户的账号,但给了用户的OpenID,这个OpenID对一个是唯一的,测试也证明不会改变,也就是说同一个号和同一个号交互,我们得到的OpenID是不会变的,因此,可以用OpenID作为用户的身份标识。
如何绑定
在里面给用户一个验证链接,用户点击链接,会用内嵌的浏览器打开这个链接,然后就是一般的网页登录验证界面,我们通过HTTP(S)获取用户输入的系统用户名与密码,验证通过后完成绑定。 具体如何生成链接和如何传递OpenID下面详述。
如何生成绑定链接
绑定涉及到用户的身份甚至利益,所以需要注意安全性。我们需要绑定的是OpenID和系统用户,系统用户名是用户直接在链接页面输入后通过HTTP(S)传给我们的,这没有问题。OpenID对用户来说透明,用户不会传给我们,我们也只有在用户发消息时才可获得OpenID,所以很明显,OpenID需要包含在生成的链接中,至于需不需要对OpenID作加密就看你自己了,我觉得这不重要,更为重要的是要在链接中带上签名和加上时间戳。因为我们需要确认这个链接是由我们服务端生成的,用户自己或者其他人不能够伪造出这个链接,加上时间戳是为了给这个链接一个过期时间,如果不限制过期时间,假设用户绑定后这个链接通过某种方式被别人知道,那么这个人就可以把自己的账号与用户的号绑定。所以我采取的方法是用OpenID、过期时间再加上一个密钥生成签名,生成签名的方法和服务器接口验证时的签名方法类似(密钥最好另选一个只有自己知道的)。
如何传递OpenID
有了绑定链接,用户点了绑定链接,但这只是第一步,第二步我们需要在用户在链接页面提交登录请求后进行验证,OpenID怎么传到第二步中呢?有人说了,这还不简单,在登录表单中加一个隐藏域放用户的OpenID一起提交给验证的Handler不就OK了,那我只能说很遗憾,你前面所做的安全工作都白费了,一旦A用户的OpenID泄漏,B用户就可以把自己的账号与A用户的号绑定了。所以永远不要相信客户端提交的东西。我的方法是当用户点击生成链接后,在链接页载入时,将OpenID存到session中,因为这个session是没法伪造的(cookie被盗除外),所以只有点击这个链接的用户的session中才会有链接中包含的OpenID。
关于服务器签名多说一句
大家都知道消息接口验证时微信会向我们服务器发一个GET请求,在其中带上只有我们和务器知道的签名,我们在请求处理Handler中会验证这个签名,这点大家无异议;消息接口验证通过后,就会把用户发的消息以POST的方式发给我们,很多人可能会在这里忽视对签名的检查,从而给恶意者伪造用户请求的机会。在以POST方式传递用户的消息时,仍然会将签名信息附在URL参数中,我们在处理每一个POST请求时,第一步还是得像处理消息接口验证时一样,去对URL参数中的签名作验证,只有签名验证通过后才可去取POST的信息。
绑定流程的详细描述
我在上面说了基本方法,但不够详细,导致一些新手朋友还是不清楚具体如何操作,所以我在此尝试更加详细地描述整个过程的每一步:
数据库中建立用户OpenID和系统用户的绑定关系表,初始时为空。
用户交互时,你可以取到用户的OpenID。
检查数据表,如发现该OpenID没有绑定系统用户,则返回一个链接供用户在浏览器中打开,这个链接打开后类似于系统用户登录界面。
关键是链接的生成,链接需要带3个url参数:
1. 用户OpenID:因为你需要在绑定页面中取到是哪个用户想要绑定系统用户。
2. 时间戳timestamp:这是为了防止链接泄漏出去被恶意利用,具体来说就是一个你指定的过期时间,超过这个时间这个链接就失效了,用户只能再次获取。
3. 签名signature:这是为了保证此验证链接只有你才可能生成,用户及第三方均无法伪造。
签名的生成需要你有一个只有你知道的token(密钥,不要与设置在公众号里面的token相同),生成签名方法和验证服务器消息真实性类似。流程如下:
1. 将token、OpenID、timestamp三个参数进行字典序排序。
2. 将三个参数字符串拼接成一个字符串进行sha1加密,得到链接的signature参数。
用户打开某个绑定链接时,你首先验证链接的有效性,即按上述5.1和5.2中描述的同样的步骤得到signature与参数中的sinature对比,如果相同,则再检查链接是否超时,两步验证均通过,你就可以将用户的OpenID设置到用户的session中,然后你就可以渲染验证页面,让用户输入其系统用户名和密码以提交给你服务器进行验证。
在用户提交绑定验证请求后,你只需要检查session中有没有你设置的OpenID,没有自然无效,有的话就是要绑定的OpenID了,此时你可以把这个OpenID从session中删除了。然后你如果验证系统用户名和密码通过后,就把这个OpenID和系统用户绑定起来,加入到第1步中说的绑定关系表中。
完毕。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/22282.html