一、功能描述

openId是某个微信账户对应某个小程序或者公众号的唯一标识,但openId必须经过后台解密才能获取(之前实现过前台解密,可是由于微信小程序的种种限制,前台解密无法在小程序发布后使用)

 

二、实现流程

1. 获取微信用户的登录信息;

2. 将encryptedData中的数据作为参数传给java后台

微信授权登录获取手机号 java java微信授权获取openid原理_System

3. java后台进行解密

微信授权登录获取手机号 java java微信授权获取openid原理_微信授权登录获取手机号 java_02

 

 

三、代码实现

1. 后台的解密代码



1 /**
 2      * decoding encrypted data to get openid
 3      *
 4      * @param iv
 5      * @param encryptedData
 6      * @param code
 7      * @return
 8      */
 9     @RequestMapping(value = "/decodeUserInfo", method = RequestMethod.GET)
10     private Map decodeUserInfo(String iv, String encryptedData, String code) {
11         Map map = new HashMap();
12         // login code can not be null
13         if (code == null || code.length() == 0) {
14             map.put("status", 0);
15             map.put("msg", "code 不能为空");
16             return map;
17         }
18         // mini-Program's AppID
19         String wechatAppId = "你的小程序的AppID";
20 
21         // mini-Program's session-key
22         String wechatSecretKey = "你的小程序的session-key";
23 
24         String grantType = "authorization_code";
25 
26         // using login code to get sessionId and openId
27         String params = "appid=" + wechatAppId + "&secret=" + wechatSecretKey + "&js_code=" + code + "&grant_type=" + grantType;
28 
29         // sending request
30         String sr = HttpRequest.sendGet("https://api.weixin.qq.com/sns/jscode2session", params);
31 
32         // analysis request content
33         JSONObject json = JSONObject.fromObject(sr);
34 
35         // getting session_key
36         String sessionKey = json.get("session_key").toString();
37 
38         // getting open_id
39         String openId = json.get("openid").toString();
40 
41         // decoding encrypted info with AES
42         try {
43             String result = AesCbcUtil.decrypt(encryptedData, sessionKey, iv, "UTF-8");
44             if (null != result && result.length() > 0) {
45                 map.put("status", 1);
46                 map.put("msg", "解密成功");
47 
48                 JSONObject userInfoJSON = JSONObject.fromObject(result);
49                 Map userInfo = new HashMap();
50                 userInfo.put("openId", userInfoJSON.get("openId"));
51                 userInfo.put("nickName", userInfoJSON.get("nickName"));
52                 userInfo.put("gender", userInfoJSON.get("gender"));
53                 userInfo.put("city", userInfoJSON.get("city"));
54                 userInfo.put("province", userInfoJSON.get("province"));
55                 userInfo.put("country", userInfoJSON.get("country"));
56                 userInfo.put("avatarUrl", userInfoJSON.get("avatarUrl"));
57                 userInfo.put("unionId", userInfoJSON.get("unionId"));
58                 map.put("userInfo", userInfo);
59                 return map;
60             }
61 
62 
63         } catch (Exception e) {
64             e.printStackTrace();
65         }
66         map.put("status", 0);
67         map.put("msg", "解密失败");
68         return map;
69     }



 

2. 前台代码



