part1
part2
part3
part4
part5 本页
6.1 短信发送
6.2 手机验证码登入
2.
3. 注册登入阿里云账户。找到短信服务,设置短信签名(上面图片的阿里云、菜鸟裹裹、天猫…),模板等等
4. 设置AccessKey
5. 看帮助文档,导入对应的Util包
<div id="login" v-loading="loading">
<div class="divHead">登录div>
<div class="divContainer">
<el-input placeholder=" 请输入手机号码" v-model="form.phone" maxlength='20'/>el-input>
<div class="divSplit">div>
<el-input placeholder=" 请输入验证码" v-model="form.code" maxlength='20'/>el-input>
<span @click='getCode'>获取验证码span>
div>
<div class="divMsg" v-if="msgFlag">手机号输入不正确,请重新输入div>
<el-button type="primary" :class="{btnSubmit:1===1,btnNoPhone:!form.phone,btnPhone:form.phone}" @click="btnLogin">登录el-button>
div>
getCode(){
this.form.code = ''
//正则表达式验证手机号是否正确
const regex = /^(13[0-9]{9})|(15[0-9]{9})|(17[0-9]{9})|(18[0-9]{9})|(19[0-9]{9})$/;
if (regex.test(this.form.phone)) {
this.msgFlag = false
//sendMsgApi({phone:this.form.phone}) 一个json,key是phone,value是前端输入的手机号
this.form.code = (Math.random()*1000000).toFixed(0)
}else{
this.msgFlag = true
}
},
这里的msgFlag作用是对应之前的:
,即已经通过了验证,不需要弹出提示信息
正常情况下,通过sendMsgApi发送axios请求,请求到后端发送验证码
function sendMsgApi(data) {
return $axios({
'url': '/user/sendMsg',
'method': 'post',
data
})
}
async btnLogin(){
if(this.form.phone && this.form.code){
this.loading = true
const res = await loginApi(this.form)
this.loading = false
if(res.code === 1){
sessionStorage.setItem("userPhone",this.form.phone)
window.requestAnimationFrame(()=>{
window.location.href= '/front/index.html'
})
}else{
this.$notify({ type:'warning', message:res.msg});
}
}else{
this.$notify({ type:'warning', message:'请输入手机号码'});
}
}
整体思路:短信发送的controller处理完之后,把发送的验证码存到session中。之后是登入的controller,把前端提交的短信验证码和session中的验证码进行比较,如果手机号和验证码都没错,那就放行(设置一个User的session,拦截器就会放行),同时查数据库,看看有没有对应的手机号,没有的话就插入这条手机号的数据
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session){
//获取手机号
String phone = user.getPhone();
if(StringUtils.isNotEmpty(phone)){
//生成随机的4位验证码
String code = ValidateCodeUtils.generateValidateCode(4).toString();
log.info("code={}",code);
//调用阿里云提供的短信服务API完成发送短信
//SMSUtils.sendMessage("瑞吉外卖","",phone,code);
//需要将生成的验证码保存到Session
session.setAttribute(phone,code);
return R.success("手机验证码短信发送成功");
}
return R.error("短信发送失败");
}
/**
* 登入验证
* @param map 前端传过来电话和验证码,User没有验证码的属性,接不住,所以用map,也可以用Dto
* @param session 验证成功了,就把user存入session,拦截器那边放行
* @return
*/
@PostMapping("/login")
public RetObj loginController(@RequestBody Map<String,String> map, HttpSession session){
String phone = map.get("phone");
String code = map.get("code");
String sessionCode = session.getAttribute(phone).toString();
if (StringUtils.isNotBlank(sessionCode) && code.equals(sessionCode)){
//用户名秘密验证成功!
//先查电话号码在不在数据库里,如果不在,就要保存
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getPhone,phone);
User user = userService.getOne(lambdaQueryWrapper);
if (user == null){
//存入数据库
User user1 = new User();
user1.setPhone(phone);
user1.setStatus(1);
userService.save(user1);
//session.setAttribute("user",user1.getId());
}else {
session.setAttribute(Contants.SESSION_USERID,user.getId());
}
return RetObj.success("登入成功!");
}else {
return RetObj.error("登入失败!");
}
package cn.edu.uestc.ruijitakeout.common.interceptor;
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
log.info("当前路径:{}", uri);
/**
* HandlerMethod=>Controller中标注@RequestMapping的方法
* 需要配置静态资源不拦截时,添加这块逻辑 => 前后端分离项目
*
*/
// 是我们的controller中的方法就拦截,如果不是的话,放行,给加载静态资源
if (!(handler instanceof HandlerMethod)) {
log.info("是静态资源或非controller中的方法,放行");
return true;
}
//1-通过session判断是否登入
if (request.getSession().getAttribute(Contants.SESSIONLOGIN) != null) {
log.info("用户已经登入,id={}", request.getSession().getAttribute(Contants.SESSIONLOGIN));
//进入拦截器后,给threadLocal绑定session,让后面需要的公共字段自动填充的时候,填充这个updateUser
//每次http请求,会分配一个新的线程来处理
BaseContext.setThreadLocal((Long) request.getSession().getAttribute(Contants.SESSIONLOGIN));
log.info("拦截器这里设置了ThreadLocal,值为:{}",(Long) request.getSession().getAttribute(Contants.SESSIONLOGIN));
log.info("当前线程id={}",Thread.currentThread().getId());
return true;
} else if (request.getSession().getAttribute(Contants.SESSION_USERID) != null) {
log.info("用户已经登入,id={}", request.getSession().getAttribute(Contants.SESSION_USERID));
//进入拦截器后,给threadLocal绑定session,让后面需要的公共字段自动填充的时候,填充这个updateUser
//每次http请求,会分配一个新的线程来处理
BaseContext.setThreadLocal((Long) request.getSession().getAttribute(Contants.SESSION_USERID));
log.info("拦截器这里设置了ThreadLocal,值为:{}",(Long) request.getSession().getAttribute(Contants.SESSION_USERID));
log.info("当前线程id={}",Thread.currentThread().getId());
return true;
}
//这里应该跳转到登入页面,如何做?
//5、如果未登录则返回未登录结果,通过输出流方式向客户端页面响应数据
log.info("用户未登入,通过输出流方式向客户端页面响应数据,打回登入页面");
response.getWriter().write(JSON.toJSONString(RetObj.error("NOTLOGIN")));//与前端request.js中的代码呼应
return false;
}
package cn.edu.uestc.ruijitakeout.common.config;
@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 拓展消息转换器
* @param converters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("拓展消息转换器成功加载");
//创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置对象转换器,底层使用Jackson将Java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
//将上面的消息转换器对象追加到mvc框架的转换器集合中
converters.add(0,messageConverter);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//重写方法,添加拦截器方法
registry.addInterceptor(loginInterceptor())
//拦截哪些路径
.addPathPatterns("/**")
//不拦截路径
.excludePathPatterns("/employee/backend/page/login/login.do",
//"/backend/**",
"/employee/backend/page/login/logout.do",
//"/front/**",
"/error",
"/user/**"
);
}
@Bean
public LoginInterceptor loginInterceptor(){
return new LoginInterceptor();
}
}