基于session实现登录
1.发送短信验证码
@Override public Result sendCode(String phone, HttpSession session) { //1.校验手机号是否合规 if (RegexUtils.isPhoneInvalid(phone)) { //2.不合规直接返回 错误信息 return Result.fail("手机号错误"); } //3.如果合规生成验证码 String code = RandomUtil.randomString(4); //4.将验证码保存到session中 session.setAttribute("code",code); //5.发送验证码 log.info("code: {}",code); //返回ok return Result.ok(); }
ServletContext:上下文对象,在服务器启动时被创建,关闭时被注销,被所有Servlet共享,可在web.xml中进行配置,存放一些初始化数据,拥有最长的生命周期。
HttpSession:会话对象,浏览器请求服务器时被创建,关闭浏览器窗口或页面不刷新过期时被销毁,拥有较长的生命周期。
ServletRequest:请求对象,浏览器每次发送请求时被创建,响应结束之后被销毁,用于存放来自页面的参数和浏览器信息,生命周期最短。
2.短信验证、登录、注册功能
@Override public 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(session==null || !cacheCode.toString().equals(code)){ //4.不一致直接报错 return Result.fail("错误信息"); } //5.根据手机号查询用户 LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>(); query.eq(User::getPhone,loginForm.getPhone()); User user = this.getOne(query); if(user==null){ //6.不存在直接创建新用户保存到数据库中 user=createUserWithPhone(loginForm.getPhone()); } //7.最终将用户信息保存到session中 session.setAttribute("user",user); return Result.ok(); } private User createUserWithPhone(String phone) { User user = new User(); user.setPhone(phone); user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(6)); this.save(user); return user; }
3.登录校验功能
创建拦截器
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"); if(user==null){ //3.如果不存在直接拦截 返回401状态 response.setStatus(401); return false; } //4.存在 保存到ThreadLocal中实现共享 UserHolder.saveUser((User) user); //5.放行 return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //避免造成内存泄露 UserHolder.removeUser(); } }
注册拦截器
//注册拦截器 及其相关配置 @Configuration public class MvcConfig implements WebMvcConfigurer { //添加拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()).excludePathPatterns( "/shop/**", "/voucher/**", "/shop-type/**", "/upload/**", "/blog/hot", "/user/code", "/user/login" ); } }
public class UserHolder { private static final ThreadLocal<User> tl = new ThreadLocal<>(); public static void saveUser(User user){ tl.set(user); } public static User getUser(){ return tl.get(); } public static void removeUser(){ tl.remove(); } }
ThreadLocal叫做线程变量,意思是ThreadLocal中*填充的变量*属于*当前线程*,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。