Java简单实现短信验证登录(Session、Redis)

2023-09-22 40 0

前端设计

<div class="login-form"><div style="display: flex; justify-content: space-between"><el-input style="width: 60%" placeholder="请输入手机号" v-model="form.phone" ></el-input><el-button style="width: 38%" @click="sendCode" type="success" :disabled="disabled">{{codeBtnMsg}}</el-button></div><div style="height: 5px"></div><el-input placeholder="请输入验证码" v-model="form.code"></el-input><div style="text-align: center; color: #8c939d;margin: 5px 0">未注册的手机号码验证后自动创建账户</div><el-button @click="login" style="width: 100%; background-color:#f63; color: #fff;">登录</el-button><div style="text-align: right; color:#333333; margin: 5px 0"><a href="/login2.html">密码登录</a></div>
</div><script>const app = new Vue({el: "#app",data: {radio: "",disabled: false, // 发送短信按钮codeBtnMsg: "发送验证码", // 发送短信按钮提示form:{}},methods: {login(){if(!this.radio){this.$message.error("请先确认阅读用户协议!");return}if(!this.form.phone || !this.form.code){this.$message.error("手机号和验证码不能为空!");return}axios.post("/user/login", this.form).then(({data}) => {if(data){// 保存用户信息到sessionsessionStorage.setItem("token", data);}// 跳转到首页location.href = "/index.html"}).catch(err => this.$message.error(err))},goBack(){history.back();},sendCode(){if (!this.form.phone) {this.$message.error("手机号不能为空");return;}// 发送验证码axios.post("/user/code?phone="+this.form.phone).then(() => {}).catch(err => {console.log(err);this.$message.error(err)});// 禁用按钮this.disabled = true;// 按钮倒计时let i = 60;this.codeBtnMsg = (i--) + '秒后可重发'let taskId = setInterval(() => this.codeBtnMsg = (i--) + '秒后可重发', 1000);setTimeout(() => {this.disabled = false;clearInterval(taskId);this.codeBtnMsg = "发送验证码";}, 59000)}}})
</script>

在这里插入图片描述

一、基于Session实现登录

流程

在这里插入图片描述
发送验证码:

用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号

如果手机号合法,后台此时生成对应的验证码,同时将验证码进行保存,然后再通过短信的方式将验证码发送给用户

短信验证码登录、注册:

用户将验证码和手机号进行输入,后台从session中拿到当前验证码,然后和用户输入的验证码进行校验,如果不一致,则无法通过校验,如果一致,则后台根据手机号查询用户,如果用户不存在,则为用户创建账号信息,保存到数据库,无论是否存在,都会将用户信息保存到session中,方便后续获得当前登录信息

校验登录状态:

用户在请求时候,会从cookie中携带者JsessionId到后台,后台通过JsessionId从session中拿到用户信息,如果没有session信息,则进行拦截,如果有session信息,则将用户信息保存到threadLocal中,并且放行。

具体实现

正则工具类: pringBoot后端一些常见的正则表达校验
Result

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {private Boolean success;private String errorMsg;private Object data;private Long total;public static Result ok(){return new Result(true, null, null, null);}public static Result ok(Object data){return new Result(true, null, data, null);}public static Result ok(List<?> data, Long total){return new Result(true, null, data, total);}public static Result fail(String errorMsg){return new Result(false, errorMsg, null, null);}
}
  • 发送验证码
 @Overridepublic Result sendCode(String phone, HttpSession session) {// 1.校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}// 3.符合,生成验证码String code = RandomUtil.randomNumbers(6);// 4.保存验证码到 sessionsession.setAttribute("code",code);// 5.发送验证码 (下面介绍阿里云SMS服务发送)log.debug("发送短信验证码成功,验证码:{}", code);// 返回okreturn Result.ok();}
  • 登录
@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 1.校验手机号String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}// 3.校验验证码Object cacheCode = session.getAttribute("code");String code = loginForm.getCode();if(cacheCode == null || !cacheCode.toString().equals(code)){//3.不一致,报错return Result.fail("验证码错误");}//一致,根据手机号查询用户User user = query().eq("phone", phone).one();//5.判断用户是否存在if(user == null){//不存在,则创建user =  createUserWithPhone(phone);}//7.保存用户信息到session中session.setAttribute("user",user);return Result.ok();}

二、基于Redis实现短信登录

流程

在这里插入图片描述
当注册完成后,用户去登录会去校验用户提交的手机号和验证码,是否一致,如果一致,则根据手机号查询用户信息,不存在则新建,最后将用户数据保存到redis,并且生成token作为redis的key,当我们校验用户是否登录时,会去携带着token进行访问,从redis中取出token对应的value,判断是否存在这个数据,如果没有则拦截,如果存在则将其保存到threadLocal中,并且放行。

实现

自定义常量

public class RedisConstants {public static final String LOGIN_CODE_KEY = "login:code:";public static final Long LOGIN_CODE_TTL = 5L;public static final String LOGIN_USER_KEY = "login:token:";public static final Long LOGIN_USER_TTL = 360000L;
}
  • 发送验证码
@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result sendCode(String phone, HttpSession session) {//1.校验手机号if (!RegexUtils.isPhoneInvalid(phone)) {//2.不符合return Result.fail("手机号格式错误!");}//3.符合String code = RandomUtil.randomNumbers(6);//4.保存验证码到 redisstringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);//5.发送验证码log.debug("发送短信验证码成功,验证码: " + code);//返回okreturn Result.ok();}
  • 登录
 @Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 1.校验手机号String phone = loginForm.getPhone();if (!RegexUtils.isPhoneInvalid(phone)) {return Result.fail("手机格式错误");}//2.从 redis 获取,检验验证码String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);String code = loginForm.getCode();if (cacheCode == null || !cacheCode.toString().equals(code)) {//3.不一致,报错return Result.fail("验证码错误");}//4.一致,根据手机号查询用户User user = query().eq("phone", phone).one();//5.判断用户是否存在if (user == null) {//6.不存在,创建用户并保存user = createUserWithPhone(phone);}// 7.保存用户信息到 redis// 7.1 随机生成token,作为登录令牌String token = UUID.randomUUID().toString(true);// 7.2 将User对象装维HashMap存储UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())); //idInterger类型转为String才能存// 7.3 存储String tokenKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);// 8. 返回tokenreturn Result.ok(token);}

三、阿里云Sms服务发送短信

参考文章:阿里云——Java实现手机短信验证码功能

代码编程
赞赏

相关文章

LeetCode之Isomorphic Strings
LeetCode之Combination Sum III
LeetCode之Find Minimum in Rotated Sorted Array II
LeetCode之Majority Element II
LeetCode之Product of Array Except Self
LeetCodeConvert Sorted List to Binary Search Tree