java抽奖系统登录上(三)
6.用户登录模块
这里的登录方式有两种,第一种是密码加账号登录;2、第二种是手机号加验证码来登录;
6.1 获取验证码
下面时序图中短信的转发使用的是阿里云短信服务工具;
6.2 阿里云短信服务
在阿里云短信服务上搭建项目的服务;
将该服务集成到项目上:
将其maven依赖添加到项目中;
在项目的工具类中实现一个工具类SMSUtil
首先配置id和secret,在配置文件里面;
sms.access-key-id=111111111111111111
sms.access-key-secret=1111111111111111
sms.sign-name=111111111
其次完善SMSUtil类:
@Component
public class SMSUtil {
private static final Logger logger = LoggerFactory.getLogger(SMSUtil.class);
@Value(value = "${sms.sign-name}")
private String signName;
@Value(value = "${sms.access-key-id}")
private String accessKeyId;
@Value(value = "${sms.access-key-secret}")
private String accessKeySecret;
/**
* 发送短信
*
* @param templateCode 模板号
* @param phoneNumbers 手机号
* @param templateParam 模板参数 {"key":"value"}
*/
public void sendMessage(String templateCode, String phoneNumbers, String templateParam) {
try {
Client client = createClient();
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setSignName(signName)
.setTemplateCode(templateCode)
.setPhoneNumbers(phoneNumbers)
.setTemplateParam(templateParam);
RuntimeOptions runtime = new RuntimeOptions();
SendSmsResponse response = client.sendSmsWithOptions(sendSmsRequest, runtime);
if (null != response.getBody()
&& null != response.getBody().getMessage()
&& "OK".equals(response.getBody().getMessage())) {
logger.info("向{}发送信息成功,templateCode={}", phoneNumbers, templateCode);
return;
}
logger.error("向{}发送信息失败,templateCode={},失败原因:{}",
phoneNumbers, templateCode, response.getBody().getMessage());
} catch (TeaException error) {
logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);
}
}
/**
* 使用AK&SK初始化账号Client
* @return Client
*/
private Client createClient() throws Exception {
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。
// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html。
Config config = new Config()
.setAccessKeyId(accessKeyId)
.setAccessKeySecret(accessKeySecret);
// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapi
config.endpoint = "dysmsapi.aliyuncs.com";
return new Client(config);
}
}
使用测试类进行测试:
@SpringBootTest
public class SMSTest {
@Autowired
private SMSUtil smsUtil;
@Test
void smsTest() {
smsUtil.sendMessage(
"SMS_475795287",
"15809211624",
"{\"code\":\"7945\"}");
// {"code":"7945"}
//使用json字符串的格式,对于符号要进行转意
}
}
测试结果如下:验证码成功的发向手机;
6.3 redis的使用
redis本次使用主要是用来存储验证码,这里缓存的验证码用来和客户端输入的验证码进行正误比较;相对于从磁盘和数据库中访问数据,从内存中进行访问速度会很快;redis可以部署在集群环境中,是将数据存放在内存中;
redis的优点:
1、redis将数据放在内存中,可以提高数据访问的速度;
2、redis是由c语言实现的,距离操作系统比较近;
3、redis使用的是单线程,可以避免锁竞争;
将其依赖放入到项目文件中,配置端口转发,使用端口转发的方式,直接吧服务器的redis端口映射到本地,这样本地主机就能访问redis;
配置redis的相关项:
## redis spring boot 3.x ##
spring.data.redis.host=localhost
spring.data.redis.port=8888
# 连接空闲超过N(s秒、ms毫秒)后关闭,0为禁⽤,这⾥配置值和tcp-keepalive值⼀致
spring.data.redis.timeout=60s
# 默认使⽤ lettuce 连接池
# 允许最⼤连接数,默认8(负值表⽰没有限制)
spring.data.redis.lettuce.pool.max-active=8
# 最⼤空闲连接数,默认8
spring.data.redis.lettuce.pool.max-idle=8
# 最⼩空闲连接数,默认0
spring.data.redis.lettuce.pool.min-idle=0
# 连接⽤完时,新的请求等待时间(s秒、ms毫秒),超过该时间抛出异常JedisConnectionException,(默认-1,负值表⽰没有限制)
spring.data.redis.lettuce.pool.max-wait=5s
在进行连接之前首先在服务器上启动redis;service redis-server start
其次连接之后显示连接成功,如下所示
接下来对于redis中关于stringredistemplate进行工具类封装,后续对于redis的操作通过redisutil工具类来完成:下面代码是对于redis中数据的增加,删除,查找的实现:
@Configuration
public class RedisUtil {
private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
/**
* RedisTemplate 序列化规则:先将被存储的数据转换为字节数组(不可读),再存储到redis中,
* 读取的时候按照字节数组读取
* StringRedisTemplate序列化规则 : 直接存放的就是 string (可读),写的什么读到的就是什么
* 项目背景:String,String(k,v)类型
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
// --------------- String ----------------------
// 本次考虑的存储仅限于string类型的
/**
* 设置值
*
* @param key
* @param value
* @return
*/
public boolean set(String key, String value) {
try {
stringRedisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
logger.error("RedisUtil error, set({}, {})", key, value, e);
return false;
}
}
/**
* 设置值(设置过期时间)
*
* @param key
* @param value
* @param time 单位:秒
* @return
*/
public boolean set(String key, String value, Long time) {
try {
stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
return true;
} catch (Exception e) {
logger.error("RedisUtil error, set({}, {}, {})", key, value, time, e);
//e不需要占位符
return false;
}
}
/**
* 获取值
*
* @param key
* @return
*/
public String get(String key) {
try {
return StringUtils.hasText(key)
? stringRedisTemplate.opsForValue().get(key)
: null;
} catch (Exception e) {
logger.error("RedisUtil error, get({})", key, e);
return null;
}
}
/**
* 删除值
*
* @param key
* @return
*/
public boolean del(String... key) {
// String...可以传入多种k
try {
if (null != key && key.length > 0) {
if (key.length == 1) {
stringRedisTemplate.delete(key[0]);
} else {
stringRedisTemplate.delete(
(Collection<String>) CollectionUtils.arrayToList(key)
);
}
}
return true;
} catch (Exception e) {
logger.error("RedisUtil error, del({})", key, e);
return false;
}
}
/**
* 判断key是否存在
*
* @param key
* @return
*/
public boolean hasKey(String key) {
try {
return StringUtils.hasText(key) ? stringRedisTemplate.hasKey(key) : false;
} catch (Exception e) {
logger.error("RedisUtil error, hasKey({})", key, e);
return false;
}
}
}
接下来完成根据手机号从redis'中获取验证码的操作:
service层短信验证业务接口:
public interface VerificationCodeService {
//验证码服务接口
//发送验证码
void sendVerificationCode(String phoneNumber);
//获取验证码 从缓存中
String getVerificationCode(String phoneNumber);
}
对于该层接口的实现方法
@Service
public class VerificationCodeServiceImpl implements VerificationCodeService {
// 对于redis里面的key需要标准化:为了区分业务,应该给key定义前缀:
//向活动相关的获取信息,人员相关的活动信息,下面虽然手机号一样,但是对于活动业务就区分开了
//防治一个key参加多种业务存储的value被替代
// VerificationCode_13111111111:1233、User_13111111111:userInfo
private static final String VERIFICATION_CODE_PREFIX = "VERIFICATION_CODE_";
private static final Long VERIFICATION_CODE_TIMEOUT = 120L;
private static final String VERIFICATION_CODE_TEMPLATE_CODE = "SMS_475795287";
@Autowired
private SMSUtil smsUtil;
@Autowired
private RedisUtil redisUtil;
@Override
public void sendVerificationCode(String phoneNumber) {
//校验手机号
if(!RegexUtil.checkMobile(phoneNumber)){
throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
}
//生成随机验证码 hutool里面的一个工具类完成验证码=随机生成
String code = CaptchaUtil.getCaptcha(4);
//发送验证码
// {"code":"xxxx"},参数要求的是json格式的字符串,所以提前使用map进行包装
Map<String, String> map = new HashMap<>();
map.put("code", code);
smsUtil.sendMessage(
VERIFICATION_CODE_TEMPLATE_CODE,
phoneNumber,
JacksonUtil.writeValueAsString(map));//将string的map转为json格式的字符串
//缓存验证码
// 131xxxxxxxx: code 一个手机号对应一个验证码
redisUtil.set(VERIFICATION_CODE_PREFIX + phoneNumber, code, VERIFICATION_CODE_TIMEOUT);
}
@Override
public String getVerificationCode(String phoneNumber) {
//校验手机号
if(!RegexUtil.checkMobile(phoneNumber)){
throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
}
String result= redisUtil.get(VERIFICATION_CODE_PREFIX + phoneNumber);
return result;
}
}
在生成短信四位随机数的时候使用了hutool包里面的方法,故此对随机生成验证码的相关代码进行工具类的封装:
public class CaptchaUtil { /** * 生成随机验证码 * * @param length 几位 * @return */ public static String getCaptcha(int length) { // 自定义纯数字的验证码(随机4位数字,可重复) RandomGenerator randomGenerator = new RandomGenerator("0123456789", length); LineCaptcha lineCaptcha = cn.hutool.captcha.CaptchaUtil.createLineCaptcha(200, 100); lineCaptcha.setGenerator(randomGenerator); // 重新生成code lineCaptcha.createCode(); return lineCaptcha.getCode(); } }
测试验证码发送:
手机可以收到短信验证码,下图是redis缓存测试结果:
controller层代码实现:
@RequestMapping("/verification-code/send")
public CommonResult<Boolean> sendVerificationCode(String phoneNumber) {
logger.info("sendVerificationCode phoneNumber:{}", phoneNumber);
verificationCodeService.sendVerificationCode(phoneNumber);
return CommonResult.success(Boolean.TRUE);
}
ps:委婉待续,谢谢观看!
原文地址:https://blog.csdn.net/2202_76101487/article/details/144396619
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!