当前位置: 首页 JAVA实现微信公众号网页授权登录

JAVA实现微信公众号网页授权登录

2019-10-15 17:29:26 作者: admin 浏览次数: 1070

请先看流程图:看懂图,就懂了一半了。

其实整体流程大体只需三步:用户点击登录按钮(其实就相当于一个链接) ---》  用户点击授权登录  ----》  实现获取用户信息代码。

然后获取用户信息代码只需三步:获取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:

复制代码
 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 }
复制代码

实体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 }
复制代码

方法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 }
复制代码

工具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  }
复制代码

 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,大功告成!整体流程已经搭建起来,读懂了这些代码差不多就明白了整个流程了,然后再看官方文档,你会觉得读起来很顺畅,而不是刚开始那种味同嚼蜡的感觉。你只需再根据官方文档仔细检查检查流程,有没有需要完善的地方,就可以了。

本文作者 阅读排行