[JAVA實現]微信公衆號網頁受權登陸

網上搜資料時,網友都說官方文檔太垃圾了不易看懂,如何如何的。如今我的整理了一個通俗易懂易上手的,但願能夠幫助到剛接觸微信接口的你。php

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

其實總體流程大致只需三步:用戶點擊登陸按鈕(其實就至關於一個連接) ---》  用戶點擊受權登陸  ----》  實現獲取用戶信息代碼。web

而後獲取用戶信息代碼只需三步:獲取code  ----》 經過code獲取access_token和openId  ---》 經過access_token和openId獲取用戶信息(包含union)。spring

 

以上即是總體套路,固然官網上也有,但具體如何實現呢?apache

不着急,我們一步一步來!json

 

第一步:微信登陸按鈕api

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

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

二、拼連接。   https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx公衆號IDxxxxx   &  redirect_uri = 受權回調頁面域名/你的action(即微信受權後跳向的地址)app

& 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:

 1 package com.wfcm.wxUitls;
 2 
 3 /**
 4 * 類名: WeixinOauth2Token </br>
 5 * 描述:  網頁受權信息  </br>
 6 * 建立時間:  2015-11-27 </br>
 7 * 發佈版本:V1.0  </br>
 8  */
 9 public class Oauth2Token {
10     // 網頁受權接口調用憑證
11     private String accessToken;
12     // 憑證有效時長
13     private int expiresIn;
14     // 用於刷新憑證
15     private String refreshToken;
16     // 用戶標識
17     private String openId;
18     // 用戶受權做用域
19     private String scope;
20 
21     public String getAccessToken() {
22         return accessToken;
23     }
24 
25     public void setAccessToken(String accessToken) {
26         this.accessToken = accessToken;
27     }
28 
29     public int getExpiresIn() {
30         return expiresIn;
31     }
32 
33     public void setExpiresIn(int expiresIn) {
34         this.expiresIn = expiresIn;
35     }
36 
37     public String getRefreshToken() {
38         return refreshToken;
39     }
40 
41     public void setRefreshToken(String refreshToken) {
42         this.refreshToken = refreshToken;
43     }
44 
45     public String getOpenId() {
46         return openId;
47     }
48 
49     public void setOpenId(String openId) {
50         this.openId = openId;
51     }
52 
53     public String getScope() {
54         return scope;
55     }
56 
57     public void setScope(String scope) {
58         this.scope = scope;
59     }
60 }
View Code

