现在基本上好多前端网页都可以通过短信验证码来动态登录页面,那接下来就说一说这个业务流程,用到的技术包括springboot、redis等
那接下来就开始吧!
一、SpringBoot项目搭建
首先在IDEA中创建一个空项目,类似于Eclipse中的工作空间,然后在这个空项目中创建module项目,
然后傻瓜式下一步
然后下一步
勾选出需要的依赖:
继续下一步,finish完成项目搭建
二、确保Linux中的redis中是开启状态
开启虚拟机中的redis,然后安装redis的时候,记得把redis.conf中的bind改为0.0.0.0,不然可能会有拒绝客户端访问的情况
./redis-server redis.conf
开启后的状态是如图所示:
查看redis是否是已启动状态:ps -aux | grep redis
redis准备就绪,然后开始写相关代码!
三、配置springboot的pom.xml和application.properties相关配置文件
1.配置pom.xml文件中的依赖和插件
楼主用到的依赖如下,其实用来生成mapper和dao层的逆向工程的mybatis的generator可以不用配置,楼主是为了以后和数据库做联系而准备的,可加可不加
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.0.6</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
2.配置application.properties配置文件
数据库驱动和扫描映射文件的配置可以选择忽略,因为本文中并没有和数据库关联,主要是redis的host和port,保证本机和redis之间是可以ping通的:
#数据库驱动配置
spring.datasource.url=jdbc:mysql://localhost:3306/sms?characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
#扫描映射文件
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
#redis配置
spring.redis.host=192.168.43.233
spring.redis.port=6379
三、Redis的接口和相关实现类
1.redis接口
话不多说,直接上代码
public interface MyRedis {
public void set(String key,String value);
public void set(String key,String value,int timeout);
public String get(String key);
public void del(String key);
public Long incr(String key);
public Long decr(String key);
public void setExp(String key,int seconds);
public Long getExp(String key);
public void lpush(String key,Object value);
public Object rpop(String key);
public void hset(String key,String item,String value);
public String hget(String key,String item);
public void hdel(String key,String item);
}
相关实现类:
@Service
public class MyRedisImpl implements MyRedis {
@Autowired
private StringRedisTemplate template;
@Override
public void set(String key, String value) {
ValueOperations<String, String> operations = template.opsForValue();
operations.set(key,value);
}
@Override
public void set(String key, String value, int timeout) {
ValueOperations<String, String> operations = template.opsForValue();
operations.set(key,value,timeout,TimeUnit.SECONDS);
}
@Override
public String get(String key) {
ValueOperations<String, String> operations = template.opsForValue();
return operations.get(key);
}
@Override
public void del(String key) {
template.delete(key);
}
@Override
public Long incr(String key) {
ValueOperations<String, String> operations = template.opsForValue();
Long increment = operations.increment(key,1);
return increment;
}
@Override
public Long decr(String key) {
ValueOperations<String, String> operations = template.opsForValue();
Long increment = operations.increment(key,-1);
return increment;
}
@Override
public void setExp(String key, int seconds) {
template.expire(key,seconds,TimeUnit.SECONDS);
}
@Override
public Long getExp(String key) {
Long expire = template.getExpire(key);
return expire;
}
@Override
public void lpush(String key, Object value) {
ListOperations list = template.opsForList();
list.leftPush(key,value);
}
@Override
public Object rpop(String key) {
ListOperations opsForList = template.opsForList();
return opsForList.rightPop(key);
}
@Override
public void hset(String key, String item, String value) {
HashOperations<String, Object, Object> opsForHash = template.opsForHash();
opsForHash.put(key,item,value);
}
@Override
public String hget(String key, String item) {
HashOperations opsForHash = template.opsForHash();
return (String) opsForHash.get(key,item);
}
@Override
public void hdel(String key, String item) {
HashOperations opsForHash = template.opsForHash();
opsForHash.delete(key,item);
}
}
四、控制层实现业务逻辑
我借用的是美团的首页,仅仅做学习使用,侵权立删
首先把相关的静态资源配置在resources下的static和templates中
然后根据前端对应的点击触发事件写出相应的控制层业务逻辑
1.获取手机动态验证码的controller层
这里会用到阿里提供的短信接口,同时需要配置相关的ID和密匙
还有验证码的工具类(当然你也可以选择自己手写):
导包的时候注意别导错包,一般都是aliyun的包
@Controller
public class SmsAction {
//注入redis服务
@Autowired
private MyRedis myRedis;
//登录页面
@RequestMapping("/mt")
public String shouUI(){
return "mtlogin";
}
@ResponseBody
@RequestMapping("/ajaxNum")
public String sendMsg(String phoneNum) throws Exception{
//设置超时时间-可自行调整
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//初始化ascClient需要的几个参数
final String product = "Dysmsapi";//短信API产品名称(短信产品名固定,无需修改)
final String domain = "dysmsapi.aliyuncs.com";//短信API产品域名(接口地址固定,无需修改)
//替换成你的AK
final String accessKeyId = AliAccessKey.accessKeyId;//你的accessKeyId,参考本文档步骤2
final String accessKeySecret = AliAccessKey.accessKeySecret;//你的accessKeySecret,参考本文档步骤2
//初始化ascClient,暂时不支持多region(请勿修改)
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId,
accessKeySecret);
DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);
IAcsClient acsClient = new DefaultAcsClient(profile);
//组装请求对象
SendSmsRequest request = new SendSmsRequest();
//使用post提交
request.setMethod(MethodType.POST);
//必填:待发送手机号。支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式;发送国际/港澳台消息时,接收号码格式为国际区号+号码,如“85200000000”
request.setPhoneNumbers(phoneNum);
//必填:短信签名-可在短信控制台中找到
request.setSignName("xxx");
//必填:短信模板-可在短信控制台中找到,发送国际/港澳台消息时,请使用国际/港澳台短信模版
request.setTemplateCode("xxxxxxxxxx");
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
//友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
//生成6位的动态验证码
String numeric = RandomStringTLUtils.randomNumeric(6);
request.setTemplateParam("{\"code\":\""+numeric+"\"}");
//可选-上行短信扩展码(扩展码字段控制在7位或以下,无特殊需求用户请忽略此字段)
//request.setSmsUpExtendCode("90997");
//可选:outId为提供给业务方扩展字段,最终在短信回执消息中将此值带回给调用者
request.setOutId("yourOutId");
// //请求失败这里会抛ClientException异常
// SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
// if(sendSmsResponse.getCode() != null && sendSmsResponse.getCode().equals("OK")) {
// //请求成功
// myRedis.set("sms_"+phoneNum,numeric,120);
// return numeric;
// }else {
// System.out.println("失败状态"+sendSmsResponse.getCode());
// System.out.println("失败原因"+sendSmsResponse.getMessage());
// return "error";
// }
//
// //发送成功,存储到缓存
myRedis.set("sms_"+phoneNum,numeric,120);
return numeric;
}
}
上图中有一部分注掉的,楼主为了省钱做测试用注掉了:
这中间可以打几个断点自己跑一下debug看一下每一步的数据是否有误,然后运行结束后,可以看到相关的验证码出现在网页前端,然后可以点解登录,这时候,开始写登录触发的action,我这里是给用户三次错误的机会,三次机会之前都可以重新输入验证码重新登录,你可以在将验证码存储到redis的时候设置该验证码的过期时间,过期以后会自动清除该验证码。我设置的是1分钟。三次错误以后将会清除与该用户相关的所有缓存,并给用户提示,我只是做的测试用,所以并没有把错误页面做的有多好,仅供参考:
@Controller
public class VerfiyAction {
//注入redis服务
@Autowired
private MyRedis redis;
@RequestMapping("/verfiy")
public ModelAndView login(HttpServletRequest request){
ModelAndView modelAndView = new ModelAndView();
//获取用户输入的手机号和验证码
String phoneNum = request.getParameter("mobile"); //客户端用户输入的手机号
String code = request.getParameter("code"); //客户端用户输入的验证码
//获取redis中存放的验证码
String redisCode = redis.get("sms_"+phoneNum);
if(code!=null && !"".equals(code)){
//如果用户输入的验证码和生成的验证码保持一致
if(code.equals(redisCode)){
//删除redis中存放的验证码缓存
redis.del("sms_"+phoneNum);
//同时删除redis中存放的用户输入验证码的错误次数
redis.del("error_"+phoneNum);
modelAndView.setViewName("index");
}else {
//如果验证失败 给该用户总共三次输入机会,大于三次重新获取验证码
//如果是第一次错误,则第一次给用户创建错误次数并存放于redis中,每次错误都会在原有的键上对其值+1
Long incr = redis.incr("error_"+phoneNum);
if (incr > 3){ //如果用户错误的次数大于三次
//清除旧的验证码
redis.del("sms_"+phoneNum);
//清除redis中存放的用户输入验证码的错误次数
redis.del("error_"+phoneNum);
modelAndView.addObject("error","超过验证码错误次数,请重新获取验证码!");
modelAndView.setViewName("error");
}else { //如果用户输入错误且错误小于3次 则刷新一次页面用户继续输入
modelAndView.setViewName("mtlogin");
}
}
}
return modelAndView;
}
}
以上就是单个redis服务器的短信微服务,仅供参考,若有不足之处欢迎指出!