[JAVA實現]微信公眾號網頁授權登錄


網上搜資料時,網友都說官方文檔太垃圾了不易看懂,如何如何的。現在個人整理了一個通俗易懂易上手的,希望可以幫助到剛接觸微信接口的你。

請看流程圖!看懂圖,就懂了一半了:

其實整體流程大體只需三步:用戶點擊登錄按鈕(其實就相當於一個鏈接) ---》  用戶點擊授權登錄  ----》  實現獲取用戶信息代碼。

然后獲取用戶信息代碼只需三步:獲取code  ----》 通過code獲取access_token和openId  ---》 通過access_token和openId獲取用戶信息(包含union)。

 

以上便是整體套路,當然官網上也有,但具體如何實現呢?

不着急,咱們一步一步來!

 

第一步:微信登錄按鈕

它其實就是一個連接,不過想得到這個鏈接,有一點點麻煩。

1、設置。 微信公眾平台---》接口權限---》網頁授權---》修改 ---》設置網頁授權域名(域名,不含http://),其實就是微信調你的java方法的項目路徑或項目域名,如:www.zzff.net/pp ---》點擊設置后彈出頁面(大致意思,將MP_verify_31qRIDcjN8ZD1lVJ.txt放在你項目路徑下面,如:www.ffzz.net/pp/MP_verify_31qRIDcjN8ZD1lVJ.txt 能訪問到) ---》點擊確認,授權回調頁面域名設置成功!

2、拼鏈接。   https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx公眾號IDxxxxx   &  redirect_uri = 授權回調頁面域名/你的action(即微信授權后跳向的地址)

& response_type=code(固定的) & scope = snsapi_userinfo(或者snsapi_base默認授權)  & state=STATE#wechat_redirect

 如:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

這個鏈接中參數的具體含義,官方解釋如下:

參數 是否必須 說明
appid 公眾號的唯一標識
redirect_uri 授權后重定向的回調鏈接地址,請使用urlencode對鏈接進行處理
response_type 返回類型,請填寫code
scope 應用授權作用域,snsapi_base (不彈出授權頁面,直接跳轉,只能獲取用戶openid),snsapi_userinfo (彈出授權頁面,可通過openid拿到昵稱、性別、所在地。並且,即使在未關注的情況下,只要用戶授權,也能獲取其信息
state 重定向后會帶上state參數,開發者可以填寫a-zA-Z0-9的參數值,最多128字節 
#wechat_redirect 無論直接打開還是做頁面302重定向時候,必須帶此參數

 

第二步:授權確認登錄 

這一步最簡單,第一步登錄鏈接拼好后,在手機微信中打開,微信便會跳轉到確認授權頁面,點擊確認授權即可。(這一步,不用開發者做處理!)

 

第三步:獲取用戶信息 (重點)
這一步便是真正的代碼實現的地方。開篇便講了,它只需三步:獲取code  ----》 通過code獲取access_token和openId  ---》 通過access_token和openId獲取用戶信息。

First:   獲取code

Second:  獲取網頁授權access_token和openId  

Third:通過access_token和openId獲取用戶信息。

關於union機制的細節:如果開發者需要公眾號微信登錄和APP微信登錄共用一個微信ID,那個就需要union機制了。其實很簡單,只需在微信開放平台(open.weixin.qq.com)綁定公眾號,獲取用戶信息時就會返回union字段。

 

具體代碼實現為:實體 ----  方法  --- 工具

實體Oauth2Token:

package com.wfcm.wxUitls;

/**
* 類名: WeixinOauth2Token </br>
* 描述: 網頁授權信息 </br>
* 創建時間: 2015-11-27 </br>
* 發布版本:V1.0 </br>
*/
public class Oauth2Token {
// 網頁授權接口調用憑證
private String accessToken;
// 憑證有效時長
private int expiresIn;
// 用於刷新憑證
private String refreshToken;
// 用戶標識
private String openId;
// 用戶授權作用域
private String scope;

public String getAccessToken() {
return accessToken;
}

public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}

public int getExpiresIn() {
return expiresIn;
}

public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}

public String getRefreshToken() {
return refreshToken;
}

public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}

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;
}
}

 

實體SNSUserInfo:

package com.wfcm.wxUitls;

import java.util.List;

