Java微信公众平台开发之OAuth2.0网页授权
根據(jù)官方文檔點擊查看在微信公眾號請求用戶網(wǎng)頁授權(quán)之前,開發(fā)者需要先到公眾平臺官網(wǎng)中的“開發(fā) - 接口權(quán)限 - 網(wǎng)頁服務(wù) - 網(wǎng)頁帳號 - 網(wǎng)頁授權(quán)獲取用戶基本信息”的配置選項中,修改授權(quán)回調(diào)域名。請注意,這里填寫的是域名(是一個字符串),而不是URL,因此請勿加 http:// 等協(xié)議頭,也不需要加具體的項目名,在域名空間的根目錄放一個txt文件才能驗證通過
詳細(xì)源碼點擊查看
一、兩種scope授權(quán)方式
?
- 以snsapi_base為scope發(fā)起的網(wǎng)頁授權(quán),是用來獲取進(jìn)入頁面的用戶的openid的,并且是靜默授權(quán)并自動跳轉(zhuǎn)到回調(diào)頁的。用戶感知的就是直接進(jìn)入了回調(diào)頁(往往是業(yè)務(wù)頁面)
- 以snsapi_userinfo為scope發(fā)起的網(wǎng)頁授權(quán),是用來獲取用戶的基本信息的。但這種授權(quán)需要用戶手動同意,并且由于用戶同意過,所以無須關(guān)注,就可在授權(quán)后獲取該用戶的基本信息
?
?
二、關(guān)于特殊場景下的靜默授權(quán)
- 上面已經(jīng)提到,對于以snsapi_base為scope的網(wǎng)頁授權(quán),就靜默授權(quán)的,用戶無感知
- 對于已關(guān)注公眾號的用戶,如果用戶從公眾號的會話或者自定義菜單進(jìn)入本公眾號的網(wǎng)頁授權(quán)頁,即使是scope為snsapi_userinfo,也是靜默授權(quán),用戶無感知
?
三、.用戶同意授權(quán),獲取code
我這邊是snsapi_userinfo發(fā)起的網(wǎng)頁授權(quán),redirect_uri必須用urlEncode處理下
/*** * @author phil*/ public abstract class AbstractParams {/*** 返回請求參數(shù)* @return*/public abstract Map<String,String> getParams() throws Exception; }獲取授權(quán)code請求參數(shù)AuthCodeParams.java
/*** 獲取授權(quán)code請求參數(shù)* * @author phil*/ public class AuthCodeParams extends AbstractParams{public static final String SCOPE_SNSAPIBASE = "snsapi_base"; // snsapi_base(不需要彈出授權(quán)頁面,只能獲取openid)public static final String SCOPE_SNSPAIUSERINFO = "snsapi_userinfo"; // 彈出授權(quán)頁面(獲取用戶基本信息)public static final String SCOPE_SNSAPI_LOGIN = "snsapi_login"; //網(wǎng)頁應(yīng)用目前僅填寫snsapi_loginprivate String appid;private String redirect_uri; // 使用urlencode對鏈接進(jìn)行處理private String response_type = "code"; private String scope;private String state;public AuthCodeParams() {super();}public AuthCodeParams(String appid, String redirect_uri, String response_type, String scope, String state) {super();this.appid = appid;this.redirect_uri = redirect_uri;this.response_type = response_type;this.scope = scope;this.state = state;}/*** 參數(shù)組裝* @return*/public Map<String, String> getParams() throws UnsupportedEncodingException {Map<String, String> params = new TreeMap<String, String>();params.put("appid", this.appid);params.put("redirect_uri", HttpRequestUtil.urlEncode( this.redirect_uri, HttpRequestUtil.DEFAULT_CHARSET));params.put("response_type", this.response_type);params.put("scope", this.scope);params.put("state", this.state);return params;}public String getAppid() {return appid;}public void setAppid(String appid) {this.appid = appid;}public String getRedirect_uri() {return redirect_uri;}public void setRedirect_uri(String redirect_uri) {this.redirect_uri = redirect_uri;}public String getResponse_type() {return response_type;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public String getState() {return state;}public void setState(String state) {this.state = state;} }?
尤其注意:由于授權(quán)操作安全等級較高,所以在發(fā)起授權(quán)請求時,微信會對授權(quán)鏈接做正則強(qiáng)匹配校驗,如果鏈接的參數(shù)順序不對,授權(quán)頁面將無法正常訪問;所以我都是用TreeMap拼接參數(shù)
請求鏈接的拼接
?
// 授權(quán)鏈接 private final String AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize"; AuthCodeParams authCodeParams = new AuthCodeParams(); String redirect_uri = "http://www.liebesy.com/oauth/bindWxPhone.shtml"; authCodeParams.setRedirect_uri(redirect_uri); authCodeParams.setAppid(WeiXinConfig.APP_ID); authCodeParams.setScope(AuthCodeParams.SCOPE_SNSAPIBASE);// 采用靜默授權(quán) authCodeParams.setState(MD5Util.MD5Encode("ceshi", "")); //防止被攻擊,用于校驗 String url = oAuthService.getAuthPath(authCodeParams, AUTHORIZE_URL); request.setAttribute("bindurl", url);getAuthPath()方法
/*** 獲取授權(quán)請求path* @param basic* @param path* @return* @throws Exception*/ public String getAuthPath(AbstractParams basic, String path) throws Exception{Map<String,String> params = basic.getParams();path = HttpRequestUtil.setParmas(params, path,"")+"#wechat_redirect";return path; } 如果用戶同意授權(quán),頁面將跳轉(zhuǎn)至redirect_uri/?code=CODE&state=STATE ,可以用request.getParameter("code")直接獲取到code,在此之后檢驗state是否發(fā)生變化。code作為換取access_token的票據(jù),每次用戶授權(quán)帶上的code將不一樣,code只能使用一次,5分鐘未被使用自動過期,可用redis、mc等緩存 HttpRequestUtil.java見 點擊打開鏈接四、通過code換取網(wǎng)頁授權(quán)access_token
首先請注意,這里通過code換取的是一個特殊的網(wǎng)頁授權(quán)access_token,與基礎(chǔ)支持中的access_token(該access_token用于調(diào)用其他接口)不同。公眾號可通過下述接口來獲取網(wǎng)頁授權(quán)access_token。如果網(wǎng)頁授權(quán)的作用域為snsapi_base,則本步驟中獲取到網(wǎng)頁授權(quán)access_token的同時,也獲取到了openid,snsapi_base式的網(wǎng)頁授權(quán)流程即到此為止。
獲取授權(quán)請求token的請求參數(shù)AuthTokenParams.java
/*** 獲取授權(quán)請求token的請求參數(shù)* * @author phil*/ public class AuthTokenParams extends AbstractParams {private String appid; //公眾號的唯一標(biāo)識private String secret; //公眾號的appsecretprivate String code; //填寫第一步獲取的code參數(shù)private String grant_type = "authorization_code";public AuthTokenParams() {super();}public AuthTokenParams(String appid, String secret, String code, String grant_type) {super();this.appid = appid;this.secret = secret;this.code = code;this.grant_type = grant_type;}/*** 參數(shù)組裝* @return*/public Map<String, String> getParams() {Map<String, String> params = new TreeMap<String, String>();params.put("appid", this.appid);params.put("secret", this.secret);params.put("code", this.code);params.put("grant_type", this.grant_type);return params;}public String getAppid() {return appid;}public void setAppid(String appid) {this.appid = appid;}public String getSecret() {return secret;}public void setSecret(String secret) {this.secret = secret;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getGrant_type() {return grant_type;} }授權(quán)token的返回結(jié)果AuthAccessToken.java
/*** 授權(quán)token的返回結(jié)果* @author phil* */ public class AuthAccessToken {private String access_token; // 范圍授權(quán)tokenprivate int expires_in; // 過時時間private String refresh_token;// 刷新tokenprivate String openid; // 用戶的openidprivate String scope; // 范圍private String unionid; //當(dāng)且僅當(dāng)該網(wǎng)站應(yīng)用已獲得該用戶的userinfo授權(quán)時,才會出現(xiàn)該字段public String getAccess_token() {return access_token;}public void setAccess_token(String access_token) {this.access_token = access_token;}public int getExpires_in() {return expires_in;}public void setExpires_in(int expires_in) {this.expires_in = expires_in;}public String getRefresh_token() {return refresh_token;}public void setRefresh_token(String refresh_token) {this.refresh_token = refresh_token;}public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public String getUnionid() {return unionid;}public void setUnionid(String unionid) {this.unionid = unionid;} }獲取網(wǎng)頁授權(quán)憑證方法
/*** 獲取網(wǎng)頁授權(quán)憑證* @param basic* @param path* @return*/ public AuthAccessToken getAuthAccessToken(AbstractParams basic, String path) {AuthAccessToken authAccessToken = null;//獲取網(wǎng)頁授權(quán)憑證try {//String path = HttpRequestUtil.setParmas(params, TOKENPATH, null);String result = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, path, basic.getParams(), null);if(result != null){ authAccessToken = new Gson().fromJson(result, AuthAccessToken.class);}} catch (Exception e) {logger.info("error"+e.getMessage());}return authAccessToken; }獲取網(wǎng)頁憑證
// 獲取token的鏈接 private final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";@RequestMapping(value = "bindWxPhone", method = { RequestMethod.GET }) public String prize(HttpServletRequest request, HttpServletResponse response, String url) throws Exception {AuthAccessToken authAccessToken = null;// 用戶同意授權(quán)后可以獲得code,檢驗stateString code = request.getParameter("code");if(code==null){return null;}String state = request.getParameter("state");if(state.equals(MD5Util.MD5Encode("ceshi", ""))){AuthTokenParams authTokenParams = new AuthTokenParams();authTokenParams.setAppid("");authTokenParams.setSecret("");authTokenParams.setCode(code);authAccessToken = oAuthService.getAuthAccessToken(authTokenParams, ACCESS_TOKEN_URL); }if(authAccessToken!=null){logger.info("網(wǎng)頁授權(quán)的accessToken=" + authAccessToken.getAccess_token());logger.info("正在綁定的openid=" + authAccessToken.getOpenid());}return Common.BACKGROUND_PATH + "/system/wxuserprize/bindPhone"; }五、刷新access_token(如果需要)
由于access_token擁有較短的有效期,當(dāng)access_token超時后,可以使用refresh_token進(jìn)行刷新,refresh_token有效期為30天,當(dāng)refresh_token失效之后,需要用戶重新授權(quán)。
推薦:用redis、mc之類的存儲code
?
/*** 刷新網(wǎng)頁授權(quán)驗證* @param basic 參數(shù)* @param path 請求路徑* @return*/ public String refreshAuthAccessToken(AbstractParams basic, String path) { AuthAccessToken authAccessToken = null;//刷新網(wǎng)頁授權(quán)憑證try {String result = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, path, basic.getParams(), null);if(result != null){ authAccessToken = new Gson().fromJson(result, AuthAccessToken.class);}} catch (Exception e) {authAccessToken = null;logger.info("error"+e.getMessage());}return authAccessToken==null?null:authAccessToken.getAccess_token(); }六、拉取用戶信息
如果網(wǎng)頁授權(quán)作用域為snsapi_userinfo,則此時可以通過access_token和openid拉取用戶信息了
通過網(wǎng)頁授權(quán)獲取的用戶信息AuthUserInfo.java
/*** 通過網(wǎng)頁授權(quán)獲取的用戶信息* * @author phil*/ public class AuthUserInfo {// 用戶標(biāo)識private String openid;// 用戶昵稱private String nickname;// 性別(1是男性,2是女性,0是未知)private String sex;// 國家private String country;// 省份private String province;// 城市private String city;// 用戶頭像鏈接private String headimgurl;// 用戶特權(quán)信息private List<String> privilege;// 只有在用戶將公眾號綁定到微信開放平臺帳號后,才會出現(xiàn)該字段private String unionid;public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getHeadimgurl() {return headimgurl;}public void setHeadimgurl(String headimgurl) {this.headimgurl = headimgurl;}public List<String> getPrivilege() {return privilege;}public void setPrivilege(List<String> privilege) {this.privilege = privilege;}public String getUnionid() {return unionid;}public void setUnionid(String unionid) {this.unionid = unionid;} } //獲取授權(quán)用戶信息 private final String USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo"; /*** 通過網(wǎng)頁授權(quán)獲取用戶信息* @param accessToken* @param openid* @return*/ public AuthUserInfo getAuthUserInfo(String accessToken, String openid) {AuthUserInfo authUserInfo = null; //通過網(wǎng)頁授權(quán)獲取用戶信息Map<String, String> params = new TreeMap<String, String>();params.put("openid", openid);params.put("access_token", accessToken);String result = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, USERINFO_URL, params, null); if(null != result){try {authUserInfo = new Gson().fromJson(result, AuthUserInfo.class);} catch (JsonSyntaxException e) {logger.info("transfer exception"); } }return authUserInfo; }七、檢驗授權(quán)憑證(access_token)是否有效
//判斷用戶accessToken是否有效 private final String AUTH_URL = "https://api.weixin.qq.com/sns/auth";/*** 檢驗授權(quán)憑證(access_token)是否有效* @param accessToken 網(wǎng)頁授權(quán)接口調(diào)用憑證* @param openid 用戶的唯一標(biāo)識* @return { "errcode":0,"errmsg":"ok"}表示成功 { "errcode":40003,"errmsg":"invalid openid"}失敗*/ public ResultState authToken(String accessToken,String openid){ResultState state = null;Map<String,String> params = new TreeMap<String,String>();params.put("access_token",accessToken);params.put("openid", openid);String json = HttpRequestUtil.HttpsDefaultExecute(HttpRequestUtil.GET_METHOD, AUTH_URL, params,"");if(json!=null){state = new Gson().fromJson(json, ResultState.class);}return state; }?
?
總結(jié)
以上是生活随笔為你收集整理的Java微信公众平台开发之OAuth2.0网页授权的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序开发者工具的使用
- 下一篇: Web3的流支付代表Zebec,熊市布局