Comment implémenter le processus de connexion Java de l'applet WeChat
小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{ } } }); } 复制代码
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//解密算法的向量 复制代码
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; } 复制代码
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(); 复制代码
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; } 复制代码
. 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; } 复制代码
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); } 复制代码
. 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"}} 复制代码
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; } } 复制代码
3.2.6 Méthode SDK tierce
WxMaPhoneNumberInfo phoneNoInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv); phoneNumber = phoneNoInfo.getPurePhoneNumber(); 复制代码
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!

Outils d'IA chauds

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

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

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

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

Sujets chauds

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.

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.

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.

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.

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.

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.

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.

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