/**
* 類名: SNSUserInfo </br>
* 描述: 通過網頁授權獲取的用戶信息 </br>
* 開發人員: wzf </br>
* 創建時間: 2015-11-27 </br>
* 發布版本:V1.0 </br>
*/
public class SNSUserInfo {
// 用戶標識
private String openId;
// 用戶昵稱
private String nickname;
// 性別(1是男性,2是女性,0是未知)
private int sex;
// 國家
private String country;
// 省份
private String province;
// 城市
private String city;
// 用戶頭像鏈接
private String headImgUrl;
// 用戶特權信息
private List<String> privilegeList;

private String unionid;

public String getUnionid() {
return unionid;
}

public void setUnionid(String unionid) {
this.unionid = 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 int getSex() {
return sex;
}

public void setSex(int 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> getPrivilegeList() {
return privilegeList;
}

public void setPrivilegeList(List<String> privilegeList) {
this.privilegeList = privilegeList;
}
}

 

方法WxController(其中authorize() 方法就是請求第一步獲取的鏈接):

package com.wfcm.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.client.utils.URLEncodedUtils;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.util.IOUtils;
import com.wfcm.annotation.IgnoreSign;
import com.wfcm.annotation.IgnoreToken;
import com.wfcm.entity.WfMemberEntity;
import com.wfcm.service.WfMemberService;
import com.wfcm.service.WfMemberSessionService;
import com.wfcm.utils.FastJSONUtils;
import com.wfcm.utils.NetUtil;
import com.wfcm.utils.R;
import com.wfcm.wxUitls.AccessToken;
import com.wfcm.wxUitls.JsapiTicket;
import com.wfcm.wxUitls.Oauth2Token;
import com.wfcm.wxUitls.SNSUserInfo;

@Controller
@RequestMapping("/wx")
@ResponseBody
public class WxController {

private static Logger log = LoggerFactory.getLogger(WxController.class);


/**
* 向指定URL發送GET方法的請求
*
* @param url
* 發送請求的URL
* @param param
* 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。
* @return URL 所代表遠程資源的響應結果
*
* 用戶同意授權,獲取code
*/
@RequestMapping("/authorize")
@ResponseBody
@IgnoreToken
public static R authorize() {
String appid = "wxbb000000000e";
//String uri ="wftest.zzff.net/wx/weixinLogin";
String uri = urlEncodeUTF8("wftest.zzff.net/api/wx/weixinLogin");
String result = "";
BufferedReader in = null;
try {
String urlNameString = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appid+"&redirect_uri="+uri+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";

URL realUrl = new URL(urlNameString);
// 打開和URL之間的連接
URLConnection connection = realUrl.openConnection();
// 設置通用的請求屬性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立實際的連接
connection.connect();
// 獲取所有響應頭字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍歷所有的響應頭字段
for (String key : map.keySet()) {
System.out.println(key + "--->" + map.get(key));
}
// 定義 BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line =null;
while ((line = in.readLine()) != null) {
result += line;
}
/* com.alibaba.fastjson.JSONObject jsonObj= FastJSONUtils.getJSONObject(result);
String access_token = jsonObj.getString("access_token");
long expires_in = Long.valueOf(jsonObj.getString("expires_in"));
*/
} catch (Exception e) {
System.out.println("發送GET請求出現異常!" + e);
e.printStackTrace();
}
// 使用finally塊來關閉輸入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return R.ok(result);
}

@RequestMapping("/weixinLogin")
@ResponseBody
@IgnoreToken
@IgnoreSign
public void weixinLogin(HttpServletRequest request,HttpServletResponse response) throws Exception {
// 用戶同意授權后,能獲取到code
Map<String, String[]> params = request.getParameterMap();//針對get獲取get參數
String[] codes = params.get("code");//拿到code的值
String code = codes[0];//code
//String[] states = params.get("state");
//String state = states[0];//state

System.out.println("****************code:"+code);
// 用戶同意授權
if (!"authdeny".equals(code)) {
// 獲取網頁授權access_token
Oauth2Token oauth2Token = getOauth2AccessToken("wxb0000000000e", "4c22222233333335555a9", code);
System.out.println("***********************************oauth2Token信息:"+oauth2Token.toString());
// 網頁授權接口訪問憑證
String accessToken = oauth2Token.getAccessToken();
// 用戶標識
String openId = oauth2Token.getOpenId();
// 獲取用戶信息
SNSUserInfo snsUserInfo = getSNSUserInfo(accessToken, openId);
System.out.println("***********************************用戶信息unionId:"+snsUserInfo.getUnionid()+"***:"+snsUserInfo.getNickname());
// 設置要傳遞的參數

//具體業務start

//具體業務end

String url = "http://wftest.zzff.net/#/biddd?from=login&tokenId="+snsUserInfo.getUnionid();

response.sendRedirect(url);
return ;
}
}


/**
* 獲取網頁授權憑證
*
* @param appId 公眾賬號的唯一標識
* @param appSecret 公眾賬號的密鑰
* @param code
* @return WeixinAouth2Token
*/
public static Oauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
Oauth2Token wat = null;
// 拼接請求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", appId);
requestUrl = requestUrl.replace("SECRET", appSecret);
requestUrl = requestUrl.replace("CODE", code);
// 獲取網頁授權憑證
com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl));
if (null != jsonObject) {
try {
wat = new Oauth2Token();
wat.setAccessToken(jsonObject.getString("access_token"));
wat.setExpiresIn(jsonObject.getInteger("expires_in"));
wat.setRefreshToken(jsonObject.getString("refresh_token"));
wat.setOpenId(jsonObject.getString("openid"));
wat.setScope(jsonObject.getString("scope"));
} catch (Exception e) {
wat = null;
int errorCode = jsonObject.getInteger("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("獲取網頁授權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return wat;
}

/**
* 通過網頁授權獲取用戶信息
*
* @param accessToken 網頁授權接口調用憑證
* @param openId 用戶標識
* @return SNSUserInfo
*/
public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
SNSUserInfo snsUserInfo = null;
// 拼接請求地址
String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
// 通過網頁授權獲取用戶信息
com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl));

if (null != jsonObject) {
try {
snsUserInfo = new SNSUserInfo();
// 用戶的標識
snsUserInfo.setOpenId(jsonObject.getString("openid"));
// 昵稱
snsUserInfo.setNickname(jsonObject.getString("nickname"));
// 性別(1是男性,2是女性,0是未知)
snsUserInfo.setSex(jsonObject.getInteger("sex"));
// 用戶所在國家
snsUserInfo.setCountry(jsonObject.getString("country"));
// 用戶所在省份
snsUserInfo.setProvince(jsonObject.getString("province"));
// 用戶所在城市
snsUserInfo.setCity(jsonObject.getString("city"));
// 用戶頭像
snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
// 用戶特權信息
List<String> list = JSON.parseArray(jsonObject.getString("privilege"),String.class);
snsUserInfo.setPrivilegeList(list);
//與開放平台共用的唯一標識,只有在用戶將公眾號綁定到微信開放平台帳號后,才會出現該字段。
snsUserInfo.setUnionid(jsonObject.getString("unionid"));
} catch (Exception e) {
snsUserInfo = null;
int errorCode = jsonObject.getInteger("errcode");
String errorMsg = jsonObject.getString("errmsg");
log.error("獲取用戶信息失敗 errcode:{} errmsg:{}", errorCode, errorMsg);
}
}
return snsUserInfo;
}

