Table des matières
小program客户端
Maison Java javaDidacticiel Comment implémenter le processus de connexion Java de l'applet WeChat

Comment implémenter le processus de connexion Java de l'applet WeChat

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

小program客户端

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{
 
    }
  }
});
}
复制代码
Copier après la connexion

L'applet WeChat initie une demande de connexion, et les paramètres portés sont principalement :

code:loginRes.code,//临时登录凭证
    rawData:infoRes.rawData,//用户非敏感信息
    signature:infoRes.signature,//签名
    encrypteData:infoRes.encryptedData,//用户敏感信息
    iv:infoRes.iv//解密算法的向量
复制代码
Copier après la connexion

Explication du paramètre : code :loginRes.code,//Identifiants de connexion temporaires : doivent être transmis, le code est utilisé en échange de la sessionKey et de l'openId d'arrière-plan rawData :infoRes.rawData,//Signature d'informations non sensibles de l'utilisateur :infoRes.signature,/ /Signature encrypteData : infoRes.encryptedData,//Informations sensibles de l'utilisateur iv:infoRes.iv//Vecteur d'algorithme de décryptage

signature,//Signature, Données chiffrées,//Informations sensibles de l'utilisateur, iv //Vecteur d'algorithme de décryptage :

Ces trois paramètres sont utilisés pour décoder les informations sensibles des utilisateurs, telles que les numéros de téléphone et d'autres informations.

Les données requises comprennent principalement : la clé, utilisée pour marquer le caractère unique de l'utilisateur.

3. Backend 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;
    }
复制代码
Copier après la connexion

Analysons le code ci-dessus :

3.1 Obtenez openid et sessionKey
session = this.wxService.getUserService().getSessionInfo(code);

//获取到openid和sessionkey
openid = session.getOpenid();
sessionKey = session.getSessionKey();
复制代码
Copier après la connexion

Ce code est-il très concis ? Un SDK tiers (weixin-java-tools) est utilisé ici. Grâce à ce SDK, openid et sessionKey peuvent être obtenus très facilement, comme le montre la démo détaillée.

Bien sûr, si vous ne souhaitez pas utiliser de SDK tiers, vous pouvez également l'implémenter vous-même. Le code d'implémentation est le suivant :

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;
}
复制代码
Copier après la connexion
. 3.2 Décrypter les données sensibles de l'utilisateur pour obtenir des informations sur l'utilisateur
3.2.1contrôleur

Cette partie a rencontré de nombreux pièges. Parce qu'elle doit obtenir le numéro de téléphone mobile de l'utilisateur, elle doit décrypter les informations de l'utilisateur.

/**
     * 获取用户板绑定的手机号
     * @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;
    }
复制代码
Copier après la connexion
3.2.2classe d'outils de décryptage

La classe d'outils WxMiniappUtils.decrypt est appelée ici. La classe d'outils est la suivante :

/**
     * 解密用户手机号算法
     * @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);
    }
复制代码
Copier après la connexion

. La classe Cipher utilisée ici est la classe javax.crypto.

3.2.3 Question

Mais en utilisant cette classe d'outils de décryptage ici, j'ai rencontré beaucoup de problèmes.

Premièrement : le décryptage AES signale une erreur javax.crypto.BadPaddingException : bloc de tampon corrompu

Ce problème est dû à la classe d'outils utilisant Cipher.getInstance("AES/CBC/PKCS5Padding").

Solution : Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding");.

Deuxièmement : java.security.InvalidAlgorithmParameterException : mauvaise longueur IV : doit être 16 Ce problème est dû au fait que le iv décodé n'est pas 16 bits, il semble être 15 bits, je ne sais pas pourquoi.

Solution : Comment résoudre ce problème ? Je n'ai pas trouvé de solution moi-même. Si quelqu'un a une solution, faites-le-moi savoir !

Ma solution : En fait, j'ai trouvé que ce problème n'est pas un problème avec cette classe d'outils. Après une journée de lutte, j'ai découvert que cette classe d'outils n'est pas incapable de décoder les numéros de téléphone portable. et certains ne peuvent pas analyser les numéros de téléphone mobile. Il n'y en a que des informations ordinaires, donc je pense que cela peut être dû au fait que l'utilisateur de WeChat s'est enregistré avec un numéro de téléphone mobile, donc certains peuvent être analysés et d'autres ne peuvent pas être analysés. Si quelqu'un a d'autres méthodes, n'hésitez pas à me le faire savoir !

3.2.4 Analyser les données réussies
{"phoneNumber":"13880684012","purePhoneNumber":"13880684012","countryCode":"86","watermark":{"timestamp":1519460296,"appid":"wx6ede2086ee29a89f"}}
复制代码
Copier après la connexion

Si ces données JSON sont analysées, cela signifie que l'opération a réussi.

3.2.5 Une autre solution
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;
    }
}
复制代码
Copier après la connexion
3.2.6 Méthode SDK tierce
WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);
        phoneNumber = phoneNoInfo.getPurePhoneNumber();
复制代码
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

Repo: Comment relancer ses coéquipiers
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
2 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Comment obtenir des graines géantes
1 Il y a quelques mois By 尊渡假赌尊渡假赌尊渡假赌
Combien de temps faut-il pour battre Split Fiction?
4 Il y a quelques semaines By DDD

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Racine carrée en Java Racine carrée en Java Aug 30, 2024 pm 04:26 PM

Guide de la racine carrée en Java. Nous discutons ici du fonctionnement de Square Root en Java avec un exemple et son implémentation de code respectivement.

Nombre parfait en Java Nombre parfait en Java Aug 30, 2024 pm 04:28 PM

Guide du nombre parfait en Java. Nous discutons ici de la définition, comment vérifier le nombre parfait en Java ?, des exemples d'implémentation de code.

Générateur de nombres aléatoires en Java Générateur de nombres aléatoires en Java Aug 30, 2024 pm 04:27 PM

Guide du générateur de nombres aléatoires en Java. Nous discutons ici des fonctions en Java avec des exemples et de deux générateurs différents avec d'autres exemples.

Numéro Armstrong en Java Numéro Armstrong en Java Aug 30, 2024 pm 04:26 PM

Guide du numéro Armstrong en Java. Nous discutons ici d'une introduction au numéro d'Armstrong en Java ainsi que d'une partie du code.

Weka en Java Weka en Java Aug 30, 2024 pm 04:28 PM

Guide de Weka en Java. Nous discutons ici de l'introduction, de la façon d'utiliser Weka Java, du type de plate-forme et des avantages avec des exemples.

Numéro de Smith en Java Numéro de Smith en Java Aug 30, 2024 pm 04:28 PM

Guide du nombre de Smith en Java. Nous discutons ici de la définition, comment vérifier le numéro Smith en Java ? exemple avec implémentation de code.

Questions d'entretien chez Java Spring Questions d'entretien chez Java Spring Aug 30, 2024 pm 04:29 PM

Dans cet article, nous avons conservé les questions d'entretien Java Spring les plus posées avec leurs réponses détaillées. Pour que vous puissiez réussir l'interview.

Break or Return of Java 8 Stream Forach? Break or Return of Java 8 Stream Forach? Feb 07, 2025 pm 12:09 PM

Java 8 présente l'API Stream, fournissant un moyen puissant et expressif de traiter les collections de données. Cependant, une question courante lors de l'utilisation du flux est: comment se casser ou revenir d'une opération FOREAK? Les boucles traditionnelles permettent une interruption ou un retour précoce, mais la méthode Foreach de Stream ne prend pas directement en charge cette méthode. Cet article expliquera les raisons et explorera des méthodes alternatives pour la mise en œuvre de terminaison prématurée dans les systèmes de traitement de flux. Lire plus approfondie: Améliorations de l'API Java Stream Comprendre le flux Forach La méthode foreach est une opération terminale qui effectue une opération sur chaque élément du flux. Son intention de conception est

See all articles