手机号验证码登录

登录入口

1.app 正常登录入口

2.app 网页登录,比如分享直播卡片时,进入直播间需要先进行登录

3.pc 登录

一,app常见的登录方式

1.手机号验证码登录

2.用户名密码登录

3.一键登录

二,手机验证码登录示意图

 

三,流程

0.登录or注册

需要手机号,获取验证码除了登录,还可能是注册的场景,不同的类型需要加以区分

1.获取验证码:输入手机号,调用阿里云短信服务,获取验证码

详细:

1.base64编码,手机号是不能明文传输的,需要前端base64编码,后台解码

2.验证码如何生成的,是在代码中生成的随机六位数,作为阿里云发送短信的参数

3.注册or登录,不同的场景,阿里云短信发送,文案不同;且注册时,判断用户是否已经注册,查看用户表是否存在,根据手机号码查询,且存下的手机号码也是md5加密的

4.发送限制,一个手机号一天发送短信的次数要限制,redis处理

5.落地,发送记录落表,后续验证正确性,状态包含未使用 已使用,输入正确及标记为已使用

6.缓存验证码,过期时间为60s(过期后验证码输入依然可用),后续验证正确性时,先取缓存,再根据手机号查询表

2.登录:参数:手机号码+验证码,调用登录接口,返回用户信息

1.验证码正确性,先取缓存验证码,再根据手机号查询验证码记录表

2.输入次数限制,注意区分输入验证码次数限制与验证码发送限制 redis

3.输入剩余次数提示,redis记录失败次数

4.异步更新验证码记录,线程异步更新,更新状态已使用+验证时间

5.验证成功后,删除验证码缓存

6.更新用户信息,如最新一次登录时间,登录ip

7.返回用户及权限信息,包括token

四,token生成策略

1.公钥加密生成token

参数:用户名+过期日期(当前时间毫秒数+过期时间,30天)

2.私钥解密解析token

五,代码

1.token生成

    @Overridepublic Map<String, Object> getToken(String uid, Integer exp) {if (StringUtils.isBlank(uid)) {throw ExceptionUtils.throwException(PARAM_ERROR);}String encryptToken = RSADecryptUntil.encryptByTokenPublicKey(uid + "#" + (((int) Math.floor((System.currentTimeMillis() / 1000))) + exp));if (StringUtils.isBlank(encryptToken)) {throw ExceptionUtils.throwException(TOKEN_ENCRYPTION_FAIL);}Map<String, Object> map = new HashMap<>();map.put("token", RSADecryptUntil.Base64Replace(encryptToken));return map;}
