1. 程式人生 > >微信公眾號之訂閱號(已認證)實現oauth2授權登入詳細步驟介紹

微信公眾號之訂閱號(已認證)實現oauth2授權登入詳細步驟介紹

注意:有朋友說部落格中用的是測試號,請大家注意,希望不要誤導大家。

一: 簡介

通過 微信公眾平臺---->許可權介面 可以得知 微信的訂閱號是沒有授權登入介面的,只有服務號才有該許可權。這點微信公眾平臺在多處反覆強調

最終的事實是:微信訂閱號是可以實現授權登入的!

二:具體實現步驟:

  1. 首先在 微信公眾平臺(https://mp.weixin.qq.com/) 【開發】----> 【基本配置】----->【伺服器配置】完成基本的配置資訊

URL: 這裡的地址我寫的是我們HTML5專案的某個Controller或Servlet的地址,例如 http://www.example.com/weixin/checkSignature.do

          注意這個CheckSignatureController中是要寫程式碼的,程式碼的業務邏輯是要驗證簽名並返回echostr欄位;還需要注意www.example.com 這個地址必須是外網地址(內網不行的), 因為當你配置完所有的基本資訊時,當提交的時候,微信公眾平臺會回答你剛才填的URL地址即http://www.example.com/weixin/checkSignature.do, 如果你填內網的地址,那麼這個URL調不到,如果簽名驗證不過去,這個基本配置就配置不成功。當你寫好這個CheckSignatureController後,還要將程式碼放到測試環境中去,以便微信公眾平臺可以回撥你這個Controller

@Controller
@RequestMapping(value="/weixin")
public class CheckSignatureController extends BaseController {
	
	private static transient final Logger log = LoggerFactory.getLogger(CheckSignatureController.class);
	public static String WEIXIN_TOKEN = "Token";
	
	
	@RequestMapping(value ="checkSignature.do", produces = "application/json; charset=utf-8")
	@ResponseBody
	public String checkSignature(HttpServletRequest request) {    
		String echostr = request.getParameter("echostr");		// 隨機字串
		
		if (isSignature(request)) {
		    return echostr;
		}
			
		return null;
	}
	
	
	// 檢查簽名
	public boolean isSignature(HttpServletRequest request) {
	    String signature = request.getParameter("signature");			// 微信加密簽名
	    String timestamp = request.getParameter("timestamp");			// 時間戳
	    String nonce = request.getParameter("nonce");				// 隨機數
			
	    String[] arr = new String[] {timestamp, nonce, WEIXIN_TOKEN};
	    Arrays.sort(arr);
	    String s = arr[0] + arr[1] + arr[2];
	    MessageDigest md;
	    byte[] digest = null;
	    try {
		md = MessageDigest.getInstance("SHA-1");
		digest = md.digest(s.getBytes("utf-8"));
	    } catch (Exception e) {
		e.printStackTrace();
	    }
	   
	    String sign = bytesToHexString(digest);
	    return signature.equals(sign);
	}
	
	
	public static final String bytesToHexString(byte[] bArray) {  
		StringBuffer sb = new StringBuffer(bArray.length);  
		String sTemp;  
		for (int i = 0; i < bArray.length; i++) {  
		    sTemp = Integer.toHexString(0xFF & bArray[i]);  
		    if (sTemp.length() < 2)  
			sb.append(0);  
		    sb.append(sTemp.toUpperCase());  
		}  
		return sb.toString().toLowerCase();  
	}  	
}

當你點選基本資訊的【提交】按鈕時,微信公眾平臺會向你專案傳送http請求:

http://www.example.com/weixin/checkSignature.do?signature=d96625be6855baa013e6c66cb9155dd38ed8deb5&echostr=8312595572152199567&timestamp=1460511115&nonce=534926942

如果這個地址能返回http請求中的echostr引數,就能提交成功,否則提交還會提示報錯

Token:是一個字串,自己隨意寫,但是要保證一點CheckSignatureController中會用到Token這個值,兩者要保證完全一致即可

EncodingAESKey: 我選擇的是隨機生成的

2: 測試號管理

登入這個地址,進行配置:http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

在這個地址中要配置完所有資訊

介面配置資訊

URL: http://www.example.com/weixin/checkSignature.do 和基本資訊配置一樣

Token:和基本配置中的token保持一致

JS介面安全域名

 域名:http://www.example.com  

體驗介面許可權表

   

    點選修改,然後 填入: www.example.com  , 注意不要帶http

三:配置自動回覆

填入:<a href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=微信開放平臺(網站應用)AppID&redirect_uri=http://www.example.com/oauth2.do&response_type=code&scope=snsapi_login&state=1#wechat_redirect">點選這裡立即體驗</a>

然後儲存

注意:href裡面的值大部分是固定的,特別注意appid引數,如果這個值錯了,你就不能完成授權登入

appid:為微信開放平臺(網站應用,一定要是是網站應用,不能是移動應用)中的AppID(https://open.weixin.qq.com 這個地址中的appid),更不要寫微信公眾平臺中的AppID

scope:引數值為snsapi_login,不要寫snsapi_base和snsapi_userinfo

redirect_uri:這個值寫HTML5專案中的一個Controller的對映地址,當用戶點選立即體驗超連結的時候,會跳轉到授權登入頁面,當點選授權登入之後微信公眾平臺會回撥這個地址,並在這個地址上追加code和state引數值

http://www.example.com/oauth2.do?code=0419p3Cc0YxTtG1nadCc0Ms7Cc09p3C8&state=1

你需要獲取code引數,從而呼叫微信公眾平臺的其他API,進而拿到微信使用者的基本資訊,如暱稱,頭像,性別等

這兩個引數非常關鍵,我在開發中就因為這兩個值沒寫對,卡了好長時間

http://www.example.com/oauth2.do 程式碼邏輯

1. 微信公眾平臺回撥你的地址:http://www.example.com/oauth2.do?code=0419p3Cc0YxTtG1nadCc0Ms7Cc09p3C8&state=1

2. 獲取code引數,呼叫微信公眾平臺access_token介面,來獲取access_token值

3.根據上步獲取的access_token值,呼叫userinfo介面來獲取使用者的基本資訊

具體程式碼如下:

@Controller
public class Oauth2Controller extends BaseController {
	private static transient final Logger log = LoggerFactory.getLogger(Oauth2Controller.class);
	static String AppID = "微信開放平臺的網站應用(不能是移動應用)https://open.weixin.qq.com (更不是公眾平臺切記)AppID";
	static String AppSecret = "微信開放平臺Secret";
	static String State = "1"; 
	
	@RequestMapping(value ="/oauth2.do", produces = "application/json; charset=utf-8")
	@ResponseBody
	public String mp(HttpServletRequest request) {    
		// 1. 獲取code ----------------------------------------------------
		String CODE = request.getParameter("code");
		String state = request.getParameter("state");
		if (!State.equals(state)) {
			return null;
		}
		
		JsonMapper json = JsonMapper.nonDefaultMapper();
		AccessTokenHelper accessTokenHelper = AccessTokenHelper.getInstance();
		AccessToken accessToken = accessTokenHelper.getAccessToken();
		if (accessToken != null) {
			Date expireEndDate = accessTokenHelper.getExpireInDate();
			if (new Date().getTime() >= expireEndDate.getTime() ) {
				// 2.accessToken過期了,需要重新獲取 使用code換取access_token--------
				accessToken = requestAccessToken(CODE, json);
			}
		} else {
			accessToken = requestAccessToken(CODE, json);
		}
		
		
		String accessTokenStr = accessToken.getAccess_token();
		String openid = accessToken.getOpenid();
		log.info("--accessToken: " + accessToken);
		
		// 3.使用access_token獲取使用者資訊-------------------------------
		// https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID
		Map<String, Object> paramMap = new HashMap<String, Object>();
		String userinfoURL = "https://api.weixin.qq.com/sns/userinfo";
		paramMap.clear();
		paramMap.put("access_token", accessTokenStr);
		paramMap.put("openid", openid);
		
		String userinfoResponse = HttpClientUtil.doPostSSL(userinfoURL, paramMap);
		UserInfo userinfo = json.fromJson(userinfoResponse, UserInfo.class);

		log.info("--userinfo: " + userinfo);
		
		return CODE;
	}

	
	
// https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
	public AccessToken requestAccessToken(String code, JsonMapper json) {		
		String accessTokenURL = "https://api.weixin.qq.com/sns/oauth2/access_token";
		
		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("appid", AppID);
		paramMap.put("secret", AppSecret);
		paramMap.put("code", code);
		paramMap.put("grant_type", "authorization_code");
		
		String accessTokenResponse = HttpClientUtil.doPostSSL(accessTokenURL, paramMap);
		AccessToken accessToken = json.fromJson(accessTokenResponse, AccessToken.class);
		AccessTokenHelper accessTokenHelper = AccessTokenHelper.getInstance();
		
		int expiresIn = accessToken.getExpires_in();
		Date expireEndDate = DateUtil.addSeconds(new Date(), expiresIn);
		accessTokenHelper.setExpireInDate(expireEndDate);
		accessTokenHelper.setAccessToken(accessToken);
		
		return accessToken;
	}
}


該程式碼用到json.fromJson()用於將json資料轉為實體類,大家可以將該方法替換成自己的方法

 HttpClientUtil.doPostSSL():是用於傳送https請求的方法,大家可以將該方法替換成自己的方法

整篇文章一直在強調要AppID使用微信開放平臺網站應用AppID,

微信開放平臺:登入:https://open.weixin.qq.com ---》 管理中心------》網站應用   如果你還沒有網站應用那就必須建立一個網站應用,提交稽核好像是要收費300元的

當Oauth2Controller開發後,放到測試環境中,此時要注意測試環境的伺服器的JDK,不能使用OpenJDK,要使用Sun的SDK,否則在傳送https請求會報錯。

注意:有的評論說這篇部落格幫助了他,有的說部落格內容有誤,我也不是很清楚,我只是把我在公司做的這個功能記錄上來,上線也一直在用,具體部落格內容有沒有問題請大家自己判斷,畢竟不是所有的部落格都100%的對,本人水平有限