實體SNSUserInfo:

  1 package com.wfcm.wxUitls;
  2 
  3 import java.util.List;
  4 
  5 /**
  6 * 類名: SNSUserInfo </br>
  7 * 描述: 經過網頁受權獲取的用戶信息 </br>
  8 * 開發人員: wzf </br>
  9 * 建立時間:  2015-11-27 </br>
 10 * 發佈版本:V1.0  </br>
 11  */
 12 public class SNSUserInfo {
 13     // 用戶標識
 14     private String openId;
 15     // 用戶暱稱
 16     private String nickname;
 17     // 性別(1是男性,2是女性,0是未知)
 18     private int sex;
 19     // 國家
 20     private String country;
 21     // 省份
 22     private String province;
 23     // 城市
 24     private String city;
 25     // 用戶頭像連接
 26     private String headImgUrl;
 27     // 用戶特權信息
 28     private List<String> privilegeList;
 29     
 30     private String unionid;
 31 
 32     public String getUnionid() {
 33         return unionid;
 34     }
 35 
 36     public void setUnionid(String unionid) {
 37         this.unionid = unionid;
 38     }
 39 
 40     public String getOpenId() {
 41         return openId;
 42     }
 43 
 44     public void setOpenId(String openId) {
 45         this.openId = openId;
 46     }
 47 
 48     public String getNickname() {
 49         return nickname;
 50     }
 51 
 52     public void setNickname(String nickname) {
 53         this.nickname = nickname;
 54     }
 55 
 56     public int getSex() {
 57         return sex;
 58     }
 59 
 60     public void setSex(int sex) {
 61         this.sex = sex;
 62     }
 63 
 64     public String getCountry() {
 65         return country;
 66     }
 67 
 68     public void setCountry(String country) {
 69         this.country = country;
 70     }
 71 
 72     public String getProvince() {
 73         return province;
 74     }
 75 
 76     public void setProvince(String province) {
 77         this.province = province;
 78     }
 79 
 80     public String getCity() {
 81         return city;
 82     }
 83 
 84     public void setCity(String city) {
 85         this.city = city;
 86     }
 87 
 88     public String getHeadImgUrl() {
 89         return headImgUrl;
 90     }
 91 
 92     public void setHeadImgUrl(String headImgUrl) {
 93         this.headImgUrl = headImgUrl;
 94     }
 95 
 96     public List<String> getPrivilegeList() {
 97         return privilegeList;
 98     }
 99 
100     public void setPrivilegeList(List<String> privilegeList) {
101         this.privilegeList = privilegeList;
102     }
103 }
View Code

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

  1 package com.wfcm.controller;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.IOException;
  5 import java.io.InputStreamReader;
  6 import java.io.UnsupportedEncodingException;
  7 import java.net.URL;
  8 import java.net.URLConnection;
  9 import java.net.URLEncoder;
 10 import java.security.MessageDigest;
 11 import java.security.NoSuchAlgorithmException;
 12 import java.util.ArrayList;
 13 import java.util.Formatter;
 14 import java.util.HashMap;
 15 import java.util.List;
 16 import java.util.Map;
 17 import java.util.UUID;
 18 
 19 import javax.servlet.http.HttpServletRequest;
 20 import javax.servlet.http.HttpServletResponse;
 21 
 22 import org.apache.http.client.utils.URLEncodedUtils;
 23 import org.json.JSONObject;
 24 import org.slf4j.Logger;
 25 import org.slf4j.LoggerFactory;
 26 import org.springframework.beans.factory.annotation.Autowired;
 27 import org.springframework.stereotype.Controller;
 28 import org.springframework.web.bind.annotation.RequestMapping;
 29 import org.springframework.web.bind.annotation.ResponseBody;
 30 
 31 import com.alibaba.fastjson.JSON;
 32 import com.alibaba.fastjson.JSONArray;
 33 import com.alibaba.fastjson.util.IOUtils;
 34 import com.wfcm.annotation.IgnoreSign;
 35 import com.wfcm.annotation.IgnoreToken;
 36 import com.wfcm.entity.WfMemberEntity;
 37 import com.wfcm.service.WfMemberService;
 38 import com.wfcm.service.WfMemberSessionService;
 39 import com.wfcm.utils.FastJSONUtils;
 40 import com.wfcm.utils.NetUtil;
 41 import com.wfcm.utils.R;
 42 import com.wfcm.wxUitls.AccessToken;
 43 import com.wfcm.wxUitls.JsapiTicket;
 44 import com.wfcm.wxUitls.Oauth2Token;
 45 import com.wfcm.wxUitls.SNSUserInfo;
 46 
 47 @Controller
 48 @RequestMapping("/wx")
 49 @ResponseBody
 50 public class WxController {
 51 
 52     private static  Logger log = LoggerFactory.getLogger(WxController.class);
 53      
 54     
 55     /**
 56      * 向指定URL發送GET方法的請求
 57      * 
 58      * @param url
 59      *            發送請求的URL
 60      * @param param
 61      *            請求參數,請求參數應該是 name1=value1&name2=value2 的形式。
 62      * @return URL 所表明遠程資源的響應結果
 63      * 
 64      * 用戶贊成受權,獲取code
 65      */ 
 66     @RequestMapping("/authorize")
 67     @ResponseBody
 68     @IgnoreToken
 69     public static R authorize() {
 70         String appid = "wxbb000000000e";
 71         //String uri ="wftest.zzff.net/wx/weixinLogin"; 
 72         String uri = urlEncodeUTF8("wftest.zzff.net/api/wx/weixinLogin");
 73         String result = "";
 74         BufferedReader in = null;
 75         try {
 76             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";
 77             
 78             URL realUrl = new URL(urlNameString);
 79             // 打開和URL之間的鏈接
 80             URLConnection connection = realUrl.openConnection();
 81             // 設置通用的請求屬性
 82             connection.setRequestProperty("accept", "*/*");
 83             connection.setRequestProperty("connection", "Keep-Alive");
 84             connection.setRequestProperty("user-agent",
 85                     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
 86             // 創建實際的鏈接
 87             connection.connect();
 88             // 獲取全部響應頭字段
 89             Map<String, List<String>> map = connection.getHeaderFields();
 90             // 遍歷全部的響應頭字段
 91             for (String key : map.keySet()) {
 92                 System.out.println(key + "--->" + map.get(key));
 93             }
 94             // 定義 BufferedReader輸入流來讀取URL的響應
 95             in = new BufferedReader(new InputStreamReader(
 96                     connection.getInputStream()));
 97             String line =null;
 98             while ((line = in.readLine()) != null) {
 99                 result += line;
100             }
101             /*  com.alibaba.fastjson.JSONObject jsonObj= FastJSONUtils.getJSONObject(result);  
102                 String access_token = jsonObj.getString("access_token");
103                 long expires_in = Long.valueOf(jsonObj.getString("expires_in"));
104             */
105         } catch (Exception e) {
106             System.out.println("發送GET請求出現異常!" + e);
107             e.printStackTrace();
108         }
109         // 使用finally塊來關閉輸入流
110         finally {
111             try {
112                 if (in != null) {
113                     in.close();
114                 }
115             } catch (Exception e2) {
116                 e2.printStackTrace();
117             }
118         }
119         return  R.ok(result);
120     }    
121     
122     @RequestMapping("/weixinLogin")
123     @ResponseBody
124     @IgnoreToken
125     @IgnoreSign
126     public void weixinLogin(HttpServletRequest request,HttpServletResponse response) throws Exception {  
127         // 用戶贊成受權後,能獲取到code
128         Map<String, String[]> params = request.getParameterMap();//針對get獲取get參數  
129         String[] codes = params.get("code");//拿到code的值 
130         String code = codes[0];//code  
131         //String[] states = params.get("state");
132         //String state = states[0];//state 
133         
134         System.out.println("****************code:"+code);          
135         // 用戶贊成受權
136         if (!"authdeny".equals(code)) {
137              // 獲取網頁受權access_token
138             Oauth2Token oauth2Token = getOauth2AccessToken("wxb0000000000e", "4c22222233333335555a9", code);
139             System.out.println("***********************************oauth2Token信息:"+oauth2Token.toString());
140             // 網頁受權接口訪問憑證
141             String accessToken = oauth2Token.getAccessToken();
142             // 用戶標識
143             String openId = oauth2Token.getOpenId();
144             // 獲取用戶信息
145             SNSUserInfo snsUserInfo = getSNSUserInfo(accessToken, openId);
146             System.out.println("***********************************用戶信息unionId:"+snsUserInfo.getUnionid()+"***:"+snsUserInfo.getNickname());
147             // 設置要傳遞的參數
148             
149             //具體業務start
150 
151            //具體業務end
152 
153             String url = "http://wftest.zzff.net/#/biddd?from=login&tokenId="+snsUserInfo.getUnionid();
154             
155             response.sendRedirect(url); 
156             return ;
157         }
158     }  
159 
160     
161     /**
162      * 獲取網頁受權憑證
163      * 
164      * @param appId 公衆帳號的惟一標識
165      * @param appSecret 公衆帳號的密鑰
166      * @param code
167      * @return WeixinAouth2Token
168      */
169     public static Oauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
170         Oauth2Token wat = null;
171         // 拼接請求地址
172         String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
173         requestUrl = requestUrl.replace("APPID", appId);
174         requestUrl = requestUrl.replace("SECRET", appSecret);
175         requestUrl = requestUrl.replace("CODE", code);
176         // 獲取網頁受權憑證
177         com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(NetUtil.get(requestUrl));
178         if (null != jsonObject) {
179             try {
180                 wat = new Oauth2Token();
181                 wat.setAccessToken(jsonObject.getString("access_token"));
182                 wat.setExpiresIn(jsonObject.getInteger("expires_in"));
183                 wat.setRefreshToken(jsonObject.getString("refresh_token"));
184                 wat.setOpenId(jsonObject.getString("openid"));
185                 wat.setScope(jsonObject.getString("scope"));
186             } catch (Exception e) {
187                 wat = null;
188                 int errorCode = jsonObject.getInteger("errcode");
189                 String errorMsg = jsonObject.getString("errmsg");
190                 log.error("獲取網頁受權憑證失敗 errcode:{} errmsg:{}", errorCode, errorMsg);
191             }
192         }
193         return wat;
194     }
195     
196     /**
197      * 經過網頁受權獲取用戶信息
198      * 
199      * @param accessToken 網頁受權接口調用憑證
200      * @param openId 用戶標識
201      * @return SNSUserInfo
202      */
203     public static SNSUserInfo getSNSUserInfo(String accessToken, String openId) {
204         SNSUserInfo snsUserInfo = null;
205         // 拼接請求地址
206         String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
207         requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
208         // 經過網頁受權獲取用戶信息
209         com.alibaba.fastjson.JSONObject jsonObject =  JSON.parseObject(NetUtil.get(requestUrl));
210 
211         if (null != jsonObject) {
212             try {
213                 snsUserInfo = new SNSUserInfo();
214                 // 用戶的標識
215                 snsUserInfo.setOpenId(jsonObject.getString("openid"));
216                 // 暱稱
217                 snsUserInfo.setNickname(jsonObject.getString("nickname"));
218                 // 性別(1是男性,2是女性,0是未知)
219                 snsUserInfo.setSex(jsonObject.getInteger("sex"));
220                 // 用戶所在國家
221                 snsUserInfo.setCountry(jsonObject.getString("country"));
222                 // 用戶所在省份
223                 snsUserInfo.setProvince(jsonObject.getString("province"));
224                 // 用戶所在城市
225                 snsUserInfo.setCity(jsonObject.getString("city"));
226                 // 用戶頭像
227                 snsUserInfo.setHeadImgUrl(jsonObject.getString("headimgurl"));
228                 // 用戶特權信息
229                 List<String> list = JSON.parseArray(jsonObject.getString("privilege"),String.class);
230                 snsUserInfo.setPrivilegeList(list);
231                 //與開放平臺共用的惟一標識,只有在用戶將公衆號綁定到微信開放平臺賬號後,纔會出現該字段。
232                 snsUserInfo.setUnionid(jsonObject.getString("unionid"));
233             } catch (Exception e) {
234                 snsUserInfo = null;
235                 int errorCode = jsonObject.getInteger("errcode");
236                 String errorMsg = jsonObject.getString("errmsg");
237                 log.error("獲取用戶信息失敗 errcode:{} errmsg:{}", errorCode, errorMsg);
238             }
239         }
240         return snsUserInfo;
241     }
242    
243     /**
244      * URL編碼(utf-8)
245      * 
246      * @param source
247      * @return
248      */
249     public static String urlEncodeUTF8(String source) {
250         String result = source;
251         try {
252             result = java.net.URLEncoder.encode(source, "utf-8");
253         } catch (UnsupportedEncodingException e) {
254             e.printStackTrace();
255         }
256         return result;
257     }
258     
259     private static String byteToHex(final byte[] hash) {
260         Formatter formatter = new Formatter();
261         for (byte b : hash)
262         {
263             formatter.format("%02x", b);
264         }
265         String result = formatter.toString();
266         formatter.close();
267         return result;
268     }
269 
270     private static String create_nonce_str() {
271         return UUID.randomUUID().toString();
272     }
273 
274     private static String create_timestamp() {
275         return Long.toString(System.currentTimeMillis() / 1000);
276     }
277 }
View Code

