目录
小程序客户端
首页 Java java教程 微信小程序Java登录流程怎么实现

微信小程序Java登录流程怎么实现

May 22, 2023 pm 10:40 PM
java 微信小程序

小程序客户端

doLogin: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来换取后台的 sessionKey和 openId 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();
复制代码
登录后复制

以上是微信小程序Java登录流程怎么实现的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Java 中的平方根 Java 中的平方根 Aug 30, 2024 pm 04:26 PM

Java 中的平方根指南。下面我们分别通过例子和代码实现来讨论平方根在Java中的工作原理。

Java 中的完美数 Java 中的完美数 Aug 30, 2024 pm 04:28 PM

Java 完美数指南。这里我们讨论定义,如何在 Java 中检查完美数?,示例和代码实现。

Java 中的随机数生成器 Java 中的随机数生成器 Aug 30, 2024 pm 04:27 PM

Java 随机数生成器指南。在这里,我们通过示例讨论 Java 中的函数,并通过示例讨论两个不同的生成器。

Java中的Weka Java中的Weka Aug 30, 2024 pm 04:28 PM

Java 版 Weka 指南。这里我们通过示例讨论简介、如何使用weka java、平台类型和优点。

Java 中的阿姆斯特朗数 Java 中的阿姆斯特朗数 Aug 30, 2024 pm 04:26 PM

Java 中的阿姆斯特朗数指南。这里我们讨论一下java中阿姆斯特朗数的介绍以及一些代码。

Java 中的史密斯数 Java 中的史密斯数 Aug 30, 2024 pm 04:28 PM

Java 史密斯数指南。这里我们讨论定义,如何在Java中检查史密斯号?带有代码实现的示例。

Java Spring 面试题 Java Spring 面试题 Aug 30, 2024 pm 04:29 PM

在本文中,我们保留了最常被问到的 Java Spring 面试问题及其详细答案。这样你就可以顺利通过面试。

突破或从Java 8流返回? 突破或从Java 8流返回? Feb 07, 2025 pm 12:09 PM

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

See all articles