/**
* URL編碼(utf-8)
*
* @param source
* @return
*/
public static String urlEncodeUTF8(String source) {
String result = source;
try {
result = java.net.URLEncoder.encode(source, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}

private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}

private static String create_nonce_str() {
return UUID.randomUUID().toString();
}

private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
}

工具NetUtil:

package com.wfcm.utils;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.NameValuePair;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;

/**
* Created by Song on 2016/11/28.
* 基於HttpClient提供網絡訪問工具
*/
public final class NetUtil {
public static CloseableHttpClient httpClient = HttpClientBuilder.create().build();

/**
* get請求獲取String類型數據
* @param url 請求鏈接
* @return
*/
public static String get(String url){
StringBuffer sb = new StringBuffer();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse response = httpClient.execute(httpGet); //1

HttpEntity entity = response.getEntity();
InputStreamReader reader = new InputStreamReader(entity.getContent(),"utf-8");
char [] charbufer;
while (0<reader.read(charbufer=new char[10])){
sb.append(charbufer);
}
}catch (IOException e){//1
e.printStackTrace();
}finally {
httpGet.releaseConnection();
}
return sb.toString();
}

/**
* post方式請求數據
* @param url 請求鏈接
* @param data post數據體
* @return
*/
@SuppressWarnings("unchecked")
public static String post(String url, Map<String,String> data){
StringBuffer sb = new StringBuffer();
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
if(null != data) {
for (String key : data.keySet()) {
valuePairs.addAll((Collection<? extends NameValuePair>) new BasicNameValuePair(key, data.get(key)));
}
}
try {
httpPost.setEntity(new UrlEncodedFormEntity((List<? extends org.apache.http.NameValuePair>) valuePairs));
HttpResponse response = httpClient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
BufferedInputStream bis = new BufferedInputStream(httpEntity.getContent());
byte [] buffer;
while (0<bis.read(buffer=new byte[128])){
sb.append(new String(buffer,"utf-8"));
}
}catch (UnsupportedEncodingException e){//數據格式有誤
e.printStackTrace();
}catch (IOException e){//請求出錯
e.printStackTrace();
}finally {
httpPost.releaseConnection();
}
return sb.toString();
}


}

 R類:

package com.wfcm.utils;

import java.util.HashMap;
import java.util.Map;

/**
* 返回數據
*
* @author xlf
* @email xlfbe696@gmail.com
* @date 2017年4月19日 上午11:58:56
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;

public static final String SUCCESS = "success";

public static final String EXCEPTION = "exception";

public static final Integer SUCCESSCODE = 0;

public static final Integer EXCEPTIONCODE = 500;

public R() {
put("errCode", 0);
put("msg", SUCCESS);
}

public R(int code, String msg){
put("errCode", code);
put("msg", msg);
}

public static R error() {
return error(500, "未知異常,請聯系管理員");
}

public static R error(String msg) {
return error(500, msg);
}

public static R error(int code, String msg) {
R r = new R();
r.put("errCode", code);
r.put("msg", msg);
return r;
}

public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}

public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}

public static R ok() {
return new R();
}

public R put(String key, Object value) {
super.put(key, value);
return this;
}
}

 

OK,大功告成!整體流程已經搭建起來,讀懂了這些代碼差不多就明白了整個流程了,然后再看官方文檔,你會覺得讀起來很順暢,而不是剛開始那種味同嚼蠟的感覺。你只需再根據官方文檔仔細檢查檢查流程,有沒有需要完善的地方,就可以了。

還等什么呢,趕快敲實現功能吧!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM