黑马点评项目总结1-使用Session发送验证码和登录login和 使用Redis存储验证码和Redis的token登录

黑马先是总结了从session实现登录,然后是因为如果使用了集群方式的服务器的话,存在集群共享session互相拷贝效率低下的问题,接着引出了速度更快的内存型的kv数据库Redis,

使用Session发送验证码和登录login

举个例子:

原来的发送验证码和登录的例子,直接在session中存验证码6

java 复制代码
    @Override
    public Result sendCode(String phone, HttpSession session) {
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        //我这里瞎勾八写的验证码是6,图省事
        session.setAttribute("code","6");

        return Result.ok();
    }

从session中获取验证码,然后与从表单输入的验证码相比较,如果一致,那么就是验证码正确,query()方法是查询tb_user中的用户,利用phone字段查询用户,然后如果查询出来用户那么就利用这个电话字段创建用户user对象,如果没查出来,就自动创建新的用户,然后存在UserHolder里面,UserHolder是用静态ThreadLocal存储的User对象。

java 复制代码
    @Override public Result login(LoginFormDTO loginFormDTO, HttpSession session) {
        String phone = loginFormDTO.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        String code = (String) session.getAttribute("code");
        if(code == null || !code.equals("6")){
            return Result.fail("验证码错误");
        }
        User user = query().eq("phone", phone).one();
        if(user == null){
            user = createUser(phone);
        }
        session.setAttribute("user",user);
        UserHolder.saveUser(user);
        return Result.ok();

    }

接着如果登录的话,前端界面用户的界面是访问/user/me来返回用户信息,注意这里的me函数一定要返回user,否则黑马点评的界面会又再次跳转到登录界面,非常✓8。黑马点评项目登录有好几次都是因为这个UserHolder的UserDTO为空导致又跳转到登录界面,非常✓8。

java 复制代码
    @GetMapping("/me")
    public Result me(){
        // TODO 获取当前登录的用户并返回
        UserDTO user = UserHolder.getUser();
        log.debug("me:{}", user);
        return Result.ok(user);
    }

使用Redis存储验证码和Redis的token登录

UserServicelmpl.java,注意,我们使用Redis返回token的时候,Key是token,存储的是UserMap,UserMap是存储了UserDTO信息的,UserDTO存储了用户信息,包括他的phone,因此,token是和电话号码存在一一对应关系的!!我们后续拦截器拦截的时候,获取了Token之后,利用获取到的Token去Redis里面查询的时候就知道是哪个用户了!!!

java 复制代码
@Service
@Slf4j
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("手机号格式错误");
        }
        //使用了Redis存储验证码,Key是LOGIN_CODE_KEY,value是6
        stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY,"6",RedisConstants.LOGIN_CODE_TTL,
                TimeUnit.MINUTES);
        log.debug("发送验证码成功");
        return Result.ok();
    }
    @Override public Result login(LoginFormDTO loginFormDTO, HttpSession session) {
        String phone = loginFormDTO.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        //从内存式的Redis数据库中获取验证码,Key是LOGIN_CODE_KEY,value是6
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY );
        String code = loginFormDTO.getCode();
        if(code == null || !code.equals(cacheCode)){
            return Result.fail("验证码错误");
        }
        User user = query().eq("phone", phone).one();
        if(user == null){
            user = createUser(phone);
        }
        
        String token = UUID.randomUUID().toString(true);
		//不存储User,只存储UserDTO,减轻存储压力,避免存储敏感信息
        UserDTO userDTO= BeanUtil.copyProperties(user,UserDTO.class);
		//把UserDTO信息转化为HashMap
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        String tokenKey = LOGIN_USER_KEY + token;
        //把登录的这个用户以hashMap方式存储到Redis之中,token为Key,取出来的Value才是UserDTO
        stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);
        //设置用户的登录过期时间,防止Redis存储太多用户信息导致内存占用很多
        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);
        UserHolder.saveUser(userDTO);
//        返回token
        return Result.ok(token);

    }

    private User createUser(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName("user"+ RandomUtil.randomString(10));
        return user;
    }
}

拦截器设置:

拦截器前端

拦截器后端

LoginInterceptor.java

UserServicelmpl.java的@Override public Result login(LoginFormDTO loginFormDTO, HttpSession session)函数return Result.ok(token);直接返回了token到前端界面,前端界面再把这个token存储到请求头的'authorization'里面。

java 复制代码
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    private StringRedisTemplate stringRedisTemplate;
    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override // 拦截请求
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("authorization");//根据authorization获取token
        if (StrUtil.isBlank(token)) {
            response.setStatus(401);
            return false;
        }
        //根据token从Redis里面获取UserMap
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);
        //把UserMap从HashMap形式转化为Java Bean。
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //存储到UserHolder的ThreadLocal里面
        UserHolder.saveUser(userDTO);
        //刷新过期时间
        stringRedisTemplate.expire(LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 放行
        return true;
    }
    @Override // 拦截响应
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用户
        UserHolder.removeUser();
    }
}
相关推荐
闪电悠米2 小时前
黑马点评-Redis 消息队列-03_stream_consumer_group
开发语言·数据库·redis·分布式·缓存·junit·lua
qqxhb3 小时前
47|成本与性能:缓存、批处理、模型路由与降级
缓存·批处理·智能模型路由·多级降级预案·成本预算
佛祖让我来巡山5 小时前
线上 Redis 突然“爆”了,怎么办?
redis·redis宕机·redis崩了·redis线上事故
三十..6 小时前
Redis 核心原理与高可用架构实践
运维·数据库·redis
叶小鸡8 小时前
Java 篇-项目实战-AI 天机学堂(从 0 到 1)-day5
数据库·redis·缓存
大模型最新论文速读8 小时前
小红书提出 RedKnot:分头处理 kv 缓存,延时降低 60%效果还提升
论文阅读·人工智能·深度学习·机器学习·缓存·自然语言处理
IT策士8 小时前
Redis 从入门到精通:Python 操作 Redis
redis·python·bootstrap
周杰伦的稻香9 小时前
Go + Redis:本地部署高性能图片主色调提取服务
开发语言·redis·golang
大囚长10 小时前
大模型API的上下文缓存(Contextual Cache)
人工智能·缓存
小二·10 小时前
Redis 7 分布式缓存架构实战
redis·分布式·缓存