SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则

  1. 基础+实战的Demo和Coding上传到我的代码仓库
  2. 在原有基础上加入一些设计模式,stream+lamdba等新的糖
  3. 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式

代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Session-链接: RedisProjectDemo
Redis优化-链接: RedisProject

需求:基于Session实现短信验证登录

本项目是一个前后端分离项目,系统框架和展示如下
在这里插入图片描述
在这里插入图片描述
我们希望在登录可以通过短信验证方式进行注册/登录

基于Session的短信登录

在这里插入图片描述

我们获得前端参数,实现登录验证功能

我们先给出架构图

在这里插入图片描述

发送手机验证码

    @PostMapping("code")
    public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
        // TODO 发送短信验证码并保存验证码
//        return Result.fail("功能未完成");
        return userService.sendCode(phone, session);
    }
public interface IUserService extends IService<User> {

    Result sendCode(String phone, HttpSession session);

    Result login(LoginFormDTO loginForm, HttpSession session);
}

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result sendCode(String phone, HttpSession session) {
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号码格式错误!");
        }
        String code = RandomUtil.randomNumbers(6);
		//模拟发短信,将验证码放到session
       session.setAttribute("code", code);

        log.debug("发送短信验证码成功: "+ code);
        return Result.ok();
    }

实现登录 (注意MyBatisP的接口使用)

在这里插入图片描述

  1. @RequestBody是为了封装信息,实现自动配置
  2. @RequestParam对应了前端传来的参数对应
    /**
     * 登录功能
     * @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码
     */
    @PostMapping("/login")
    public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
        // TODO 实现登录功能
//        return Result.fail("功能未完成");
        return userService.login(loginForm, session);
    }
public interface IUserService extends IService<User> {

    Result sendCode(String phone, HttpSession session);

    Result login(LoginFormDTO loginForm, HttpSession session);
}

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
		//从前端拿到对应的手机号
        String phone = loginForm.getPhone();
        if ((RegexUtils.isPhoneInvalid(phone))) {
            return Result.fail("手机格式错误!");
        }


        String code = loginForm.getCode();
        //获取对应的code
		 Object cacheCode = session.getAttribute("code");

        if (cacheCode == null || !cacheCode.toString().equals(code)) {
            return Result.fail("验证码错误!");
        }
		
		// 这里是MyBatisP的命令
        User user = query().eq("phone", phone).one();
		
		//如果没有就去创建一个User
        if(user==null){
            user = createUserWithPhone(phone);
        }
		//将user通过BeanUtil工具类封装到DTO对象
        session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));

        return Result.ok();
    }

将user通过BeanUtil工具类封装到DTO对象,可以大大减少不必要数据的传输
所以设置了DTO层封装数据处理

    private User createUserWithPhone(String phone) {
        User user = new User();
        user.setPhone(phone);
        
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(8));
        save(user);
        return user;
    }
  1. 通过ThreadLocal存放当前对象,实现全局获取效果;
public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

    public static void saveUser(UserDTO user){
        tl.set(user);
    }

    public static UserDTO getUser(){
        return tl.get();
    }

    public static void removeUser(){
        tl.remove();
    }
}

  1. 实现拦截器进行登录验证
    因为要对 限制的资源进行验证,所以使用了拦截器来进行处理;
    在这里插入图片描述
public class LoginInterceptor1 implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public LoginInterceptor1(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("user");

        if (user == null) {
            response.setStatus(401);
            return false;
        }

        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        
        UserHolder.saveUser(userDTO);
        return true;

    }


    @Override
    public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception
    ex) throws Exception {
//保存到当前线程中,直接remove即可
        UserHolder.removeUser();
        System.out.println("123");
    }


}

这里的构造注入是因为
private StringRedisTemplate stringRedisTemplate; public LoginInterceptor1(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate = stringRedisTemplate; } 这里的LoginInterceptor是通过new来实现的,没有配置到Bean工厂中去,所以通过这种方式实现
后面的配置类中会进行注入

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
	            registry.addInterceptor(new LoginInterceptor())
                    .excludePathPatterns(
                            "/shop/**",
                            "voucher/**",
                            "shop-type/**",
                            "upload/**",
                            "/blog/hot",
                            "/user/code",
                            "/user/login"
                    );
	}
}

这里是为了拦截请求,做限制处理

在这里插入图片描述

在这里插入图片描述

新的问题

在这里插入图片描述

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