🎥项目源码地址
链接: https://pan.baidu.com/s/1LkJDNwV5THPoywbEX6Gpyg
提取码: gajm
🍎 1.验证码登录所需要的功能接口
思路 : 这个我们需要两个接口来实现, 分别是发送验证码和登录功能
代码 :
/**
* 发送手机验证码
*/
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
// TODO 发送短信验证码并保存验证码
return userService.sendCode(phone, session);
}
/**
* 登录功能
* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
*/
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
// TODO 实现登录功能
return userService.login(loginForm, session);
}
🍊2. 发送验证码业务实现
思路 : 校验手机号(正则) ➡️ 生成验证码 ➡️ 将验证码保存到Session中 ➡️ 返回OK
代码 :
@Override
public Result sendCode(String phone, HttpSession session) {
//1.校验(正则)手机号
if (RegexUtils.isPhoneInvalid(phone)) {
//2.如果不符合, 返回错误信息
return Result.fail("手机号格式错误!");
}
//3.符合, 生成验证码
String code = RandomUtil.randomNumbers(6);
//4.保存验证码到session中
session.setAttribute("code", code);
//5.发送验证码 后期有空调阿里云的api
log.debug("手机号:{} 发送短信验证码成功, 验证码: {}", phone, code);
//6.返回OK
return Result.ok();
}
🍅3. 登录功能
思路 : 校验手机号(正则) ➡️ 校验验证码 ➡️ 根据手机号查询用户 ➡️ 如果不存在就创建用户 ➡️ 保存用户信息到Session中
代码 :
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
//1.校验手机号
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
//2.如果不符合, 返回错误信息
return Result.fail("手机号格式错误!");
}
Enumeration<String> attributeNames = session.getAttributeNames();
System.out.println(attributeNames);
//3.校验验证码
Object cacheCode = session.getAttribute("code");
String code = loginForm.getCode();
if (cacheCode == null || !cacheCode.toString().equals(code)){
//4.不一致,报错
return Result.fail("验证码错误");
}
//5.一致的话, 根据手机号查询用户
User user = query().eq("phone", phone).one();
//6.判断用户是否存在
if (user == null) {
//不存在, 创建新用户
user = createUserWithPhone(phone);
}
//7.保存用户信息到session中
session.setAttribute("user", user);
return Result.ok();
}
🍖4. 拦截器
思路 : 利用SpringMVC的拦截器, 来拦截请求, 首先我们要制定拦截的规则, 最后再设置接口的拦截或放行
代码1 : 设置拦截规则
public class LoginInterceptor implements HandlerInterceptor {
@Override
//前置拦截
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.获取session
HttpSession session = request.getSession();
//2.获取session中的用户
Object user = session.getAttribute("user");
//判断用户是否存在, 如果请求的接口没有被放行, 但是session中是存在用户信息, 该接口也可以被访问到
if (user == null){
//4.不存在, 拦截返回401状态码
response.setStatus(401);
return false;
}
//存在,保存用户信息到ThreadLocal
UserHolder.saveUser((User)user);
//6.放行
return true;
}
@Override
//Controller执行之后拦截
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
//视图渲染之后
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//移除用户
UserHolder.removeUser();
}
}
代码2 : 设置要放行的接口
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
//要方行的接口,
.excludePathPatterns(
"/user/code",
"/user/login"
);
}
}
🌐 5.集群Session共享问题
session共享问题:多台Tomcat并不共享session存储空间,当请求切换到不同tomcat服务时导致数据丢失的问题。 session的替代方案应该满足:
数据共享
内存存储 key、
value结构