免费手机号和验证码图片

您所在的位置:网站首页 免费手机号码以及验证码 免费手机号和验证码图片

免费手机号和验证码图片

#免费手机号和验证码图片| 来源: 网络整理| 查看: 265

登录入口

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生成 @Override public Map 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 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 genKeyPair() throws NoSuchAlgorithmException { Map keyMap = new HashMap(); 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 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 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.手机验证码登录

依赖包

com.aliyun aliyun-java-sdk-core 4.0.6

2.1发送验证码

/** * * @param type 发送验证码类型 10 注册 11验证码登录,12修改手机号 **/ @PostMapping("/sendCode") @ApiOperation("发送短信验证码") public BaseResult 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))); } @Override public 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; } @Override public 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 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客户端的登录都是web return BaseResult.success(registerService.codeLogin(reqDTO, getIp(request), "web")); } @Data public class ReqPhoneLoginVO { private String phone; private String code; } public Map 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数据-20210410 String 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); } } @Override public 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中的code redisMgr.remove("jysvc:" + type + ":" + phoneKey); return true; }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3