微信小程序-登錄

環境

後端:java

簡介 

     小程序官方API URL:  https://developers.weixin.qq.com/miniprogram/dev/api/api-login.html

     小程序登錄 流程圖

 

 -------------------------------------------------------------------------------實測開始-------------------------------------------------------------------------

小程序客戶端(實測)

//index.js
//獲取應用實例
const app = getApp()
Page({
  getPhoneNumber: function (e) {
    console.log("errMsg="+e.detail.errMsg)
    console.log("iv="+e.detail.iv)
    console.log("encryptedData="+e.detail.encryptedData)
    if (e.detail.errMsg == 'getPhoneNumber:fail user deny') {
      wx.showModal({
        title: '提示',
        showCancel: false,
        content: '未授權',
        success: function (res) { }
      })
    } else {
      wx.showModal({
        title: '提示',
        showCancel: false,
        content: '同意授權',
        success: function (res) { 
          wx.request({
            url: 'http://xxxxxxx/aglie/user/myhuser/v1/appletLogin',
            method: 'POST', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
            data: {
              code: app.globalData.codeInfo,//獲取openid的話 需要向後臺傳遞code,利用code請求api獲取openid
              encryptedData: e.detail.encryptedData,//獲取encryptedData
              iv: e.detail.iv, //獲取iv
              type:'1',
            },
            header: {
              //'content-type': 'application/json' // 默認值
              'Content-Type': 'application/x-www-form-urlencoded'
            },
            success: function (res) {
              console.log("res::=" + res.data);
            }
          })

        }
      })
    }
  },

  getUserInfo: function(e) {
    wx.request({
      url: 'http://xxxxxxx/aglie/user/myhuser/v1/appletLogin',
      method: 'POST', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
      data: {
        code: app.globalData.codeInfo,//獲取openid的話 需要向後臺傳遞code,利用code請求api獲取openid
        encryptedData: e.detail["encryptedData"],//獲取encryptedData
        iv: e.detail["iv"], //獲取iv
        type:'0',
      },
      header: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      success:function(res){
        console.log("res::=" + res.data);
      }
    })

    app.globalData.userInfo = e.detail.userInfo
    this.setData({
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  }
})

 java服務端

一.定義final變量

	// AppID(小程序ID) 小程序唯一標識 (在微信小程序管理後臺獲取)
	private static final String   APPID = "wx8a503ea211d48xxx";
	// 小程序的 AppSecret(小程序密鑰)  (在微信小程序管理後臺獲取)
	private static final String  APPSECRET  = "d3455f3cfb9685c602b60e48caa0xxxx";
	// 授權(必填) 填寫爲 authorization_code
	private static final String  GRANT_TYPE = "authorization_code";

二.調用官方API,返回客戶端OpenId信息 (實測)

1.API請求參數設計

參數名稱 必選 類型 描述
type  true   String  0獲取用戶基本信息  1獲取用戶電話號碼
code  true   String  小程序code
iv    String  加密算法的初始向量
encryptedData    String  加密數據
      注意(此處使用 encryptedData 加密與iv偏移量)加密解密處理

1.API 調用

/**
     * @throws Exception 
	 *  
	 * @Title: appletLogin   
	 * @Description: TODO(根據小程序客戶端code,返回登錄狀態)  採用順序方式:1.code  2.用戶INFO 2.Phone
	 * @param:  code  客戶端code
	 * @param:  encryptedData  包括敏感數據在內的完整用戶信息的加密數據
	 * @param:  iv  加密算法的初始向量
	 * @param:  type 0用戶基礎信息    1用戶電話號碼
	 * 
	 * @return: Map<String,Object>      
	 * @throws
	 */
	@ResponseBody
	@RequestMapping(value = "v1/appletLogin" , method = RequestMethod.POST)
	public Map<String, Object> appletLogin(HttpServletRequest request,HttpServletResponse response
			,@RequestParam(required=true) String code
			,String encryptedData,String iv,String type) throws Exception{
	     Map<String, Object> map = Maps.newHashMap();
	     Map<String, Object> mapListInfo= Maps.newHashMap();
			//code獲取參數
	     if (StringUtils.isBlank(code)) {
	    	 map.put("status", "fail");
	    	 map.put("message", "code is not null.");
	    	 return map;
	     }
	     //定義變量
	     String str_code ="";
	     String str_encryptedData ="";
	     String str_iv ="";
	     
	     String str_session_key= "";
	     String str_status= "";
	     String str_message="";
	      
	     String str_openId="";
	     String str_nickName= "";
	     String str_gender= "";
	     String str_city= "";
	     String str_province= "";
	     String str_country= "";
	     String str_avatarUrl= "";
	     
	     String str_phoneNumber= "";
	     String str_purePhoneNumber= "";
	     String str_countryCode= "";

		 //獲取客戶端請求code
	      str_code =request.getParameter("code");
	      str_encryptedData =request.getParameter("encryptedData");
	      str_iv =request.getParameter("iv");

	     //1.code請求騰訊API(返回openid,session_key)
	     TenApplet tenApplet=new TenApplet();
	     Map<String, Object> map_vale =tenApplet.get_openid_key(str_code);
	      str_openId= (String) map_vale.get("openId");
	      str_session_key= (String)map_vale.get("session_key");
	      str_status= (String)map_vale.get("status");
	      str_message= (String)map_vale.get("message");
	     //處理異常 code 
	     switch (str_status) {
			case "40163":
				 map.put("status", str_status);
		    	 map.put("message", str_message);
				return map;
			case "40029":
				map.put("status", str_status);
				map.put("message", str_message);
				return map;
			case "40125":
				map.put("status", str_status);
				map.put("message", str_message);
				return map;
			}

		//1.1 調用AES解密(返回 encryptedData 數據) 獲取用戶信息和電話號碼
	     if(StringUtils.isNotBlank(str_encryptedData)){
		 Map<String, Object> aes_map_vale =tenApplet.get_encryptedData(str_encryptedData, str_session_key, str_iv,type);
			 if("0".equals(type)){
				 //獲取用戶信息
			     str_nickName= (String) aes_map_vale.get("nickName");
			     str_gender= (String)aes_map_vale.get("gender");
			     str_city= (String)aes_map_vale.get("city");
			     str_province= (String)aes_map_vale.get("province");
			     str_country= (String)aes_map_vale.get("country");
			     str_avatarUrl= (String)aes_map_vale.get("avatarUrl");
			 }else if ("1".equals(type)) {
				 //獲取電話號碼
			     str_phoneNumber= (String) aes_map_vale.get("phoneNumber");
			     str_purePhoneNumber= (String)aes_map_vale.get("purePhoneNumber");
			     str_countryCode= (String)aes_map_vale.get("countryCode");
			 }
	     }
	     		/**處理登錄**/

                 成功返回
                 失敗返回

	     		/**處理登錄 end**/

		        map.put("status", "success");
    	    	map.put("message", "登錄成功");
    	    	return map;
		}
	}

2.get_openid_key()方法

//--------根據code獲取 openid,session_key------------
	public Map<String, Object> get_openid_key(String code) {
		  Map<String, Object> map = Maps.newHashMap();
			 //code獲取參數
		     if (StringUtils.isBlank(code)) {
		    	 map.put("status", "fail");
		    	 map.put("message", "code is not null.");
		    	 return map;
		     }

			//發起GET請求獲取憑證
		     String stringToken = String.format(
						"https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=%s",
						APPID, APPSECRET, code,GRANT_TYPE);
			String result = HttpRequest.sendGet(stringToken,null);
		    JSONObject jsonObject = null;
			jsonObject = JSONObject.fromObject(result);
		     
			try {
				String openId = jsonObject.getString("openid");// 用戶唯一標識
				String session_key = jsonObject.getString("session_key");// 密鑰
				map.put("openId", openId);
				map.put("session_key", session_key);
				map.put("status", "success");
				map.put("message", "請求成功");
			} catch (Exception e) {
				switch (jsonObject.getString("errcode")) {
				case "40163":
					map.put("message", "code重複使用");
					break;
				case "40029":
					map.put("message", "code無效");
					break;
				case "40125":
					map.put("message", "appId or secret無效");
					break;
				default:
					map.put("message", "錯誤");
					break;
				}
				map.put("openId", "");
				map.put("session_key", "");
				map.put("status", "fail");
			}
		     
		return map;
	}

3.get_encryptedData()方法

	//--------解密 ------------
	/**
	 * 
	 * @Title: get_encryptedData   
	 * @Description: TODO(解密方法)   
	 * @param: @param encryptedData
	 * @param: @param session_key
	 * @param: @param iv
	 * @param: @param type 0基礎用戶信息  1電話   
	 * @param: @return
	 * @param: @throws Exception      
	 * @return: Map<String,Object>      
	 * @throws
	 */
	public Map<String, Object> get_encryptedData(String encryptedData, String session_key, String iv,String type) throws Exception {
		  Map<String, Object> map = Maps.newHashMap();
			//解密encryptedData,獲取unionId相關信息
			String str_encryptedData = decrypt(encryptedData, session_key, iv,"UTF-8");
			JSONObject userInfoJSON = null;
			userInfoJSON = JSONObject.fromObject(str_encryptedData);
		    map = Maps.newHashMap();
			//type 0返回用戶基礎信息    1返回用戶電話號碼
			if("0".equals(type)){  
			    map.put("openId", userInfoJSON.get("openId"));
			    map.put("nickName", userInfoJSON.get("nickName"));
			    map.put("gender", userInfoJSON.get("gender").toString());
			    map.put("city", userInfoJSON.get("city"));
				map.put("province", userInfoJSON.get("province"));
				map.put("country", userInfoJSON.get("country"));
				map.put("avatarUrl", userInfoJSON.get("avatarUrl"));
				map.put("language", userInfoJSON.get("language"));
			}else {
			    map.put("phoneNumber", userInfoJSON.get("phoneNumber"));
			    map.put("purePhoneNumber", userInfoJSON.get("purePhoneNumber"));
			    map.put("countryCode", userInfoJSON.get("countryCode"));
			}
			return map;
	}

4.祕鑰處理

 /**
     * AES解密用戶敏感數據獲取用戶信息 
     *
     * @param encryptedData 包括敏感數據在內的完整用戶信息的加密數據
     * @param key    數據進行加密簽名的密鑰
     * @param iv            加密算法的初始向量
     * @param encodingFormat 解密後的結果需要進行的編碼
     * 	 描述:對稱解密使用的算法爲 AES-128-CBC,數據採用PKCS#7填充。
		對稱解密的目標密文爲 Base64_Decode(encryptedData)。
		對稱解密祕鑰 aeskey = Base64_Decode(session_key), aeskey 是16字節。
		對稱解密算法初始向量 爲Base64_Decode(iv),其中iv由數據接口返回。
     * @return
     * */
    public static  String decrypt(String encryptedData, String key, String iv, String encodingFormat) throws Exception {
         init();

        // 被加密的數據
        byte[] dataByte = Base64.decodeBase64(encryptedData);
        // 加密祕鑰
        byte[] keyByte = Base64.decodeBase64(key);
        // 偏移量
        byte[] ivByte = Base64.decodeBase64(iv);

        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");

            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");

            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));

            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化

            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, encodingFormat);
                return result;
            }
            return null;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidParameterSpecException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return null;
    }
    
    private static boolean hasInited = false;

    public static void init() {
        if (hasInited) {
            return;
        }
        Security.addProvider(new BouncyCastleProvider());
        hasInited = true;
    }

3.返回結果

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章