微信公眾號用java中控伺服器-實現使用者網頁授權和獲取使用JS-SDK許可權
阿新 • • 發佈:2019-02-13
一、網頁授權,是指使用者點選微信公眾號裡的自定義選單在開啟頁面中獲取授權使用者資訊,可以用來實現免登入。
下圖是需要引導使用者點開的地址其中redirect_uri=指向你真正要跳轉的地址,比如可以在自定義選單跳轉地址中改成這種地址形式,就可以在頁面中獲取到位址列的code。
/**
* 獲取微信使用者資訊或者使用者openid
* @param code 微信給選單地址重定向後帶位址列的引數
* @param type 1只返回openid,20返回使用者資訊
* @return
* @throws IOException
*/
@RequestMapping(value= "/getWxUser", method=RequestMethod.POST)
@ResponseBody
public Object getWxUser(String code, Integer type) throws IOException{
log.debug(code);
log.debug(type);
if(Assert.isInvalidString(code) || type==null){
return null;
}
//獲取access_token和openid
Map<String, Object> params = new HashMap<String, Object>();
params.put("appid", SysConfig.getValue(SysConstants.Config.WX_APPID));//微信公眾號的Appid
params.put("secret", SysConfig.getValue(SysConstants.Config.WX_SECRET));//微信公眾號的AppSecret
params.put("code", code);//微信給選單地址重定向後帶位址列的引數
params.put("grant_type", "authorization_code");
HttpURLConnection conn = HttpUtil.doGet(SysConstants.Url.WX_OAUTH_ACCESS_TOKEN_URL, params, null);
JSONObject result = JSONObject.parseObject(HttpUtil.getString(conn));
log.debug(result);
if(type==1){
return result;
}else if(type==20){
//獲取使用者資訊
params = new HashMap<String, Object>();
//這個access_token是網頁授權專用的,和jssdk中的2小時時效,一天最多2000次的那個不一樣,這個沒有限制次數
params.put("access_token", result.get("access_token"));
params.put("openid", result.get("openid"));
params.put("lang", "zh_CN");
conn = HttpUtil.doGet(SysConstants.Url.WX_USERINFO_URL, params, null);
result = JSONObject.parseObject(HttpUtil.getString(conn));
log.debug(result);
return result;
}else{
return null;
}
}
二、獲取微信JS-SDK許可權
獲取微信js-sdk許可權可以通過微信來呼叫手機的一些功能,比如拍照。
這裡要用到資料庫儲存access_token和jsapi_ticket,因為這兩個都只有2小時時效,一天最多2000次的。
/**
* 獲取wx.config的配置引數
* @param url
* @return
* @throws Exception
*/
@RequestMapping(value="/getWxJS", method=RequestMethod.POST)
@ResponseBody
public Object configWxJS(String url) throws Exception{
JSONObject json = new JSONObject();
String appId = SysConfig.getValue(SysConstants.Config.WX_APPID);
//1、資料庫中查詢jsapi_ticket,用修改時間與現在的時間比較是否過期(7200秒)。
//2、如果jsapi_ticket過期(超過7200秒),資料庫中查詢access_token,用修改時間與現在的時間比較是否過期(7200秒),如果jsapi_ticket沒有過期直接用來加密簽名
//3、如果access_token過期,先重新獲取access_token,如果沒有過期使用access_token獲取jsapi_ticket
List<Config> configs = configService.findByProperty("configKey", "jsapi_ticket");//資料庫中查詢jsapi_ticket
Config jsapiticket = Assert.isValidCollection(configs)?configs.get(0):null;
Long timestamp = System.currentTimeMillis();//當前時間
if(jsapiticket==null || Assert.isInvalidString(jsapiticket.getConfigValue()) || (timestamp-jsapiticket.getModifyTime().getTime())/1000l>=jsapiticket.getExpiresIn()){//判斷jsapi_ticket是否過期
//需要修改jsapi_ticket
configs = configService.findByProperty("configKey", "access_token");//資料庫查詢access_token
Config accesstoken = Assert.isValidCollection(configs)?configs.get(0):null;
if(accesstoken==null || Assert.isInvalidString(accesstoken.getConfigValue()) || (timestamp-accesstoken.getModifyTime().getTime())/1000l>=accesstoken.getExpiresIn()){//判斷access_token是否過期
//需要修改access_token
Map<String, Object> params = new HashMap<String, Object>();
params.put("grant_type", "client_credential");
params.put("appid", appId);
params.put("secret", SysConfig.getValue(SysConstants.Config.WX_SECRET));
HttpURLConnection conn = HttpUtil.doGet(SysConstants.Url.WX_JS_ACCESS_TOKEN_URL, params, null);
JSONObject result = JSONObject.parseObject(HttpUtil.getString(conn));
log.debug(result.toString());
if(Assert.isValidString(result.getString("access_token"))){
accesstoken = accesstoken==null?new Config():accesstoken;
accesstoken.setConfigKey("access_token");
accesstoken.setConfigValue(result.getString("access_token"));
accesstoken.setExpiresIn(result.getLong("expires_in"));
accesstoken.setModifyTime(new Date(timestamp));
configService.saveOrUpdate(accesstoken);
}else{
throw new Exception("獲取access_token出錯。");
}
}
//使用access_token獲取修改jsapi_ticket
Map<String, Object> params = new HashMap<String, Object>();
params.put("access_token", accesstoken.getConfigValue());
params.put("type", "jsapi");
HttpURLConnection conn = HttpUtil.doGet(SysConstants.Url.WX_JSAPI_TICKET, params, null);
JSONObject result = JSONObject.parseObject(HttpUtil.getString(conn));
log.debug(result.toString());
if(Assert.isValidString(result.getString("ticket"))){
jsapiticket = jsapiticket==null?new Config():jsapiticket;
jsapiticket.setConfigKey("jsapi_ticket");
jsapiticket.setConfigValue(result.getString("ticket"));
jsapiticket.setExpiresIn(result.getLong("expires_in"));
jsapiticket.setModifyTime(new Date(timestamp));
configService.saveOrUpdate(jsapiticket);
}else{
throw new Exception("獲取jspapi_ticket出錯。");
}
}
//使用jsapi_ticket加密簽名
String nonceStr = UUID.randomUUID().toString().replaceAll("-", "").substring(10, 26);
String str = "jsapi_ticket="+jsapiticket.getConfigValue()+"&noncestr="+nonceStr+"×tamp="+timestamp+"&url="+url;
String signature = DigestUtils.shaHex(str);
json.put("appId", appId);
json.put("signature", signature);
json.put("nonceStr", nonceStr);
json.put("timestamp", String.valueOf(timestamp));
log.debug(json.toString());
return json;
}
通過這個介面就可以獲取到頁面中呼叫微信的js所需要的配置資訊
wx.config({
debug: true, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。
appId: ”, // 必填,公眾號的唯一標識
timestamp: , // 必填,生成簽名的時間戳
nonceStr: ”, // 必填,生成簽名的隨機串
signature: ”,// 必填,簽名,見附錄1
jsApiList: [] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
});
三、一些重要的常量
public class SysConstants {
private SysConstants(){}
public class Url{
/**
* 獲取授權access_token的地址
* 引數 :?appid=${APPID}&secret=${SECRET}&code=${CODE}&grant_type=authorization_code
*/
public static final String WX_OAUTH_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
/**
* 獲取使用者資訊的地址
* 引數 :?access_token=${ACCESS_TOKEN}&openid=${OPENID}&lang=zh_CN
*/
public static final String WX_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo";
/**
* 獲取基礎介面的access_token地址
* 引數 :?grant_type=client_credential&appid=APPID&secret=APPSECRET
*/
public static final String WX_JS_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
/**
* 獲取jsapiticket地址
* ?access_token=ACCESS_TOKEN&type=jsapi
*/
public static final String WX_JSAPI_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
}