part1
part2
part3
part4
part5 本頁(yè)
6 移動(dòng)端短信發(fā)送和手機(jī)驗(yàn)證碼登入
6.1 短信發(fā)送
6.2 手機(jī)驗(yàn)證碼登入
6.1 短信發(fā)送
6.1.1整體分析
2.
3. 注冊(cè)登入阿里云賬戶。找到短信服務(wù),設(shè)置短信簽名(上面圖片的阿里云、菜鳥裹裹、天貓…),模板等等
4. 設(shè)置AccessKey
5. 看幫助文檔,導(dǎo)入對(duì)應(yīng)的Util包
6.2 手機(jī)驗(yàn)證碼登入
6.2.1 整體思路整理
- 需求分析
- 涉及表的操作(數(shù)據(jù)模型)
- 代碼開發(fā)思路
想要判斷當(dāng)前手機(jī)號(hào)是否在表中,如果不在,說(shuō)明是新用戶,將改號(hào)碼保存 - 使用瀏覽器的手機(jī)模式查看H5的頁(yè)面
- 攔截器改造,現(xiàn)在加入移動(dòng)端的登入(session中是否有user,user是成功登入后設(shè)置的),設(shè)置threadLocal等信息。
- 后端思路:短信發(fā)送的controller處理完之后,把發(fā)送的驗(yàn)證碼存到session中。之后是登入的controller,把前端提交的短信驗(yàn)證碼和session中的驗(yàn)證碼進(jìn)行比較,如果手機(jī)號(hào)和驗(yàn)證碼都沒(méi)錯(cuò),那就放行(設(shè)置一個(gè)User的session,攔截器就會(huì)放行),同時(shí)查數(shù)據(jù)庫(kù),看看有沒(méi)有對(duì)應(yīng)的手機(jī)號(hào),沒(méi)有的話就插入這條手機(jī)號(hào)的數(shù)據(jù)
6.2.2 前端代碼分析
- login.html顯示到頁(yè)面上的代碼如下:
<div id="login" v-loading="loading">
<div class="divHead">登錄</div>
<div class="divContainer">
<el-input placeholder=" 請(qǐng)輸入手機(jī)號(hào)碼" v-model="form.phone" maxlength='20'/></el-input>
<div class="divSplit"></div>
<el-input placeholder=" 請(qǐng)輸入驗(yàn)證碼" v-model="form.code" maxlength='20'/></el-input>
<span @click='getCode'>獲取驗(yàn)證碼</span>
</div>
<div class="divMsg" v-if="msgFlag">手機(jī)號(hào)輸入不正確,請(qǐng)重新輸入</div>
<el-button type="primary" :class="{btnSubmit:1===1,btnNoPhone:!form.phone,btnPhone:form.phone}" @click="btnLogin">登錄</el-button>
</div>
- 獲取驗(yàn)證碼,注意發(fā)送請(qǐng)求的參數(shù)是sendMsgApi({phone:this.form.phone}),json的格式,后端使用注解@RequestParam接收,可以直接接收到一個(gè)對(duì)象中,屬性含有phone字段
getCode(){
this.form.code = ''
//正則表達(dá)式驗(yàn)證手機(jī)號(hào)是否正確
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}) 一個(gè)json,key是phone,value是前端輸入的手機(jī)號(hào)
this.form.code = (Math.random()*1000000).toFixed(0)
}else{
this.msgFlag = true
}
},
這里的msgFlag作用是對(duì)應(yīng)之前的:<div class="divMsg" v-if="msgFlag">手機(jī)號(hào)輸入不正確,請(qǐng)重新輸入</div>
,即已經(jīng)通過(guò)了驗(yàn)證,不需要彈出提示信息
正常情況下,通過(guò)sendMsgApi發(fā)送axios請(qǐng)求,請(qǐng)求到后端發(fā)送驗(yàn)證碼文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-438354.html
function sendMsgApi(data) {
return $axios({
'url': '/user/sendMsg',
'method': 'post',
data
})
}
- 登入模塊,前端只需要給后端傳loginApi(this.form),這個(gè)form:{phone:‘’“,code:‘’” } 是一個(gè)json,需要把phone和前端的code都傳給后端,后端之前設(shè)置了session:session.setAttribute(phone,code); 進(jìn)行一個(gè)驗(yàn)證就好了。
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:'請(qǐng)輸入手機(jī)號(hào)碼'});
}
}
6.2.3 后端代碼分析
整體思路:短信發(fā)送的controller處理完之后,把發(fā)送的驗(yàn)證碼存到session中。之后是登入的controller,把前端提交的短信驗(yàn)證碼和session中的驗(yàn)證碼進(jìn)行比較,如果手機(jī)號(hào)和驗(yàn)證碼都沒(méi)錯(cuò),那就放行(設(shè)置一個(gè)User的session,攔截器就會(huì)放行),同時(shí)查數(shù)據(jù)庫(kù),看看有沒(méi)有對(duì)應(yīng)的手機(jī)號(hào),沒(méi)有的話就插入這條手機(jī)號(hào)的數(shù)據(jù)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-438354.html
- 發(fā)送短信controller: 前端發(fā)送請(qǐng)求的參數(shù)是sendMsgApi({phone:this.form.phone}),json的格式,后端使用注解@RequestParam接收,可以直接接收到一個(gè)對(duì)象中,屬性含有phone字段
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session){
//獲取手機(jī)號(hào)
String phone = user.getPhone();
if(StringUtils.isNotEmpty(phone)){
//生成隨機(jī)的4位驗(yàn)證碼
String code = ValidateCodeUtils.generateValidateCode(4).toString();
log.info("code={}",code);
//調(diào)用阿里云提供的短信服務(wù)API完成發(fā)送短信
//SMSUtils.sendMessage("瑞吉外賣","",phone,code);
//需要將生成的驗(yàn)證碼保存到Session
session.setAttribute(phone,code);
return R.success("手機(jī)驗(yàn)證碼短信發(fā)送成功");
}
return R.error("短信發(fā)送失敗");
}
- 注意這里不能再使用User接收,主要是User中沒(méi)有code驗(yàn)證碼屬性,解決方法一是使用Dto繼承User,方法二是使用map接收。這里用方法二,因?yàn)閏ode驗(yàn)證碼只需要和session做驗(yàn)證就好了,沒(méi)有存到數(shù)據(jù)庫(kù)里的需求。
/**
* 登入驗(yàn)證
* @param map 前端傳過(guò)來(lái)電話和驗(yàn)證碼,User沒(méi)有驗(yàn)證碼的屬性,接不住,所以用map,也可以用Dto
* @param session 驗(yàn)證成功了,就把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)){
//用戶名秘密驗(yàn)證成功!
//先查電話號(hào)碼在不在數(shù)據(jù)庫(kù)里,如果不在,就要保存
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getPhone,phone);
User user = userService.getOne(lambdaQueryWrapper);
if (user == null){
//存入數(shù)據(jù)庫(kù)
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("登入失?。?);
}
- 寫完之后配置攔截器,有兩個(gè)類要配置
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("當(dāng)前路徑:{}", uri);
/**
* HandlerMethod=>Controller中標(biāo)注@RequestMapping的方法
* 需要配置靜態(tài)資源不攔截時(shí),添加這塊邏輯 => 前后端分離項(xiàng)目
*
*/
// 是我們的controller中的方法就攔截,如果不是的話,放行,給加載靜態(tài)資源
if (!(handler instanceof HandlerMethod)) {
log.info("是靜態(tài)資源或非controller中的方法,放行");
return true;
}
//1-通過(guò)session判斷是否登入
if (request.getSession().getAttribute(Contants.SESSIONLOGIN) != null) {
log.info("用戶已經(jīng)登入,id={}", request.getSession().getAttribute(Contants.SESSIONLOGIN));
//進(jìn)入攔截器后,給threadLocal綁定session,讓后面需要的公共字段自動(dòng)填充的時(shí)候,填充這個(gè)updateUser
//每次http請(qǐng)求,會(huì)分配一個(gè)新的線程來(lái)處理
BaseContext.setThreadLocal((Long) request.getSession().getAttribute(Contants.SESSIONLOGIN));
log.info("攔截器這里設(shè)置了ThreadLocal,值為:{}",(Long) request.getSession().getAttribute(Contants.SESSIONLOGIN));
log.info("當(dāng)前線程id={}",Thread.currentThread().getId());
return true;
} else if (request.getSession().getAttribute(Contants.SESSION_USERID) != null) {
log.info("用戶已經(jīng)登入,id={}", request.getSession().getAttribute(Contants.SESSION_USERID));
//進(jìn)入攔截器后,給threadLocal綁定session,讓后面需要的公共字段自動(dòng)填充的時(shí)候,填充這個(gè)updateUser
//每次http請(qǐng)求,會(huì)分配一個(gè)新的線程來(lái)處理
BaseContext.setThreadLocal((Long) request.getSession().getAttribute(Contants.SESSION_USERID));
log.info("攔截器這里設(shè)置了ThreadLocal,值為:{}",(Long) request.getSession().getAttribute(Contants.SESSION_USERID));
log.info("當(dāng)前線程id={}",Thread.currentThread().getId());
return true;
}
//這里應(yīng)該跳轉(zhuǎn)到登入頁(yè)面,如何做?
//5、如果未登錄則返回未登錄結(jié)果,通過(guò)輸出流方式向客戶端頁(yè)面響應(yīng)數(shù)據(jù)
log.info("用戶未登入,通過(guò)輸出流方式向客戶端頁(yè)面響應(yīng)數(shù)據(jù),打回登入頁(yè)面");
response.getWriter().write(JSON.toJSONString(RetObj.error("NOTLOGIN")));//與前端request.js中的代碼呼應(yīng)
return false;
}
package cn.edu.uestc.ruijitakeout.common.config;
@Slf4j
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 拓展消息轉(zhuǎn)換器
* @param converters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
log.info("拓展消息轉(zhuǎn)換器成功加載");
//創(chuàng)建消息轉(zhuǎn)換器對(duì)象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//設(shè)置對(duì)象轉(zhuǎn)換器,底層使用Jackson將Java對(duì)象轉(zhuǎn)為json
messageConverter.setObjectMapper(new JacksonObjectMapper());
//將上面的消息轉(zhuǎn)換器對(duì)象追加到mvc框架的轉(zhuǎn)換器集合中
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();
}
}
到了這里,關(guān)于springboot項(xiàng)目:瑞吉外賣 前后端 代碼、思路 詳細(xì)分析 part5的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!