1 wx.login({
 2       success: function (res) {
 3         that.globalData.code = res.code;//登录凭证
 4         if (that.globalData.code) {
 5           //2、调用获取用户信息接口
 6           // 查看是否授权
 7           wx.getUserInfo({
 8             success: function (res) {
 9               that.globalData.encryptedData = res.encryptedData
10               that.globalData.iv = res.iv
11               console.log('[INFO] app.js/ ',{ encryptedData: res.encryptedData, iv: res.iv, code: that.globalData.code })
12               //3.请求自己的服务器,解密用户信息 获取unionId等加密信息
13               wx.request({
14                 url: 'https://www.****.cn/***/****/decodeUserInfo',//自己的服务接口地址
15                 method: 'get',
16                 header: {
17                   "Content-Type": "applciation/json"
18                 },
19                 data: { encryptedData: res.encryptedData, iv: res.iv, code: that.globalData.code },
20                 success: function (data) {
21 
22                   //4.解密成功后 获取自己服务器返回的结果
23                   if (data.data.status == 1) {
24                     var userInfos = data.data.userInfo;
25                     that.globalData.openId = userInfos.openId;
26                     console.log('[INFO] app.js/ userInfo:',userInfos)
27                   } else {
28                     console.log('[INFO] app.js/ 解密失败')
29                   }
30                 },
31                 fail: function () {
32                   console.log('[INFO] app.js/ 系统错误')
33                 }
34               })
35             },
36             fail: function () {
37               console.log('[INFO] app.js/ 获取用户信息失败')
38             }
39           })
40         } else {
41           console.log('[INFO] app.js/ 获取用户登录态失败!' + r.errMsg)
42         }
43       },
44       fail: function () {
45         console.log('[INFO] app.js/ 登陆失败')
46       }
47     })
48 
49     // 获取用户信息
50     wx.getSetting({
51       success: res => {
52         if (res.authSetting['scope.userInfo']) {
53           // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
54           wx.getUserInfo({
55             success: res => {
56               // 可以将 res 发送给后台解码出 unionId
57               this.globalData.userInfo = res.userInfo;
58 
59               // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
60               // 所以此处加入 callback 以防止这种情况
61               if (this.userInfoReadyCallback) {
62                 this.userInfoReadyCallback(res)
63               }
64             }
65           })
66         }
67       }
68     })
69   },



 

2. HttpRequest工具类



