微信小程序Java登录流程(ssm实现具体功能和加解密隐私信息问题解决方案)_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 微信小程序Java登录流程(ssm实现具体功能和加解密隐私信息问题解决方案)

微信小程序Java登录流程(ssm实现具体功能和加解密隐私信息问题解决方案)

 2018/7/17 9:20:51  SIHAIloveYAN  程序员俱乐部  我要评论(0)
  • 摘要:文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的微信公众号:好好学java,获取优质学习资源。一、登录流程图20180510145856113.png二、小程序客户端doLogin:function(callback=()=>{}){letthat=this;wx.login({success:function(loginRes){if(loginRes){//获取用户信息wx.getUserInfo({withCredentials:true
  • 标签:程序 解决方案 功能 解决 实现 问题解决 流程 Java 问题

文章有不当之处,欢迎指正,如果喜欢微信阅读,你也可以关注我的微信公众号:好好学java,获取优质学习资源。

一、登录流程图

20180510145856113.png20180510145856113.png

二、小程序客户端

monospace; border-radius: 0px; color: #a9b7c6; padding: 0.5em; display: block !important; white-space: pre !important; overflow: auto !important;" class="hljs javascript">doLogin:function">function(callback?=?()?=>{}){
let?that?=?this;
wx.login({
??success:function(loginRes){
????if(loginRes){
??????//获取用户信息
??????wx.getUserInfo({
????????withCredentials:true,//非必填??默认为true
????????success:function(infoRes){
??????????console.log(infoRes,'>>>');
??????????//请求服务端的登录接口
??????????wx.request({
????????????url:?api.loginUrl,
????????????data:{
??????????????code:loginRes.code,//临时登录凭证
??????????????rawData:infoRes.rawData,//用户非敏感信息
??????????????signature:infoRes.signature,//签名
??????????????encrypteData:infoRes.encryptedData,//用户敏感信息
??????????????iv:infoRes.iv//解密算法的向量
????????????},
????????????success:function(res){
??????????????console.log('login?success');
??????????????res?=?res.data;
??????????????if(res.result==0){
????????????????that.globalData.userInfo?=?res.userInfo;
????????????????wx.setStorageSync('userInfo',JSON.stringify(res.userInfo));
????????????????wx.setStorageSync('loginFlag',res.skey);
????????????????console.log("skey="+res.skey);
????????????????callback();
??????????????}else{
????????????????that.showInfo('res.errmsg');
??????????????}
????????????},
????????????fail:function(error){
??????????????//调用服务端登录接口失败
?????????????//?that.showInfo('调用接口失败');
??????????????console.log(error);
????????????}
??????????});
????????}
??????});
????}else{

????}
??}
});
}

微信小程序端发起登录请求,携带的参数主要有:

????code:loginRes.code,//临时登录凭证
????rawData:infoRes.rawData,//用户非敏感信息
????signature:infoRes.signature,//签名
????encrypteData:infoRes.encryptedData,//用户敏感信息
????iv:infoRes.iv//解密算法的向量

参数解释:
code:loginRes.code,//临时登录凭证:必传,通过code来换取后台的sessionKeyopenId
rawData:infoRes.rawData,//用户非敏感信息
signature:infoRes.signature,//签名
encrypteData:infoRes.encryptedData,//用户敏感信息
iv:infoRes.iv//解密算法的向量

signature,//签名、encryptedData,//用户敏感信息、iv//解密算法的向量:

这三个参数是用来解码用户敏感信息的,比如电话号码等信息。

需要的数据主要有:skey,用于标志用户的唯一性。

三、Java后台

/**
?????*?登陆接口
?????*/
????@RequestMapping("/login")
????@ApiResponses({
????????????@ApiResponse(code?=?404,?message?=?"服务器未找到资源"),
????????????@ApiResponse(code?=?200,?message?=?"请求成功"),
????????????@ApiResponse(code?=?500,?message?=?"服务器错误"),
????????????@ApiResponse(code?=?401,?message?=?"没有访问权限"),
????????????@ApiResponse(code?=?403,?message?=?"服务器拒绝访问"),
????})
????@ApiOperation(value?=?"小程序登录",?httpMethod?=?"POST",?notes?=?"小程序登录")
????public?ResponseEntity<LoginDataResult>?login(
????????????@ApiParam(required?=?true,?value?=?"临时登录凭证code",?name?=?"code")?String?code,
????????????@ApiParam(required?=?true,?value?=?"用户非敏感信息",?name?=?"rawData")
????????????@RequestParam(value?=?"rawData",?required?=?true)?String?rawData,
????????????@ApiParam(required?=?true,?value?=?"签名",?name?=?"signature")
????????????@RequestParam(value?=?"signature",?required?=?true)?String?signature,
????????????@ApiParam(required?=?true,?value?=?"用户敏感信息",?name?=?"encrypteData")
????????????@RequestParam(value?=?"encrypteData",?required?=?true)?String?encrypteData,
????????????@ApiParam(required?=?true,?value?=?"解密算法的向量",?name?=?"iv")
????????????@RequestParam(value?=?"iv",?required?=?true)?String?iv
????)?{

????????ObjectMapper?mapper?=?new?ObjectMapper();

????????logger.info("signature============================================================="+signature);
????????logger.info("encrypteData=========================================================="+encrypteData);
????????logger.info("iv========================================================================"+iv);

????????RawData?data?=?null;
????????WxMaJscode2SessionResult?session?=?null;
????????String?openid?=?null;
????????String?sessionKey?=?null;
????????String?phoneNumber?=?null;

????????try?{
????????????if?(rawData?!=?null?&&?!"".equals(rawData))?{
????????????????//1、获取用户非敏感信息
????????????????data?=?mapper.readValue(rawData,?RawData.class);
????????????}
????????????session?=?this.wxService.getUserService().getSessionInfo(code);

????????????//获取到openid和sessionkey
????????????openid?=?session.getOpenid();
????????????sessionKey?=?session.getSessionKey();

????????????logger.info("sessionkey========================================================="+sessionKey);

??????????/*??//2、获取用户手机号
????????????phoneNumber?=?phone(code,?signature,?rawData,?encrypteData,?iv);

????????????logger.info("phoneNumber========================================="+phoneNumber);
*/
????????}?catch?(IOException?e)?{
????????????e.printStackTrace();
????????????logger.info("获取用户信息失败");
????????????LoginDataResult?loginDataResult?=?new?LoginDataResult();
????????????loginDataResult.setCode("2");
????????????loginDataResult.setMsg("请求失败");
????????????return?ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(loginDataResult);
????????}?catch?(WxErrorException?e)?{
????????????e.printStackTrace();
????????????logger.info("获取用户信息失败");
????????}

????????//3、向数据库插入用户信息
????????String?skey?=?insertUser(data,?openid,?phoneNumber);

????????//4、缓存openid,?sessionKey,?skey到redis
????????redisCache(openid,?sessionKey,?skey);


????????//5、把新的skey返回给小程序
????????LoginDataResult?loginDataResult?=?new?LoginDataResult();
????????loginDataResult.setSkey(skey);
????????loginDataResult.setCode("1");
????????loginDataResult.setMsg("请求成功");

????????return?ResponseEntity.status(HttpStatus.OK).body(loginDataResult);
????}

????/**
?????*?缓存openid,sessionKey,skey等信息
?????*?@param?openid?小程序用户唯一标志
?????*?@param?sessionKey?小程序会话标志
?????*?@param?skey?后台生成的用户唯一标志,会话管理
?????*/
????private?void?redisCache(String?openid,?String?sessionKey,?String?skey)?{
????????//根据openid查询skey是否存在
????????String?skey_redis?=?jedisClient.hget("WEXIN_USER_OPENID_SKEY",?openid);
????????if?(StringUtils.isNotBlank(skey_redis))?{
????????????//存在?删除?skey?重新生成skey?将skey返回
????????????jedisClient.hdel("WEXIN_USER_OPENID_SKEY",?openid);
????????????jedisClient.hdel("WEIXIN_USER_SKEY_OPENID",?skey_redis);
????????????jedisClient.hdel("WEIXIN_USER_SKEY_SESSIONKEY",?skey_redis);
????????}

????????//??缓存一份新的
????????jedisClient.hset("WEXIN_USER_OPENID_SKEY",?openid,?skey);
????????jedisClient.expire("WEXIN_USER_OPENID_SKEY",432000);//设置5天过期
????????jedisClient.hset("WEIXIN_USER_SKEY_OPENID",?skey,?openid);
????????jedisClient.expire("WEIXIN_USER_SKEY_OPENID",432000);//设置5天过期
????????jedisClient.hset("WEIXIN_USER_SKEY_SESSIONKEY",?skey,?sessionKey);
????????jedisClient.expire("WEIXIN_USER_SKEY_SESSIONKEY",432000);//设置5天过期
????}

????/**
?????*?将用户信息插入到数据库
?????*?@param?data?用户信息
?????*?@param?openid
?????*?@param?phoneNumber?手机号
?????*?@return
?????*/
????private?String?insertUser(RawData?data,?String?openid,?String?phoneNumber)?{
????????//判断用户数据库是否存在,不存在,入库。
????????Member?user?=?userService.selectUserByOpenid(openid);
????????//uuid生成唯一key
????????String?skey?=?UUID.randomUUID().toString();
????????if?(user?==?null)?{
????????????//入库
????????????user?=?new?Member();
????????????user.setId(skey);
????????????user.setCountry(data.getCountry());
????????????user.setCreatedate(new?Date());
????????????user.setDf(1);
????????????user.setGender(data.getGender().equals("1")???1?:?2);//1为男,2为女
????????????user.setHeadimg(data.getAvatarUrl());
????????????user.setNickname(data.getNickName());
????????????user.setOpenid(openid);
????????????user.setCitycode(data.getCity());
????????????user.setProvincecode(data.getProvince());
????????????user.setMobileno(phoneNumber);
????????????//插入到数据库
????????????userService.insertUser(user);
????????}?else?{
????????????//已存在
????????????logger.info("用户openid已存在,不需要插入");
????????????return?user.getId();//返回用户唯一标志skey
????????}
????????return?skey;
????}

????/**
?????*?获取用户板绑定的手机号
?????*?@param?sessionKey?小程序session
?????*?@param?signature?签名
?????*?@param?rawData?用户信息
?????*?@param?encryptedData?小程序加密数据
?????*?@param?iv?小程序向量
?????*?@return
?????*/
????@ApiOperation(value?=?"用户手机号获取",?httpMethod?=?"GET",?notes?=?"用户手机号获取")
????public?String?phone(String?sessionKey,?String?signature,?String?rawData,?String?encryptedData,?String?iv)?{
????????String?phoneNumber?=?null;

????????try?{
????????????byte[]?bytes?=?WxMiniappUtils.decrypt(Base64.decodeBase64(sessionKey),?Base64.decodeBase64(iv),?Base64.decodeBase64(encryptedData));
????????????String?phone?=?new?String(bytes,?"UTF8");
????????????logger.info("phone====================================="+phone);
????????}?catch?(NoSuchPaddingException?e)?{
????????????e.printStackTrace();
????????}?catch?(NoSuchAlgorithmException?e)?{
????????????e.printStackTrace();
????????}?catch?(InvalidAlgorithmParameterException?e)?{
????????????e.printStackTrace();
????????}?catch?(InvalidKeyException?e)?{
????????????e.printStackTrace();
????????}?catch?(BadPaddingException?e)?{
????????????e.printStackTrace();
????????}?catch?(IllegalBlockSizeException?e)?{
????????????e.printStackTrace();
????????}?catch?(UnsupportedEncodingException?e)?{
????????????e.printStackTrace();
????????}
????????return?null;
????}

下面对上面代码进行分析:

3.1获取openid和sessionKey

session?=?this.wxService.getUserService().getSessionInfo(code);

//获取到openid和sessionkey
openid?=?session.getOpenid();
sessionKey?=?session.getSessionKey();

这段代码是不是十分的简洁,这里用到了一个第三方的sdk(weixin-java-tools),通过这个sdk可以非常简便的获取到openid和sessionKey,具体的demo。

当然,如果你不想用第三方的sdk,也可以自己实现,实现代码如下:

public?static?JSONObject?getSessionKeyOrOpenId(String?code){
????//微信端登录code
????String?wxCode?=?code;
????String?requestUrl?=?"https://api.weixin.qq.com/sns/jscode2session";
????Map<String,String>?requestUrlParam?=?new?HashMap<String,?String>(??);
????requestUrlParam.put(?"appid","你的小程序appId"?);//小程序appId
????requestUrlParam.put(?"secret","你的小程序appSecret"?);
????requestUrlParam.put(?"js_code",wxCode?);//小程序端返回的code
????requestUrlParam.put(?"grant_type","authorization_code"?);//默认参数

????//发送post请求读取调用微信接口获取openid用户唯一标识
????JSONObject?jsonObject?=?JSON.parseObject(?UrlUtil.sendPost(?requestUrl,requestUrlParam?));
????return?jsonObject;
}

3.2解密用户敏感数据获取用户信息

3.2.1controller

这个部分自己遇到了好多的坑,由于需要获取用户的手机号码,需要解密用户的信息。

??/**
?????*?获取用户板绑定的手机号
?????*?@param?sessionKey?小程序session
?????*?@param?signature?签名
?????*?@param?rawData?用户信息
?????*?@param?encryptedData?小程序加密数据
?????*?@param?iv?小程序向量
?????*?@return
?????*/
????@ApiOperation(value?=?"用户手机号获取",?httpMethod?=?"GET",?notes?=?"用户手机号获取")
????public?String?phone(String?sessionKey,?String?signature,?String?rawData,?String?encryptedData,?String?iv)?{
????????String?phoneNumber?=?null;

????????try?{
????????????byte[]?bytes?=?WxMiniappUtils.decrypt(Base64.decodeBase64(sessionKey),?Base64.decodeBase64(iv),?Base64.decodeBase64(encryptedData));
????????????String?phone?=?new?String(bytes,?"UTF8");
????????????logger.info("phone====================================="+phone);
????????}?catch?(NoSuchPaddingException?e)?{
????????????e.printStackTrace();
????????}?catch?(NoSuchAlgorithmException?e)?{
????????????e.printStackTrace();
????????}?catch?(InvalidAlgorithmParameterException?e)?{
????????????e.printStackTrace();
????????}?catch?(InvalidKeyException?e)?{
????????????e.printStackTrace();
????????}?catch?(BadPaddingException?e)?{
????????????e.printStackTrace();
????????}?catch?(IllegalBlockSizeException?e)?{
????????????e.printStackTrace();
????????}?catch?(UnsupportedEncodingException?e)?{
????????????e.printStackTrace();
????????}
????????return?null;
????}
3.2.2decrypt工具类

这里调用了WxMiniappUtils.decrypt这个工具类,工具类如下:

?/**
?????*?解密用户手机号算法
?????*?@param?sessionkey?小程序登录sessionKey
?????*?@param?iv?向量
?????*?@param?encryptedData
?????*?@return
?????*?@throws?NoSuchPaddingException
?????*?@throws?NoSuchAlgorithmException
?????*?@throws?InvalidAlgorithmParameterException
?????*?@throws?InvalidKeyException
?????*?@throws?BadPaddingException
?????*?@throws?IllegalBlockSizeException
?????*/
????public?static?byte[]?decrypt(byte[]?sessionkey,?byte[]?iv,?byte[]?encryptedData)
????????????throws?NoSuchPaddingException,?NoSuchAlgorithmException,?InvalidAlgorithmParameterException,
????????????InvalidKeyException,?BadPaddingException,?IllegalBlockSizeException?{
????????AlgorithmParameterSpec?ivSpec?=?new?IvParameterSpec(iv);
????????Cipher?cipher?=?Cipher.getInstance("AES/CBC/PKCS5Padding");
????????SecretKeySpec?keySpec?=?new?SecretKeySpec(sessionkey,?"AES");
????????cipher.init(Cipher.DECRYPT_MODE,?keySpec,?ivSpec);
????????return?cipher.doFinal(encryptedData);
????}

这里用到的Cipher类是 javax.crypto的类。

3.2.3问题

但是这里使用这个decrypt工具类的时候,遇到了好多的问题。

第一:AES解密是报错javax.crypto.BadPaddingException: pad block corrupted

这个问题是由于,工具类使用了Cipher.getInstance("AES/CBC/PKCS5Padding")

解决:Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding");。

第二:java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 16
这个问题是由于,解码出来的iv不是16位,好像是15位,这个为什么我也不太清楚。

解决:这个怎么解决,自己也没有找到方法,如果有大神解决,望告知!

我的解决方法:其实我发现这个问题并不是这个工具类的问题,我折腾了一天发现,这个工具类并不是不能够解码手机号,有的是可以的,有的解析不到手机号,只有普通的信息,所以我觉得,这个可能是微信用户注册的时候,是不是用手机号注册的,所以会出现有些能够解析,有的不能解析。如果有大神有其他方法,望告知!

3.2.4解析成功数据
{"phoneNumber":"13880684012","purePhoneNumber":"13880684012","countryCode":"86","watermark":{"timestamp":1519460296,"appid":"wx6ede2086ee29a89f"}}

如果解析到了这样的json数据,说明是成功了的。

3.2.5 另外一种方案
public?class?AES?{
????public?static?final?AES?instance?=?new?AES();

????public?static?boolean?initialized?=?false;

????/**
?????*?AES解密
?????*?@param?content?密文
?????*?@return
?????*?@throws?InvalidAlgorithmParameterException
?????*?@throws?NoSuchProviderException
?????*/
????public?byte[]?decrypt(byte[]?content,?byte[]?keyByte,?byte[]?ivByte)?throws?InvalidAlgorithmParameterException?{
????????initialize();
????????try?{
????????????Cipher?cipher?=?Cipher.getInstance("AES/CBC/PKCS7Padding");
????????????Key?sKeySpec?=?new?SecretKeySpec(keyByte,?"AES");

????????????cipher.init(Cipher.DECRYPT_MODE,?sKeySpec,?generateIV(ivByte));//?初始化
????????????byte[]?result?=?cipher.doFinal(content);
????????????return?result;
????????}?catch?(NoSuchAlgorithmException?e)?{
????????????e.printStackTrace();
????????}?catch?(NoSuchPaddingException?e)?{
????????????e.printStackTrace();
????????}?catch?(InvalidKeyException?e)?{
????????????e.printStackTrace();
????????}?catch?(IllegalBlockSizeException?e)?{
????????????e.printStackTrace();
????????}?catch?(BadPaddingException?e)?{
????????????e.printStackTrace();
????????}?catch?(NoSuchProviderException?e)?{
????????????//?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}?catch?(Exception?e)?{
????????????//?TODO?Auto-generated?catch?block
????????????e.printStackTrace();
????????}
????????return?null;
????}

????public?static?void?initialize(){
????????if?(initialized)?return;
????????Security.addProvider(new?BouncyCastleProvider());
????????initialized?=?true;
????}

????//生成iv
????public?static?AlgorithmParameters?generateIV(byte[]?iv)?throws?Exception{
????????AlgorithmParameters?params?=?AlgorithmParameters.getInstance("AES");
????????params.init(new?IvParameterSpec(iv));
????????return?params;
????}
}

这个也会有上面的问题,有时候会解析失败!具体方法,还在摸索中,有大神知道方法和原有,望告知!

3.2.6第三方sdk方法
WxMaPhoneNumberInfo?phoneNoInfo?=?this.wxService.getUserService().getPhoneNoInfo(sessionKey,?encryptedData,?iv);
????????phoneNumber?=?phoneNoInfo.getPurePhoneNumber();

这个也会有上面的问题出现,有时候会解析失败!

四、总结

1.小程序端发起请求并携带主要参数

2.java后台接到/login请求后,根据code去调用微信接口获取用户唯一标识openid和sessionKey

3.根据openid查询mysql数据库,判断该用户是否存在,如果不存在将用户非敏感信息和其他初始化数据存入到数据库中,如果已存在,不操作

4.根据openid查询redis数据库,判断openid对应的skey是否存在,如果存在则删除原来老的skey以及对应的openid和sessionKey

5.通过uuid生成唯一的skey,用openid做键,skey做值,存入到redis中

6.然后把skey做键,openid和sessionKey的json串做值也重新存入到redis中

7.根据解密算法,参数有encryptedData、sessionKey和iv,获取用户信息userInfo,如果userInfo字段不满足需要,可通过userInfo.put( "balance",user.getUbalance() );添加所需要的字段和值

8.将微信小程序需要的数据封装到map中,返回给小程序端。

参考资料(感谢)
  • https://blog.csdn.net/abcwanglinyong/article/details/80267901
  • https://github.com/binarywang/weixin-java-miniapp-demo
  • https://www.cnblogs.com/nosqlcoco/p/6105749.html
  • https://www.cnblogs.com/suxiaoqi/p/7874635.html
发表评论
用户名: 匿名