酒店信息管理项目——之短信验证码登录
这个项目是本人在工作之余利用闲暇时间进行开发的,主要是为了弥补大学毕业设计的遗憾(本人并不是没有过_,只是没有做自己喜欢的毕业设计)。酒店信息管理系统是一个前后端分离项目,前端使用vue,后端使用java开发,开发完后部署在云服务器上。
废话少说直接上项目地址,http://121.43.170.105:8004
账号:black 密码:root (也可以自行注册)
如果有不明白可以看我的主页关于酒店信息管理项目系列的文章
前言
有需要代码的小伙伴私信我WX: 1208191350
一、项目结构和技术栈的使用
项目的整体结构采用的是前后端分离的形式:
后端框架的使用:spring-boot maven mybatis-plus mybatisX
后端技术栈的使用:
1.JWT令牌权限校验与授权登录
2.短信验证码登录,使用阿里云短信服务
3.swagger api接口文档生成器
4.mybatis-plus-generator 代码生成
5.七牛云图片上传服务,将图片保存到三方服务器
6.结合数据库使用spring-boot的定时调度任务
7.redis进行短信验证码的验证,和JWT令牌(Token)的激活(保活)
后台项目结构:
前端框架的使用:vue-cli3 element-ui vue-router
前端技术栈的使用:html css scss js vue axsio
前端项目结构:
二、功能介绍
1.短信验证码登录
1.1使用redis的工具类:
package com.hotel.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
// 去除警告
@SuppressWarnings("all")
public class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
1.1使用导入阿里云短信服务的依赖包:
<!-- 阿里云短信服务的依赖包 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.4.6</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-ecs</artifactId>
<version>4.17.6</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
1.3使用阿里云短息服务的工具类:
package com.hotel.sms;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Random;
/**
* User: codeWsj
* Date: 2021/8/23
*/
public class SmsUtil {
static final String accessKeyId ;
static final String accessKeySecret ;
static {
InputStream inputStream = SmsUtil.class.getClassLoader().getResourceAsStream("sms.properties");
Properties properties = new Properties();
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
accessKeyId = properties.getProperty("accessKeyId");
accessKeySecret = properties.getProperty("accessKeySecret");
}
public static String sendSms(String phoneNum) throws Exception {
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", "cn-hangzhou");
request.putQueryParameter("PhoneNumbers", phoneNum);//设置手机号
request.putQueryParameter("SignName", "oldqiu");//设置短信签名
request.putQueryParameter("TemplateCode", "SMS_186953933");//设置短信模板
StringBuffer randomNumStr = new StringBuffer();
for (int i = 0; i < 6; i++) {//产生随机验证码
randomNumStr.append(new Random().nextInt(10));
}
System.out.println(randomNumStr.toString());
/**
* 存到redis里面,可以设置过去时间,这里不存到session里面
*/
request.putQueryParameter("TemplateParam", "{\"code\":\""+randomNumStr.toString()+"\"}");//设置验证码
try {
CommonResponse response = client.getCommonResponse(request);
System.out.println(response.getData());
} catch (Exception e) {
e.printStackTrace();
}
return randomNumStr.toString();
}
}
1.5随机码的生成类:
package com.hotel.sms;
import java.util.Random;
public class VerifyCodeUtil {
public static final String VERIFY_CODES = "2345678";
public static String generateVerifyCode(int verifySize, String sources) {
if (sources == null || sources.length() == 0) {
sources = VERIFY_CODES;
}
int codesLen = sources.length();
Random rand = new Random(System.currentTimeMillis());
StringBuilder verifyCode = new StringBuilder(verifySize);
for (int i = 0; i < verifySize; i++) {
verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
}
return verifyCode.toString();
}
}
1.6业务代码实现(发送验证码和校验验证码,JWT的部分请看其他的文章):
/**
* 发送验证码
* @param phoneNum
* @return
* @throws Exception
*/
public ResultVo sendCode(String phoneNum) throws Exception {
// 检验手机号码的正确性
if (!RegexUtils.isChinaPhoneLegal(phoneNum)) return new ResultVo(2000,"输入的手机号码不正确");
// 查询数据库——校验电话号码是否存在,不存在直接返回,请其先注册
QueryWrapper queryWrapper = new QueryWrapper<User>();
queryWrapper.eq("phone", phoneNum);
User user = userMapper.selectOne(queryWrapper);
if (user == null) return new ResultVo(2000,"手机号码不存在,请注册");
String code = SmsUtil.sendSms(phoneNum);
// 发送成功,存到redis里面,有效1分钟
redisUtil.set("phone",phoneNum,60);
redisUtil.set("code",code,60);
return new ResultVo(1000,"发送验证码成功",code);
}
/**
* 校验验证码
* @param map
* @return
* @throws Exception
*/
@Override
public ResultVo verifyCode(Map map) throws Exception {
String phone = (String) map.get("phone");
String code = (String) map.get("code");
// 判断手机号码是否正常
if (!RegexUtils.isChinaPhoneLegal(phone)) return new ResultVo(2000,"请输入正常格式的号码");
if (StringUtils.isEmpty(code) || StringUtils.isEmpty(phone)) return new ResultVo(2000,"电话或验证码不能为空");
// 从redis里面取值
String codeRedis = (String) redisUtil.get("code");
String phoneNum = (String) redisUtil.get("phone");
// 判断手机号码是否和发送验证码的手机号码一致
if (!phone.equals(phoneNum)) return new ResultVo(2000,"手机号码错误");
// 判断验证码是否过期
if (StringUtils.isEmpty(code)) return new ResultVo(2001,"验证码已过期");
// 判断验证码是否错误
if (!code.equals(codeRedis)) return new ResultVo(2001,"验证码错误");
// 登录成功返回token令牌给前端
// 查询数据库——校验电话号码是否存在,不能存在直接返回
QueryWrapper queryWrapper = new QueryWrapper<User>();
queryWrapper.eq("phone", phone);
User user = userMapper.selectOne(queryWrapper);
if (user == null) return new ResultVo(2000,"手机号码不存在,请注册");
// 颁发JWT令牌
PrivateKey privateKey = null;
try {
ClassPathResource resource = new ClassPathResource("rsa.pri");
InputStream inputStream = resource.getInputStream();
privateKey = RsaUtils.getPrivateKey(JwtUtils.readBytes3(inputStream));
} catch (Exception e) {
e.printStackTrace();
}
// 把user信息存储到token,设置token过期时间为30分钟
String jwtToken = JwtUtils.generateTokenExpireInMinutes(user, privateKey, 30);
return new ResultVo(1000,"登录成功",jwtToken);
}