1 import java.io.BufferedReader;
  2 import java.io.IOException;
  3 import java.io.InputStreamReader;
  4 import java.io.PrintWriter;
  5 import java.net.URL;
  6 import java.net.URLConnection;
  7 import java.util.List;
  8 import java.util.Map;
  9 
 10 public class HttpRequest {
 11 
 12     public static void main(String[] args) {
 13         //发送 GET 请求
 14         String s=HttpRequest.sendGet("http://v.qq.com/x/cover/kvehb7okfxqstmc.html?vid=e01957zem6o", "");
 15         System.out.println(s);
 16 
 17 //        //发送 POST 请求
 18 //        String sr=HttpRequest.sendPost("http://www.toutiao.com/stream/widget/local_weather/data/?city=%E4%B8%8A%E6%B5%B7", "");
 19 //        JSONObject json = JSONObject.fromObject(sr);
 20 //        System.out.println(json.get("data"));
 21     }
 22 
 23     /**
 24      * 向指定URL发送GET方法的请求
 25      *
 26      * @param url
 27      *            发送请求的URL
 28      * @param param
 29      *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
 30      * @return URL 所代表远程资源的响应结果
 31      */
 32     public static String sendGet(String url, String param) {
 33         String result = "";
 34         BufferedReader in = null;
 35         try {
 36             String urlNameString = url + "?" + param;
 37             URL realUrl = new URL(urlNameString);
 38             // 打开和URL之间的连接
 39             URLConnection connection = realUrl.openConnection();
 40             // 设置通用的请求属性
 41             connection.setRequestProperty("accept", "*/*");
 42             connection.setRequestProperty("connection", "Keep-Alive");
 43             connection.setRequestProperty("user-agent",
 44                     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
 45             // 建立实际的连接
 46             connection.connect();
 47             // 获取所有响应头字段
 48             Map<String, List<String>> map = connection.getHeaderFields();
 49             // 遍历所有的响应头字段
 50             for (String key : map.keySet()) {
 51                 System.out.println(key + "--->" + map.get(key));
 52             }
 53             // 定义 BufferedReader输入流来读取URL的响应
 54             in = new BufferedReader(new InputStreamReader(
 55                     connection.getInputStream()));
 56             String line;
 57             while ((line = in.readLine()) != null) {
 58                 result += line;
 59             }
 60         } catch (Exception e) {
 61             System.out.println("发送GET请求出现异常!" + e);
 62             e.printStackTrace();
 63         }
 64         // 使用finally块来关闭输入流
 65         finally {
 66             try {
 67                 if (in != null) {
 68                     in.close();
 69                 }
 70             } catch (Exception e2) {
 71                 e2.printStackTrace();
 72             }
 73         }
 74         return result;
 75     }
 76 
 77     /**
 78      * 向指定 URL 发送POST方法的请求
 79      *
 80      * @param url
 81      *            发送请求的 URL
 82      * @param param
 83      *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
 84      * @return 所代表远程资源的响应结果
 85      */
 86     public static String sendPost(String url, String param) {
 87         PrintWriter out = null;
 88         BufferedReader in = null;
 89         String result = "";
 90         try {
 91             URL realUrl = new URL(url);
 92             // 打开和URL之间的连接
 93             URLConnection conn = realUrl.openConnection();
 94             // 设置通用的请求属性
 95             conn.setRequestProperty("accept", "*/*");
 96             conn.setRequestProperty("connection", "Keep-Alive");
 97             conn.setRequestProperty("user-agent",
 98                     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
 99             // 发送POST请求必须设置如下两行
100             conn.setDoOutput(true);
101             conn.setDoInput(true);
102             // 获取URLConnection对象对应的输出流
103             out = new PrintWriter(conn.getOutputStream());
104             // 发送请求参数
105             out.print(param);
106             // flush输出流的缓冲
107             out.flush();
108             // 定义BufferedReader输入流来读取URL的响应
109             in = new BufferedReader(
110                     new InputStreamReader(conn.getInputStream()));
111             String line;
112             while ((line = in.readLine()) != null) {
113                 result += line;
114             }
115         } catch (Exception e) {
116             System.out.println("发送 POST 请求出现异常!"+e);
117             e.printStackTrace();
118         }
119         //使用finally块来关闭输出流、输入流
120         finally{
121             try{
122                 if(out!=null){
123                     out.close();
124                 }
125                 if(in!=null){
126                     in.close();
127                 }
128             }
129             catch(IOException ex){
130                 ex.printStackTrace();
131             }
132         }
133         return result;
134     }
135 }



 

3. AesCbuUtil工具类



1 import org.apache.commons.codec.binary.Base64;
 2 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 3 
 4 import javax.crypto.BadPaddingException;
 5 import javax.crypto.Cipher;
 6 import javax.crypto.IllegalBlockSizeException;
 7 import javax.crypto.NoSuchPaddingException;
 8 import javax.crypto.spec.IvParameterSpec;
 9 import javax.crypto.spec.SecretKeySpec;
10 import java.io.UnsupportedEncodingException;
11 import java.security.*;
12 import java.security.spec.InvalidParameterSpecException;
13 
14 public class AesCbcUtil {
15 
16     static {
17         //BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/
18         Security.addProvider(new BouncyCastleProvider());
19     }
20 
21     /**
22      * AES解密
23      *
24      * @param data           //密文,被加密的数据
25      * @param key            //秘钥
26      * @param iv             //偏移量
27      * @param encodingFormat //解密后的结果需要进行的编码
28      * @return
29      * @throws Exception
30      */
31     public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {
32 //        initialize();
33 
34         //被加密的数据
35         byte[] dataByte = Base64.decodeBase64(data.getBytes());
36         //加密秘钥
37         byte[] keyByte = Base64.decodeBase64(key.getBytes());
38         //偏移量
39         byte[] ivByte = Base64.decodeBase64(iv.getBytes());
40 
41 
42         try {
43             Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
44 
45             SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
46 
47             AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
48             parameters.init(new IvParameterSpec(ivByte));
49 
50             cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
51 
52             byte[] resultByte = cipher.doFinal(dataByte);
53             if (null != resultByte && resultByte.length > 0) {
54                 String result = new String(resultByte, encodingFormat);
55                 return result;
56             }
57             return null;
58         } catch (NoSuchAlgorithmException e) {
59             e.printStackTrace();
60         } catch (NoSuchPaddingException e) {
61             e.printStackTrace();
62         } catch (InvalidParameterSpecException e) {
63             e.printStackTrace();
64         } catch (InvalidKeyException e) {
65             e.printStackTrace();
66         } catch (InvalidAlgorithmParameterException e) {
67             e.printStackTrace();
68         } catch (IllegalBlockSizeException e) {
69             e.printStackTrace();
70         } catch (BadPaddingException e) {
71             e.printStackTrace();
72         } catch (UnsupportedEncodingException e) {
73             e.printStackTrace();
74         }
75 
76         return null;
77     }
78 
79 }