手机发送短信验证码登录完整实例
项目需求
后台生成随机6位数作为验证码,发送给手机,同时将验证码存入缓存,用户登录时验证输入的验证码是否过期或者是否正确。
一、发送短信
1.了解短信发送
通过发送短信的API,建立一个URL类的对象打开网络连接,通过连接对象得到输入流,就能实现短信发送
1 URL url= new URL(""https://XXXXXX?phoneNumbers=[手机号]&content=[短信内容]"");//使用方法,拼接参数 2 url.openConnection().getInputStream();
封装上述方法
1 import com.alibaba.fastjson.JSONObject; 2 import org.apache.commons.lang.StringUtils; 3 4 import java.io.*; 5 import java.net.HttpURLConnection; 6 import java.net.URL; 7 import java.net.URLConnection; 8 import java.util.HashMap; 9 import java.util.List; 10 import java.util.Map; 11 import java.util.Map.Entry; 12 13 public class SendRequestMethod { 14 15 /** 16 * 向指定 URL 发送POST方法的请求 17 * 18 * @param url 发送请求的 URL 19 * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 20 * @return 所代表远程资源的响应结果 21 */ 22 public static String postMethod(String url, String param, Map<String, String> headParam) { 23 Long s0 = System.currentTimeMillis(); 24 PrintWriter out = null; 25 BufferedReader in = null; 26 String result = ""; 27 try { 28 URL realUrl = new URL(url); 29 // 打开和URL之间的连接 30 HttpURLConnection conn = (HttpURLConnection)realUrl.openConnection(); 31 // 设置通用的请求属性 32 conn.setRequestProperty("accept", "*/*"); 33 conn.setRequestMethod("POST"); 34 conn.setRequestProperty("Accept-Charset", "UTF-8"); 35 conn.setRequestProperty("Content-Type", "application/json"); 36 conn.setRequestProperty("charset","UTF-8"); 37 if (headParam != null) { 38 for (Entry<String, String> entry : headParam.entrySet()) { 39 conn.setRequestProperty(entry.getKey(), entry.getValue()); 40 } 41 } 42 // 发送POST请求必须设置如下两行 43 conn.setUseCaches(false); 44 conn.setDoOutput(true); 45 conn.setDoInput(true); 46 conn.setConnectTimeout(1000000); 47 conn.setReadTimeout(1000000); 48 49 // 获取URLConnection对象对应的输出流 50 if(StringUtils.isNotBlank(param)){ 51 out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream(),"utf-8")); 52 out.write(param); 53 // flush输出流的缓冲 54 out.flush(); 55 } 56 57 // 定义BufferedReader输入流来读取URL的响应 58 in = new BufferedReader( 59 new InputStreamReader(conn.getInputStream(), "utf-8")); 60 String line; 61 while ((line = in.readLine()) != null) { 62 result += line; 63 } 64 } catch (Exception e) { 65 System.out.println("发送 POST 请求出现异常!" + e); 66 System.out.println(JSONObject.toJSONString(e)); 67 e.printStackTrace(); 68 } 69 //使用finally块来关闭输出流、输入流 70 finally { 71 try { 72 if (out != null) { 73 out.close(); 74 } 75 if (in != null) { 76 in.close(); 77 } 78 } catch (IOException ex) { 79 ex.printStackTrace(); 80 } 81 } 82 return result; 83 }
发送短信设置发送内容和手机号
1 import com.alibaba.fastjson.JSONObject; 2 import com.wisesoft.core.util.prop.PropertiesUtil; 3 import org.apache.commons.lang.StringUtils; 4 5 import java.util.*; 6 7 public class SendMessage { 8 9 /** 10 * 短信API服务器地址(根据自己的url设置) 11 */ 12 private static String pathUrl= "http://xxxxx"; 13 14 15 public static JSONObject send(String content,String... phoneNumbers){ 16 17 JSONObject param = new JSONObject(2); 18 param.put("content",content); 19 param.put("phoneNumbers", StringUtils.join(phoneNumbers,",")); 20 21 Map<String,String> headParam = new HashMap<>(); 22 headParam.put("Content-Type","application/json;charset=UTF-8"); 23 24 String requestResult = SendRequestMethod .postMethod(pathUrl,param.toJSONString(),headParam); 25 JSONObject result = JSONObject.parseObject(requestResult ); 26 return result; 27 } 28 }
二、手机号登录
1.发送短信接口
写接口之前,先写个缓存(这里用的是Redis)的工具类(只写了要用的两个方法)
=
1 package com.wisesoft.scenic.service.joggle.utils.redis; 2 3 import com.wisesoft.core.util.StringUtil; 4 import com.wisesoft.core.util.prop.FrameworkProps; 5 import com.wisesoft.scenic.interfaceserver.vo.InterfaceServerVO; 6 import redis.clients.jedis.Jedis; 7 import redis.clients.jedis.JedisPool; 8 import redis.clients.jedis.JedisPoolConfig; 9 import redis.clients.jedis.JedisSentinelPool; 10 11 import java.util.HashSet; 12 import java.util.List; 13 import java.util.Map; 14 import java.util.Set; 15 16 public class RedisUtil { 17 18 private static JedisSentinelPool sentinelPool; 19 private static JedisPool jedisPool; 20 21 static { 22 23 String str_host = getProperty("redis.host", ""); 24 String str_port = getProperty("redis.port", ""); 25 String password = getProperty("redis.password", ""); 26 int database = getProperty("redis.database", 0); 27 int timeout = getProperty("redis.timeout", 5000); 28 String runmodel = getProperty("redis.runmodel", ""); 29 30 //连接池配置 31 JedisPoolConfig config = new JedisPoolConfig(); 32 config .setMaxTotal(10); 33 config .setMaxIdle(5); 34 config .setMinIdle(5); 35 ..... 36 37 if (StringUtil.isNotBlank(runmodel) && "cluster".equalsIgnoreCase(runmodel)) { 38 // mastername是我们配置给哨兵的服务名称 39 String mastername = getProperty("redis.mastername", ""); 40 int port = 6379; 41 // 哨兵信息(举例,根据实际情况不同配置) 42 Set<String> sentinels = new HashSet<String>(Arrays.asList( 43 "10.201.7.171:26379", 44 "10.201.7.175:26379", 45 "10.201.7.176:26379" 46 )); 47 sentinelPool = new JedisSentinelPool(mastername, sentinels, config, timeout, password, database); 48 49 } else { 50 int port = Integer.valueOf(str_port); 51 jedisPool = new JedisPool(config, str_host, port, timeout, password); 52 } 53 } 54 55 private RedisClient() { 56 } 57 58 public static String getProperty(String name, String defaultValue) { 59 return FrameworkProps.getProperty(name, defaultValue); 60 } 61 62 /** 63 * 设置缓存(没有过期时间) 64 * 65 */ 66 public static String set(String key, String value) { 67 Jedis jedis = getJedis(); 68 try { 69 String val = jedis.set(key, value); 70 return val; 71 } finally { 72 jedis.close(); 73 } 74 } 75 76 public static String get(String key) { 77 Jedis jedis = getJedis(); 78 try { 79 String val = jedis.get(key); 80 return val; 81 } finally { 82 jedis.close(); 83 } 84 } 85 86 /** 87 * 设置缓存(有过期时间) 88 * 89 */ 90 public static String set(String key, String value, int second) { 91 Jedis jedis = getJedis(); 92 try { 93 String val = jedis.set(key, value); 94 jedis.expire(key, second); 95 return val; 96 } finally { 97 jedis.close(); 98 } 99 } 100 101 public static Long del(String key) { 102 Jedis jedis = getJedis(); 103 try { 104 Long obj = jedis.del(key); 105 return obj; 106 } finally { 107 jedis.close(); 108 } 109 } 110 111 /** 112 * 获取客户端连接 113 * 114 */ 115 public static Jedis getJedis() { 116 if (sentinelPool != null) { 117 return sentinelPool.getResource(); 118 } 119 return jedisPool.getResource(); 120 } 121 122 }
发送短信接口代码如下:
1 @RequestMapping(value = "/sendMessage", method = RequestMethod.POST, produces = "application/json;charset=utf-8") 2 @ResponseBody 3 public String sendMessage(@RequestBody String jsonStr) { 4 JSONObject object = JSON.parseObject(jsonStr); 5 String phone = object.getString("phone"); 6 JSONObject object = new JSONObject(); 7 // 随机生成验证码 8 String verifyCode = (int)(Math.random()* 900000 + 100000)+""; 9 10 // redis配置,实际应该封装一个工具类,这里简单写一下 11 RedisUtil.set(phone + "_verifyCode", verifyCode, 600); 12 String content = "【CSDN】验证码:"+verifyCode+",您正在使用短信验证码登录,有效期10分钟。"; 13 JSONObject send = SendMessage.send(content, phone); 14 if(send != null && 200 == send.getIntValue("code")){ 15 object.put("code",0); 16 object.put("msg","发送成功"); 17 return object.toString(); 18 } else { 19 object.put("code",1); 20 object.put("msg","发送失败"); 21 return object.toString(); 22 } 23 24 }
2.登录接口
代码如下:
1 @RequestMapping(value = "/login", method = RequestMethod.POST, produces = "application/json;charset=utf-8") 2 @ResponseBody 3 public String login(@RequestBody String jsonStr) { 4 JSONObject object = JSON.parseObject(jsonStr); 5 String phone = object.getString("phone"); 6 String verifyCode = object.getString("verifyCode"); 7 JSONObject object = new JSONObject(); 8 9 if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)) { 10 object.put("code",1); 11 object.put("msg","手机号或验证码不能为空"); 12 return object.toString(); 13 } else if (!loginService.checkPhone(phone)) { 14 object.put("code",1); 15 object.put("msg","输入的手机号非法,请输入正确的手机号"); 16 return object.toString(); 17 } 18 return loginService.loginByPhone(phone, verifyCode); 19 }
登录业务逻辑
1 @Override 2 public String loginByPhone(String phone, String verifyCode) { 3 JSONObject object = new JSONObject(); 4 // 获取短信验证码 5 String codeStr = RedisUtil.get(phone + "_verifyCode"); 6 if (StringUtil.isEmpty(codeStr)) { 7 object.put("code",1); 8 object.put("msg","验证码已失效,请重新发送"); 9 return object.toString(); 10 } 11 // 判断验证码是否正确 12 if (verifyCode.equals(codeStr)) { 13 // 查询用户信息 14 User user = userService.getByPhone(phone); 15 Date date = new Date(); 16 // 用户登录信息 17 UserAccount account = new UserAccount(); 18 // 判断账号是否存在 19 if (user == null) { 20 // 用户不存在,则注册账号 21 User userInfo= new User(); 22 userInfo.setId(UuidUtil.generateUUID()); 23 userInfo.setPhoneNum(phone); 24 userInfo.setCreateTime(date); 25 userInfo.setUpdateTime(date); 26 userInfo.setRegTime(date); 27 userInfo.setLastLoginTime(date); 28 userService.insert(userInfo); 29 BeanUtils.copyProperties(userInfo,account); 30 } else { 31 // 用户存在 32 if (user.getLastLoginTime() != null) { 33 date = user.getLastLoginTime(); 34 } 35 BeanUtils.copyProperties(user,account); 36 // 更新登录信息 37 user.setLastLoginTime(new Date()); 38 userService.update(user); 39 } 40 // 设置缓存(没有过期时间) 41 String userJson = JSONObject.toJSONString(account); 42 RedisUtil.set("user" + account.getUserId(), userJson); 43 object.put("code",0); 44 object.put("msg","登录成功"); 45 object.put("result",account); 46 return object.toString(); 47 } else { 48 object.put("code",1); 49 object.put("msg","输入验证码不正确"); 50 return object.toString(); 51 } 52 } 53 54 @Override 55 public boolean checkPhone(String phone) { 56 // 手机号格式(不验证号码段) 57 Pattern p = Pattern.compile("^^1[0-9]{10}$"); 58 Matcher m = p.matcher(phone); 59 return m.matches(); 60 }
总结
以上就是今天要讲的内容,本文仅仅简单介绍了手机验证码登录的流程,很多细节并没有深入,若有问题,还请大家多多指教。