黑马点评项目总结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();
    }
}
相关推荐
minihuabei4 小时前
linux centos 安装redis
linux·redis·centos
monkey_meng6 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
想要打 Acm 的小周同学呀7 小时前
LRU缓存算法
java·算法·缓存
hlsd#7 小时前
go 集成go-redis 缓存操作
redis·缓存·golang
镰刀出海7 小时前
Recyclerview缓存原理
java·开发语言·缓存·recyclerview·android面试
奶糖趣多多9 小时前
Redis知识点
数据库·redis·缓存
CoderIsArt10 小时前
Redis的三种模式:主从模式,哨兵与集群模式
数据库·redis·缓存
ketil2714 小时前
Redis - String 字符串
数据库·redis·缓存
王佑辉16 小时前
【redis】延迟双删策略
redis
生命几十年3万天16 小时前
redis时间优化
数据库·redis·缓存