工具NetUtil:

 1 package com.wfcm.utils;
 2 
 3 import java.io.BufferedInputStream;
 4 import java.io.IOException;
 5 import java.io.InputStreamReader;
 6 import java.io.UnsupportedEncodingException;
 7 import java.util.ArrayList;
 8 import java.util.Collection;
 9 import java.util.List;
10 import java.util.Map;
11 
12 import org.apache.commons.httpclient.NameValuePair;
13 import org.apache.http.HttpEntity;
14 import org.apache.http.HttpResponse;
15 import org.apache.http.client.entity.UrlEncodedFormEntity;
16 import org.apache.http.client.methods.HttpGet;
17 import org.apache.http.client.methods.HttpPost;
18 import org.apache.http.impl.client.CloseableHttpClient;
19 import org.apache.http.impl.client.HttpClientBuilder;
20 import org.apache.http.message.BasicNameValuePair;
21 
22 /**
23  * Created by Song on 2016/11/28.
24  * 基於HttpClient提供網絡訪問工具
25  */
26 public final class NetUtil {
27     public static CloseableHttpClient httpClient = HttpClientBuilder.create().build();
28 
29     /**
30      * get請求獲取String類型數據
31      * @param url 請求連接
32      * @return
33      */
34     public static String get(String url){
35         StringBuffer sb = new StringBuffer();
36         HttpGet httpGet = new HttpGet(url);
37         try {
38             HttpResponse response = httpClient.execute(httpGet);           //1
39 
40             HttpEntity entity = response.getEntity();
41             InputStreamReader reader = new InputStreamReader(entity.getContent(),"utf-8");
42             char [] charbufer;
43             while (0<reader.read(charbufer=new char[10])){
44                 sb.append(charbufer);
45             }
46         }catch (IOException e){//1
47             e.printStackTrace();
48         }finally {
49             httpGet.releaseConnection();
50         }
51         return sb.toString();
52     }
53 
54     /**
55      * post方式請求數據
56      * @param url 請求連接
57      * @param data post數據體
58      * @return
59      */
60     @SuppressWarnings("unchecked")
61     public static String post(String url, Map<String,String> data){
62         StringBuffer sb = new StringBuffer();
63         HttpPost httpPost = new HttpPost(url);
64         List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
65         if(null != data) {
66             for (String key : data.keySet()) {
67                 valuePairs.addAll((Collection<? extends NameValuePair>) new BasicNameValuePair(key, data.get(key)));
68             }
69         }
70         try {
71             httpPost.setEntity(new UrlEncodedFormEntity((List<? extends org.apache.http.NameValuePair>) valuePairs));
72             HttpResponse response = httpClient.execute(httpPost);
73             HttpEntity httpEntity = response.getEntity();
74             BufferedInputStream bis = new BufferedInputStream(httpEntity.getContent());
75             byte [] buffer;
76             while (0<bis.read(buffer=new byte[128])){
77                 sb.append(new String(buffer,"utf-8"));
78             }
79         }catch (UnsupportedEncodingException e){//數據格式有誤
80             e.printStackTrace();
81         }catch (IOException e){//請求出錯
82             e.printStackTrace();
83         }finally {
84             httpPost.releaseConnection();
85         }
86         return sb.toString();
87     }
88     
89     
90  }
View Code

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

 

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

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

相關文章
相關標籤/搜索