package com.zgzt.platform.authentication.utils;import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;public class RSADecryptUntil {private static final Logger log = LoggerFactory.getLogger(RSADecryptUntil.class);private static final String ALGORITHM = "RSA";private static final String PUBLICK_EY = "PUBLICK_EY";private static final String PRIVATE_KEY = "PRIVATE_KEY";/*** 加密算法*/private static final String CIPHER_DE = "RSA";/*** 解密算法*/private static final String CIPHER_EN = "RSA";/*** 密钥长度*/private static final Integer KEY_LENGTH = 1024;/*** RSA最大加密明文大小*/private static final int MAX_ENCRYPT_BLOCK = 117;/*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;/*** token 公钥和私钥*/private static String tokenPublicKey = "AoQkavJtaZNzqUV0VByMU3n7KLdR/Sf1/kpTzLwqW9mB1GoFsuvrQ1/DVWFcuW54lNQl3/3ptK+megiyX3aq3O8nB92B69xUGXAyF" + "J2XTeX5WneLWVznR/zQPo5mxwILj7eJ6hVdWwIDAQAB";private static String tokenPrivateKey = "F6ZHkw+2OtesChCRq8m1pk3OpRXRUHIxTefsot1H9J/X+SlPMvCpb2YHUagWy6+tDX8NVYVy5bniU1CXf/em0r6Z6CLJfdqrc7ycH" + "3YHr3FQZcDIUnZdN5flad4tZXOdH/NA+jmbHAguPt4nqFV1bAgMBAAECgYEAwUzuGQ5X36U9Qy4AxG299L4KiZscav4Lr3NOhWUfn" + "I1Di+1UBb/6PzHZYfhQl4tGVzedH2SItqOKegTRI7G9Tlt2SLroYwXBhWzS785XKPygREN7sDXvWBoxD4Squ/0iV883fPXVLdUjSQn" + "OniWI9DnD8/m1SWnzAkLpt4JjvxECQQD4yKJJyAjvv9uQv0HED3nHXEEDR5IxfqEW+51LmxFPGBXXKRvqyS3hjzUQb8ixNAMRFFReA" + "drYTGS4PQ/xVSHHAkEA1Y315wyXRj670oi9JsOjRNQ8ToCPCFXWWbQevlJj9t2R61nQxEVyBxHnPGsniOLJ0MMrEl/2IcWc0ZtuCRw" + "nzQJBAMZ4cRfBUHfLrGNGYTYDTpif3XG7WELKDcNjCfJ2DBH4WfwjXJUq18J5V9D8DLRplQS8Hi489pTWJQfiFuTlkKMCQCeF02nEccb" + "FW3t2ZRNkh7X4VYTt1Arl3/rQFBSDKQ8KKLRW9gUtGRJn5NTQvAtgdZtWU4VeDy5m5UQBsRasiE0CQD6opMGepDgYkVRDcOfyvc/Yiyy" + "lCpMWkQk3ZjlRW2i9+d2zuQNUt22R3/N6JfBbnSDp0brauQpxIJvuG0D6TZQ=";/*** 极光秘钥*/public static final String jPushPrivateKey = "kixR/SHVACkPXynvf5ZFD3UZxNGS4fVp78DFIOQY3UgnVdRqkktkjkgDDTUiYRaC\n" + "FDBcgYn4atEJk/lXeTQaw8Z6hrBL4vgMB1nzhUeR0FSknFNxZj7vjLt1TIjkG32Q\n" + "16QRJpTAdz/gi0+iiHd3HVqnj6EDAgMBAAECgYAfQI6FABH914+bxMm6zvAosr6t\n" + "i8o1Ew7PqwGcpG+7Wt5+ikoFK7u0ZOnd5wYpiqbhdkCBbvFIbwtYSM6266YggufO\n" + "FQ75uaVVjgN8yNB0Dfw/+5ymdoTfN4+Al+Rn7uDYuUyVdYKO6081RusQwqkhrU7K\n" + "w9jJd2BXpvD/+Ig6EQJBAOhaKdhe1HdtV8Hcgkk/ZT3wJAfy7Q8TQQk+pYeXK/i4\n" + "tG6ZBwZ5NwNXufEj2gp83bmw8Lhl7vMekvXs72OHUesCQQDB6xn2g3bqwikMp5WU\n" + "1v4BSuPbrRLIFONvlamVPlratZnxlDxAXRa/hY+HJORdVzCl7PWhMXhaMcuHntU4\n" + "5Y9JAkAUjITO4fQga8crGflbyQOHKsnE+jME9kr2KlgxWalF4e/zKA17ARVgck27\n" + "idQqwUhKt99SL5GmZrnQjhfN0ZXpAkAYYmjcX8GnWYzx42ziz3oXTYSDjirrb/z9\n" + "fhNaCgJAuE9IWnyNF2eR48idlN0Gg71BUB+/Ckp5BQPz5NwpEGzJAkA908Loukwm\n" + "+qaHOVOTtRtzwjYQ9c6ReWyALMCdQZ64O7OOGozcyBgWQ/CbqKiYew/h7Pz1OJXI\n" + "yzCUz9DONIo3";/*** 生成秘钥对,公钥和私钥** @return* @throws NoSuchAlgorithmException*/public static Map<String, Object> genKeyPair() throws NoSuchAlgorithmException {Map<String, Object> keyMap = new HashMap<String, Object>();KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);keyPairGenerator.initialize(KEY_LENGTH); // 秘钥字节数KeyPair keyPair = keyPairGenerator.generateKeyPair();PublicKey publicKey = keyPair.getPublic();PrivateKey privateKey = keyPair.getPrivate();keyMap.put(PUBLICK_EY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}/*** 公钥加密** @param data* @param publicKey* @return* @throws InvalidKeySpecException*/public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {// 得到公钥byte[] keyBytes = Base64.decodeBase64(publicKey.getBytes());X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);Key key = keyFactory.generatePublic(x509EncodedKeySpec);// 加密数据,分段加密Cipher cipher = Cipher.getInstance(CIPHER_EN);cipher.init(Cipher.ENCRYPT_MODE, key);int inputLength = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offset = 0;byte[] cache;int i = 0;while (inputLength - offset > 0) {if (inputLength - offset > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(data, offset, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(data, offset, inputLength - offset);}out.write(cache, 0, cache.length);i++;offset = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return encryptedData;}/*** 私钥解密** @param data* @param privateKey* @return* @throws Exception*/public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {// 得到私钥byte[] keyBytes = Base64.decodeBase64(privateKey.getBytes());PKCS8EncodedKeySpec pKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);Key key = keyFactory.generatePrivate(pKCS8EncodedKeySpec);// 解密数据,分段解密Cipher cipher = Cipher.getInstance(CIPHER_DE);cipher.init(Cipher.DECRYPT_MODE, key);int inputLength = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offset = 0;byte[] cache;int i = 0;byte[] tmp;while (inputLength - offset > 0) {if (inputLength - offset > MAX_DECRYPT_BLOCK) {cache = cipher.doFinal(data, offset, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(data, offset, inputLength - offset);}out.write(cache);i++;offset = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return decryptedData;}/*** 获取公钥** @param keyMap* @return*/public static String getPublicKey(Map<String, Object> keyMap) {Key key = (Key) keyMap.get(PUBLICK_EY);String str = new String(Base64.encodeBase64(key.getEncoded()));return str;}/*** 获取私钥** @param keyMap* @return*/public static String getPrivateKey(Map<String, Object> keyMap) {Key key = (Key) keyMap.get(PRIVATE_KEY);String str = new String(Base64.encodeBase64(key.getEncoded()));return str;}//私钥解密public static String decryptByPrivateKey(String content, String privateKey) {try {byte[] decryptStrByte = RSADecryptUntil.decryptByPrivateKey(Base64.decodeBase64(content), privateKey);return new String(decryptStrByte);} catch (Exception e) {log.error("decryptByPrivateKey" + e.getMessage());}return null;}//极光钥解密public static String decryptByJPushPrivateKey(String content) {try {byte[] decryptStrByte = RSADecryptUntil.decryptByPrivateKey(Base64.decodeBase64(content), jPushPrivateKey);return new String(decryptStrByte);} catch (Exception e) {log.error("decryptByPrivateKey" + e.getMessage());}return null;}//私钥解密public static String decryptByTokenPrivateKey(String content) {try {byte[] decryptStrByte = RSADecryptUntil.decryptByPrivateKey(Base64.decodeBase64(content), tokenPrivateKey);return new String(decryptStrByte);} catch (Exception e) {log.error("decryptByPrivateKey:content:" + content + ";preContent:" + RSADecryptUntil.Base64Replace(content) + ";err:" + e.getMessage());}return null;}//公钥加密public static String encryptByTokenPublicKey(String content) {try {//log.info("公钥加密");byte[] encryptStrByte = RSADecryptUntil.encryptByPublicKey(content.getBytes(), tokenPublicKey);byte[] btt = Base64.encodeBase64(encryptStrByte);return new String(btt);} catch (Exception e) {log.error("encryptByPublicKey" + e.getMessage());}return null;}/*** 从普通字符串转换为适用于URL的Base64编码字符串** @param normalString* @return*/public static String Base64Replace(String normalString) {return normalString.replace('+', '*').replace('/', '-').replace('=', '.');}/*** 从替换过得字符串转成正确的编码字符串** @param base64String* @return*/public static String Base64Restore(String base64String) {return base64String.replace('.', '=').replace('*', '+').replace('-', '/');}}

2.手机验证码登录

依赖包

<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>4.0.6</version>
</dependency>

2.1发送验证码

    /*** * @param type 发送验证码类型  10 注册 11验证码登录,12修改手机号**/@PostMapping("/sendCode")@ApiOperation("发送短信验证码")public BaseResult<Boolean> sendCode(HttpServletRequest request, @RequestBody ReqPhoneSmsVO reqDTO)throws UnsupportedEncodingException {String phoneBase64 = reqDTO.getPhone();if (StringUtils.isBlank(phoneBase64)) {return BaseResult.success(false);}Base64.Decoder decoder = Base64.getDecoder();String phone = new String(decoder.decode(phoneBase64), "UTF-8");return BaseResult.success(registerService.sendCode(phone, reqDTO.getType(), getIp(request)));}
@Overridepublic Boolean sendCode(String phone, Integer type, String ip) {if (StringUtils.isEmpty(phone) || type == null || !CheckMobilePhoneNum(phone)) {log.error("phone format error ");throw ExceptionUtils.throwException(PARAM_ERROR);}//判断发送短信类型,控制发送短信次数String hexStr = HexUntil.str2HexStr(phone);String key = "jysvcn:" + type + ":" + hexStr;Object valObj = redisMgr.get(key);Integer value = 0;if (!Objects.isNull(valObj)) {value = Integer.valueOf(String.valueOf(valObj));}if (value != null && value >= maxCodeNum) {log.error("captcha transmission limit reached");throw ExceptionUtils.throwException(CAPTCHA_LIMIT);}String code = "111111";if (message) {code = String.valueOf((int) ((Math.random() * 9 + 1) * 100000));}String result = savePhoneMsg(phone, type, ip, code);if (Objects.isNull(result)) {log.error("captcha transmission failed");throw ExceptionUtils.throwException(CAPTCHA_ERROR);}//添加redis 缓存中redisMgr.put(key, DateTimeUtils.getSecond(), value == null ? "1" : (++value) + "");redisMgr.put("jysvc:" + type + ":" + hexStr, 3600, result);return true;}
@Overridepublic String savePhoneMsg(String phone, Integer type, String ip, String code) {String phoneMD5 = DigestUtils.md5DigestAsHex(phone.getBytes());// 临时注释掉发送验证码if (type == 10) {//注册验证码,检查是否存在//判断用户是否存在Register info = getUserInfoByPhoneMd5(phoneMD5);if (info != null) {//如果存在,报错log.info("register -Already registered:{}", phoneMD5);throw ExceptionUtils.throwException(REGISTER_REPEAT);}}//判断是否开启真实的发送验证码if (message) {String sendJson = aliYunSmsUtils.sendSms(phone, "{\"code\":\"" + code + "\"}", SmsTypeEnum.valueOf(type),type);if (StringUtils.isEmpty(sendJson)) {throw ExceptionUtils.throwException(CODE_SEND_FAIL);}}executor.execute(() -> {PhoneMsg phoneMsg = new PhoneMsg();phoneMsg.setId(0);phoneMsg.setIp(ip);phoneMsg.setPhone(phone.substring(0, 3) + "****" + phone.substring(7));phoneMsg.setVerify(code);phoneMsg.setCreatetime((int) Math.floor((System.currentTimeMillis() / 1000)));phoneMsg.setType(type);phoneMsg.setPhoneMd5(phoneMD5);String sendJson1 = SmsTypeEnum.getDesc(type);phoneMsg.setContent(sendJson1.replace("${code}", code));phoneMsg.setStatus(0);phoneMsgMapper.insertPhoneCode(phoneMsg);});return code;}

2.2手机号验证码登录

    @PostMapping("/codeLogin")@ApiOperation("验证码登录")public BaseResult<Map<String, Object>> codeLogin(HttpServletRequest request, @RequestBody ReqPhoneLoginVO reqDTO)throws UnsupportedEncodingException {String ua = request.getHeader("User-Agent");String phoneBase64 = reqDTO.getPhone();if (!StringUtils.isBlank(phoneBase64)) {Base64.Decoder decoder = Base64.getDecoder();reqDTO.setPhone(new String(decoder.decode(phoneBase64), "UTF-8"));}//暂定除了手机和pc客户端的登录都是webreturn BaseResult.success(registerService.codeLogin(reqDTO, getIp(request), "web"));}
@Data
public class ReqPhoneLoginVO {private String phone;private String code;
}
public Map<String, Object> codeLogin(ReqPhoneLoginVO reqDTO, String ip, String clientType) {String phone = reqDTO.getPhone();String code = reqDTO.getCode();if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) {throw ExceptionUtils.throwException(PARAM_EMPTY);}String base = phone;//统一改为不加盐的MD5数据-20210410String phoneMD5 = DigestUtils.md5DigestAsHex(base.getBytes());//验证手机号的登录验证码是否正确Boolean register = updateRegisterCode(phone, code, 11, phoneMD5);if (!register) {throw ExceptionUtils.throwException(CODE_ERROR);}//判断用户是否存在String uuid = LogbackUtil.generateTraceId();String regId = "";String clientPlatForm = "web";Register info = getUserInfoByPhoneMd5(phoneMD5);if (info != null) {return getLoginInfo(info, uuid, regId, ip, clientPlatForm, phone);} else {//如果不存在,直接进行注册//密码随机生成//String pwd= ""+rand.nextInt(899999)+ 100000;//6位随机数字//String pwd = "966812";//6位随机数字String pwd = (phone.length() > 6) ? phone.substring(phone.length() - 6) : "966812";return registerByPhone(phone, phoneMD5, pwd, uuid, regId, ip, clientPlatForm);}}
    @Overridepublic Boolean updateRegisterCode(String phone, String code, Integer type, String phoneMd5) {//首先去库中查询是否有验证码  并且判断验证码验证次数是否超过上限 5次String phoneKey = HexUntil.str2HexStr(phone);String redisCountKey = "jysvcen:" + type + ":" + phoneKey;Object num = redisMgr.get(redisCountKey);if (num != null && (Integer) num >= maxCodeNum) {log.error("Verification code error limit");throw ExceptionUtils.throwException(CODE_VERIFY_LIMIT);}Object redisCode = redisMgr.get("jysvc:" + type + ":" + phoneKey);if (Objects.isNull(redisCode)) {redisCode = getVerifyCodeDb(type, phoneMd5);//log.info("getVerifyCodeDb:code:{}",redisCode);}//查询不到验证码 或者验证码有误if (Objects.isNull(redisCode)) {log.error("Verification code has expired");throw ExceptionUtils.throwException(CODE_EXPIRED);}if (!((String) redisCode).equals(code)) {Integer errNum = (Integer) num;//记录失败次数 一小时刷新errNum = errNum == null ? 1 : errNum + 1;redisMgr.put(redisCountKey, 3600, errNum);log.error("Verification code error" + errNum);Integer errSurplus = maxCodeNum - errNum;if (errSurplus == 0) {throw ExceptionUtils.throwException(SEND_TOO_MANY_TIMES);} else {throw ExceptionUtils.throwException(CODE_ERROR_SURPLUS, errSurplus);}}executor.execute(() -> {//异步修改验证码Integer verifytime = (int) Math.floor((System.currentTimeMillis() / 1000));phoneMsgMapper.updateByVerifyPhone(verifytime, phoneMd5);});//验证过后清除Redis中的coderedisMgr.remove("jysvc:" + type + ":" + phoneKey);return true;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38929.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:Python代码的快捷键:让编程速度更加快速高效

Python代码的快捷键&#xff1a;让编程速度更加快速高效 Python是一种面向对象、直译式计算机程序设计语言&#xff0c;备受程序员喜爱。虽然Python本身已经趋于简洁易读&#xff0c;但使用Python代码编辑器的快捷键可以进一步提高编程效率&#xff0c;帮助合理利用时间。 为…

chatgpt赋能python:Python实现滚动小球

Python实现滚动小球 Python是一种高级编程语言&#xff0c;可以用于开发各种类型的应用程序。在此我们将介绍如何使用Python编写代码实现滚动小球。这个小球会在屏幕上滚动&#xff0c;给用户一个视觉效果。 实现滚动小球的Python代码 import pygameBLACK (0, 0, 0) WHITE …

流浪地球2的科学幻想与现实中的未来计算机科技

作为一个科幻迷、《三体》迷&#xff0c;从小时候第一次看《珊瑚岛上的死光》开始&#xff0c;一直期待一部国产科幻电影。等待了40余年&#xff0c;有生之年终于如愿以偿。 周末二刷了《流浪地球2》。丁老师认为这部科幻电影已经超越了1&#xff0c;以及原著&#xff0c;成为…

还在为投稿发愁吗?ICCVIT 2023,一个计算机、视觉与智能技术国际会议

◆ChatGPT热潮带来的行业革新大行其道&#xff0c;相关区块链、信息安全、视觉、机器学习....产业必将带来诸多变革。 本文为大家梳理了近期可投的4则EI检索的国际会议&#xff0c;均可推荐发表SCI&#xff0c;还有超多IEEE Fellow主讲&#xff0c;不仅涵盖计算机各个学科方向&…

不刷卡,不扫码,微信推出刷掌支付;Meta将再裁员6000人;AI“黑箱”被打开?谷歌找到大模型能力涌现机制|极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#…

Chat-GPT有感

Chat-GPT有感 1、Chat-GPT是什么&#xff1f; ​ 2023年当红炸子鸡非Chat-GPT莫属&#xff0c;或许我们即将站在历史的开端、站在人工智能涌现的开端、站在科技百花齐放的开端&#xff0c;我也想问问Chat-GPT&#xff0c;它到底是什么呢&#xff1f; GPT全称为Generative Pre…

前端多语言开发,如何中英文切换

本文以vue为例&#xff1a; 在vue项目中配置两个js文件&#xff0c;一个中文一个英文 将main.js中引入配置好的文件 添加切换中英文的按钮 在methods中写入点击事件 js文件中&#xff0c;把需要的中英文分别写入 在需要的页面中写入 但需要注意的是&#xff0c;在标签中使用$t…

【TypeScript入门】了解TypeScript的工作流程

走进“前端的未来”TS——了解TS的工作流程 文章简介主要内容1 TypeScript工作流程1.1 图示简单解释1.2 理顺区分JS与其他语言 2 如何用TypeScript运行web2.1 为项目添加html文件2.2 用npm高效管理前端项目2.3 npm项目依赖 summary下期预告【基础入门】《前端的未来——TypeScr…

vue2实现中英文切换

简单的使用&#xff08;应用场景&#xff1a;少量几个页面的中英文切换&#xff09; 1、安装 vue-i18n依赖 yarn add vue-i18n 或者 npm install vue-i18n --save-dev 2、在src/components下新建文件夹language&#xff0c;并在文件夹language下新建zh.js及en.js 【src/compon…

vue实现中英文切换

简单的使用&#xff08;应用场景&#xff1a;15个以内页面的中英文切换&#xff09; 1、安装 vue-i18n依赖 yarn add vue-i18n 或者 npm install vue-i18n --save-dev 2、在src/components下新建文件夹language&#xff0c;并在文件夹language下新建zh.js及en.js 【src/co…

vue切换中英语言制作方法(Element+i18n的使用)

介绍&#xff1a; Vue I18n 是 Vue.js 的国际化插件。它可以轻松地将一些本地化功能集成到你的 Vue.js 应用程序中。 1、安装Element ui npm i element-ui -S2、全局引用Element ui 在 main.js 中写入以下内容&#xff1a; import Vue from vue; import ElementUI from elem…

vue3+antd 实现国际化 切换中英文

首先新建结构文件夹 1.下载i18n插件 npm install vue-i18n --save 2.配置i18n.js文件 import { createI18n} from vue-i18n import message from ./langs const localeData {legacy: true, // composition APIlocale: cn,messages: message, } const i18n createI18n(loca…

Vue项目切换语言,vue项目国际化,使用vue-i18n前端实现中英文切换

在项目需求中&#xff0c;我们会遇到国际化的中英文切换。 使用vue搭建的项目中我们可以使用i18n插件进行项目的中英文切换。 npm引入i18n npm install i18n --save-dev为了数据的使用方便&#xff0c;我们需要在src文件夹下建立一个如下图的文件夹 index.js&#xff1a; im…

【latex】论文或参考文献中出现俄文\西班牙文人名的解决方法

常用的latex的编辑器有&#xff1a;texstudio或者overleaf.值得说的是overleaf真的好用&#xff0c;因为它容错率高&#xff0c;即使你的编码语法有些小错误&#xff0c;它也能成功编译。缺点就是&#xff0c;某些论文投稿网站不支持overleaf.但它还是值得我喜欢&#xff0c;因…

前端多语言的切换

前端开发中多语言的切换&#xff0c;可使用js动态替换内容 1、在用户点击切换语言后&#xff0c;把选择的语言版本保存在cookie中 //写入cookie函数 function setCookie(name,value) {var Days 30;var exp new Date();exp.setTime(exp.getTime() Days*24*60*60*1000);docume…

chatgpt赋能python:Python动画制作指南:从入门到精通

Python动画制作指南&#xff1a;从入门到精通 Python作为一种易学易用的编程语言&#xff0c;在数据分析、机器学习等领域已经得到广泛应用。但是你知道吗&#xff1f;Python还可以用来制作动画&#xff01;本文将为你介绍如何用Python制作动画&#xff0c;从入门到精通&#…

吴恩达和OpenAI的《面向开发者的ChatGPT提示工程》精华笔记

《ChatGPT Prompt Engineering for Developers》 面向开发者的ChatGPT提示工程 shadow 趁着假期&#xff0c;学习了prompt课程&#xff0c;做了一些精简和关键知识点的梳理&#xff0c;分享给大家。 LLM 可完成的任务 包括: 总结&#xff08;如总结用户评论&#xff09; 推断&a…

刚刚,吴恩达 ChatGPT 新课三连发!

你有没有想过&#xff0c;你可以自己构建一个AI系统&#xff0c;或者开发一个使用大语言模型&#xff08;LLM&#xff09;的应用&#xff0c;甚至理解并创建扩散模型&#xff1f;我在吴恩达的三门新课程中找到了答案&#xff0c;这些课程让我看到了AI的无限可能性。 好消息&…

AI大神吴恩达与OpenAI官方合作推出的ChatGPT提示工程课,到底在讲什么?

ChatGPT提示工程课程&#xff0c;吴恩达&OpenAI 概述 本课程将着重介绍指令调优LM的开发最佳实践&#xff0c;以帮助开发人员利用LM技术构建聊天机器人等应用程序。 亮点 &#x1f4da; LM可用于快速构建软件应用程序&#xff0c;API可以使开发人员非常快速地构建。&#x…

吴恩达ChatGPT课程最新中文版,已突破3.2k Star!

Datawhale开源 吴恩达ChatGPT课程系列&#xff0c;中文版 项目背景 一个月前&#xff0c;DeepLearning.ai 创始人吴恩达联合 OpenAI 推出入门大模型学习的经典课程《ChatGPT Prompt Engineering for Developers》&#xff0c;深入浅出地介绍了对于开发者&#xff0c;如何构造 …