本文档仅为微信开发文档记录,非参考文档,非官方文档。如果其中出现错误,欢迎指正。然后其中有代码是参考的网上资料,具体参考哪里的,已经记不清了。
这里记录下来,希望对以后开发有帮助,也可以帮助到需要的同学。
然后我也不知道为什么从word复制进来,代码都在一起了,太多了我也不一个个去分开了。
注:这是我之前写开发的时候用到的,可能已经过时了或者有更好的方法。因为有一段时间没有写javaweb开发了,我最近浏览时了解到Spring中的@Schedule注解,可以每隔多少时间调用一次方法,我觉得可以用来去获取token,但是自己还没有尝试过,仅仅提出来作为参考。
一、关于微信开发中JS-SDK的使用,其中包括AccessToken的获取与JsApiTicket的获取以及全局缓存的问题
开发前的准备与说明:
1).绑定域名,在微信公众号的【公众号设置】-【js接口安全域名中设置】
2).引入JS文件,http://res.wx.qq.com/open/js/jweixin-1.2.0.js(具体版本可看官方文档)
3).在开发中的页面,是需要先用config接口注入权限验证配置的,其中包括的内容有:
appId,timestamp,nonceStr,signature,jsApiList:[],其中签名是需要jsApiTicket生成的,而jsApiTicket是需要AccessToken来生成的。
并且根据微信官方,获取这些凭证每天是有次数限制的。建议的是全局缓存这两个数据在服务器端。接下来详细解说一下这几个步骤。
1.微信AccessToken的获取
第一步:
建立一个通用类,CommonUtil类
第二步:定义两个final static变量,分别为去获取AccessToken的地址与获取jsApiTicket的地址(微信官方文档指出,这两个是发送请求到微信指定服务器获取的。这两个量定义很快就会用到。)
代码如下:
```
//凭证获取(GET)
public final staticStringtoken_url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
private final staticStringjsApiTicket_url="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
```
第三步:从上面的地址或者是微信官方文档都可看出,微信获取这两个凭证都是用的https协议,而返回的数据是一个JSON格式的数据。所以定义个静态方法来发送https请求,并获取到数据。(这个请求的方法我也是从网上获取来的,属于CommonUtil类)
```
/**
*发送https请求
*
*@paramrequestUrl请求地址
*@paramrequestMethod请求方式(GET、POST)
*@paramoutputStr提交的数据
*@returnJSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static JSONObject httpsRequest(Stringr equestUrl,String requestMethod,String outputStr){
JSONObjectjsonObject=null;
try{
//创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager [] tm={new MyX509TrustManager()};
SSLContextssl Context=SSLContext.getInstance("SSL","SunJSSE");
sslContext.init(null,tm,newjava.security.SecureRandom());
//从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactoryssf=sslContext.getSocketFactory();
URLurl=newURL(requestUrl);
HttpsURLConnectionconn=(HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
//设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
//当outputStr不为null时向输出流写数据
if(null!=outputStr){
OutputStreamoutputStream=conn.getOutputStream();
//注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
//从输入流读取返回内容
InputStreaminputStream=conn.getInputStream();
InputStreamReaderinputStreamReader=newInputStreamReader(inputStream,"utf-8");
BufferedReaderbufferedReader=newBufferedReader(inputStreamReader);
Stringstr=null;
StringBufferbuffer=newStringBuffer();
while((str=bufferedReader.readLine())!=null){
buffer.append(str);
}
//释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream=null;
conn.disconnect();
jsonObject=JSONObject.fromObject(buffer.toString());
}catch(ConnectExceptionce){
}catch(Exceptione){
}
returnjsonObject;
}
```
第四步:封装AccessToken类
封装AccessToken类,方便之后处理数据。
```
* AccessToken类,表示微信的token参数
* token:凭证
* expiresIn:凭证有效时间,单位为秒
* **/
publicclassAccessToken{
privateStringtoken;
privateintexpiresIn;
publicStringgetToken(){
returntoken;
}
publicvoidsetToken(Stringtoken){
this.token=token;
}
publicintgetExpiresIn(){
returnexpiresIn;
}
publicvoidsetExpiresIn(intexpiresIn){
this.expiresIn=expiresIn;
}
}
```
第五步:定义获取access_token方法
```
publicstaticAccessTokengetAccessToken(Stringappid,Stringappsecret){
AccessTokenaccessToken=null;
StringrequestUrl=token_url.replace("APPID",appid).replace("APPSECRET",appsecret);
JSONObjectjsonObject=httpsRequest(requestUrl,"GET",null);
//请求成功
if(null!=jsonObject){
try{
accessToken=newAccessToken();
accessToken.setToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getInt("expires_in"));
}catch(Exceptione){
accessToken=null;
System.out.println("获取token失败~~~~~~~~~~~~~~~~~~~~~~");
}
}
returnaccessToken;
}
```
这个方法是用来向微信服务器获取access_token,需要传入的数据为appid与appsecret,这两个数据都是微信公众平台获取的,不叙述了。
以上方法为获取accessToken的过程,除了发送请求的方法有些复杂外,其他都是简单易懂的。
2.获取JSAPITICKET步骤
第一步:封装JsApiTicket类
```
* JsApiTicket类,用于微信内部调动js的凭证内容
* ticket:凭证
* expiresIn:有效时间
*
* **/
publicclassJsApiTicket{
privateStringticket;
privateintexpiresIn;
publicStringgetTicket(){
returnticket;
}
publicvoidsetTicket(Stringticket){
this.ticket=ticket;
}
publicintgetExpiresIn(){
returnexpiresIn;
}
publicvoidsetExpiresIn(intexpiresIn){
this.expiresIn=expiresIn;
}
}
```
第二步:书写获取jsapiticket的方法,这个方法和获取accessToken方法是类似的,发送一个请求到微信服务器,但是传入的参数是accessToken
```
/**
*获取JsApiTicket
* 2017.5.8
*/
publicstaticJsApiTicketgetJsApiTicket(StringaccessToken){
JsApiTicketjsApiTicket=null;
StringrequestUrl=jsApiTicket_url.replace("ACCESS_TOKEN",accessToken);
JSONObjectjsonObject=httpsRequest(requestUrl,"GET",null);
if(null!=jsonObject){
jsApiTicket=newJsApiTicket();
jsApiTicket.setTicket(jsonObject.getString("ticket"));
jsApiTicket.setExpiresIn(jsonObject.getInt("expires_in"));
}
returnjsApiTicket;
}
```
以上是获取jsApiTicket的方法,但是在js前端发送验证的是签名而非这个凭证,但是签名是由jsApiTicket作为参数来生成,下面来说创建签名的方法
3.创建验证签名:
第一步、定义Sha1Util类,我不知道微信开发包中是否存在这个类,有的话不必自己定义,没有的话就自己写一个
第二步:在该类中书写获取随机字符串的方法,很快会用到
```
publicstaticStringgetNonceStr(){
Randomrandom=newRandom();
returnMD5Util.MD5Encode(String.valueOf(random.nextInt(10000)),"UTF-8");
}
```
第三步:书写获取时间戳的方法:很快会用到
```
publicstaticStringgetTimeStamp(){
returnString.valueOf(System.currentTimeMillis()/1000);
}
```
第四步:书写创建签名的方法,这也是最后要生成签名的方法:
```
//创建签名SHA1
publicstaticStringcreateSHA1Sign(SortedMapsignParams)throwsException{
StringBuffersb=newStringBuffer();
Setes=signParams.entrySet();
Iteratorit=es.iterator();
while(it.hasNext()){
Map.Entryentry=(Map.Entry)it.next();
Stringk=(String)entry.getKey();
Stringv=(String)entry.getValue();
sb.append(k+"="+v+"&");
//要采用URLENCODER的原始值!
}
Stringparams=sb.substring(0,sb.lastIndexOf("&"));
returngetSha1(params);
}
//Sha1签名
publicstaticStringgetSha1(Stringstr){
if(str==null||str.length()==0){
returnnull;
}
charhexDigits[]={'0','1','2','3','4','5','6','7','8','9',
'a','b','c','d','e','f'};
try{
MessageDigestmdTemp=MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[]md=mdTemp.digest();
intj=md.length;
charbuf[]=newchar[j*2];
intk=0;
for(inti=0;i
bytebyte0=md[i];
buf[k++]=hexDigits[byte0>>>4&0xf];
buf[k++]=hexDigits[byte0&0xf];
}
returnnewString(buf);
}catch(Exceptione){
returnnull;
}
}
```
第五步:回到CommonUtil类中,书写一个创建签名的方法。为方便传数据到页面上进行验证,故所有的数据都是作为参数传入,分别为随机字符串,jsApiTicket,时间戳,url,其中url为你要调用的页面地址,#之后的不要
然后对传入的数据进行排序,生成签名
```
/**
*创建jsApi签名
*
*/
publicstaticStringcreateJsApiTicketSign(Stringnoncestr,StringjsApiTicket,Stringtimestamp,Stringurl){
Stringstring1="jsapi_ticket="+jsApiTicket
+"&noncestr="+noncestr
+"×tamp="+timestamp
+"&url="+url;
Stringsignature=Sha1Util.getSha1(string1);
returnsignature;
}
```
到此为止,签名已经生成了。准确的来说,其实现在已经可以去开发jssdk了。但是,全局缓存的问题没有解决,接下来我们去解决这个问题。
4.全局缓存问题
全局缓存问题我所知道的有两种方法,一种是将他存入到数据库当中。第二种则是因为我用的spring框架开发,利用他缓存在全局中。
第一步:建立一个final类,也就是全局缓存的ServletContextUtil,在这个类中生成全局唯一的的ServletContext,并且在这里面定义从全局缓存的数据中获取AccessToken与JsApiTicket
代码如下:
```
/**
*
*全局缓存ServletContext
*@authorAdministrator
*
*/
publicfinalclassServletContextUtil{
privatestaticServletContextservletContext;
privateServletContextUtil(){}
publicsynchronizedstaticServletContextget(){
if(null==servletContext){
WebApplicationContext webApplicationContext=ContextLoader.getCurrentWebApplicationContext();
servletContext=webApplicationContext.getServletContext();
}
returnservletContext;
}
//获取缓存的AccesToken
publicstaticAccessTokengetAccessToken(){
return(AccessToken)ServletContextUtil.get().getAttribute("Contants.ACCESS_TOKEN");
}
//获取缓存的JSAPITICKET
publicstaticJsApiTicketgetJsApiTicket(){
return(JsApiTicket)ServletContextUtil.get().getAttribute("Contants.JSAPI_TICKET");
}
```
第二步:返回到CommonUtil类中,书写一个方法,该方法用于获取两个凭证并且存储到全局中。其中appid与appsecret我是用的全局常量,你可以使用其他的方法
```
/**
*
*获取与缓存
*/
public static void initAndSetAccessToken(){
AccessTokenaccessToken=getAccessToken(InWeiChatPayConfigUtil.appid,InWeiChatPayConfigUtil.appsecret);
System.out.println("1111111111111111111111111111111111111111"+accessToken.getToken());
if(null!=accessToken){
ServletContextsc=ServletContextUtil.get();
sc.removeAttribute("Contants.ACCESS_TOKEN");
sc.setAttribute("Contants.ACCESS_TOKEN",accessToken);
JsApiTicketjsApiTicket=getJsApiTicket(accessToken.getToken());
System.out.println("111111111111111111111111"+jsApiTicket.getTicket());
if(null!=jsApiTicket){
sc.removeAttribute("Contants.JSAPI_TICKET");
sc.setAttribute("Contants.JSAPI_TICKET",jsApiTicket);
}else{
System.out.println("获取jsapi失败·······················");
}
}else{
System.out.println("获取AccessToken失败·····················");
}
}
```
第三步、我们需要新建一个监听器,用于每隔7000秒就去获取一次凭证,代码如下:
```
/**
*微信缓存监听器
*@authorAdministrator
*
*/
public class WXAccessTokenListener implements ApplicationListener{
@Override
publicvoidonApplicationEvent(ContextRefreshedEventevent){
//TODOAuto-generated method stub
if(event.getApplicationContext().getParent()==null){
Runnablerunnable=newRunnable(){
@Override
publicvoidrun(){
CommonUtil.initAndSetAccessToken();
}
};
ScheduledExecutorServiceservice=Executors.newSingleThreadScheduledExecutor();
service.scheduleAtFixedRate(runnable,1,7000,TimeUnit.SECONDS);
}
}
}
```
第四步:这是最后一步了,就是需要再spring容器中注册这个监听器,否则是无效的。
那么获取与缓存的问题也就解决了,惊不惊喜?意不意外?
下面演示一下我的使用方式: