微信小程序-登录

环境

后端: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.返回结果

 

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