《黑马点评》 短信登录部分

342 阅读4分钟

苍穹外卖先写到这里了,剩下的部分先不学了,感兴趣的可以自行去看一下什么的,好累,纠结了半天最终还是决定做黑马点评,每天学的我想紫砂,烂大街就烂大街吧呜呜呜。

gitee.com/kuguihjufg/…

黑马点评实战

超级全的笔记,而且博主还会很温柔耐心的帮人解决问题:

blog.csdn.net/RuanFun/art…

看图按步骤写下去,过关斩将,正难则反,就会越写越顺畅了。

然而只是简单那么一会儿,今晚学的还是难的,笔记全写代码里面了。

短信登录部分

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

    @Autowired
    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);
        //保存验证码到redis
        //一定要设置有效期 就相当于redis中的set key value ex 120
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+ phone,code,2, TimeUnit.MINUTES);
        //发送验证码
        log.debug("发送验证码成功,验证码: {}", code);
        //返回ok
        return Result.ok();
    }

    //每一次都需要校验验证码
    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        String phone = loginForm.getPhone();
        //校验手机
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        //TODO 3. 从redis获取验证码并校验
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();
        if(cacheCode==null || !cacheCode.toString().equals(code)){
            //不一致,报错
            return Result.fail("验证码错误");
        }
        //一致 根据手机号查询用户
        //mybatis plus     query就相当于select * from tb_user
        User user = query().eq("phone", phone).one();
        //判断用户是否存在

        if(user == null){
            //不存在 创建新用户保存
            user = createUserWithPhone(phone);
        }

        //TODO 7.保存用户信息到redis
        //TODO 7.1随机生成token 作为登录令牌
        String token = UUID.randomUUID().toString(true);
        //TODO 7.2将User对象转为Hash存储
        UserDTO userDTO = BeanUtil.copyProperties(user,UserDTO.class);
        //这里是拿来解决报错java.lang.Long cannot be cast to java.lang.String的,
        //对字段的值做修改
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        .setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        //TODO 7.3存储
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+ token,userMap);
        //然后要给这个令牌设置一个有效期
        stringRedisTemplate.expire(token,LOGIN_USER_TTL,TimeUnit.MINUTES);
        //然后还要加一个更新token有效期的逻辑
        //TODO 8 返回 token
        return Result.ok(token);
    }

    private User createUserWithPhone(String phone) {
        //创建用户
        User user = new User();
        user.setPhone(phone);
        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));

        //保存用户
        save(user);
        return user;
    }
}

配置登录拦截器

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 {
//        //获取请求头中的token  request.getHeader
//        //这里将可变字符串转变为字符串 不知道对不对
//        String token = request.getHeader("authrization");
//        if (StrUtil.isBlank(token)){
//            //不存在 拦截 返回401状态
//            response.setStatus(401);
//            return false;
//        }
//        //基于token获取redis的用户 用的哈希
//        String key = RedisConstants.LOGIN_USER_KEY + token;
//
//        Map<Object,Object> userMap = stringRedisTemplate.opsForHash()
//                .entries(key);
//
//        //判断用户是否存在
//        if(userMap.isEmpty()){
//            //不存在 拦截 返回401状态码
//            response.setStatus(401);
//            return false;
//        }
//
//        //将查询到的Hash数据转为UserDTD
//        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(),false);
//
//        //存在,保存用户信息的ThreadLocal
//        UserHolder.saveUser(userDTO);
//
//        //刷新token有效期
//        stringRedisTemplate.expire(key,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
//        //放行
//        return true;
        /**
         * 现在只有在用户访问拦截器拦截的页面才会刷新页面,假如用户访问的是不需要拦截的页面则不会导致页面的刷新。
         * 现在的解决思路是:新增一个拦截器,拦截一切路径。
         */
        //1.判断是否需要拦截(ThreadLocal中是否有用户)
        if (UserHolder.getUser() == null) {
            //没有,需要拦截,设置状态码
            response.setStatus(401);
            //拦截
            return false;
        }
        //放行
        return true;
    }

    //就去销毁业务的信息
    //后置拦截器
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户
        UserHolder.removeUser();
    }
}

复制LoginInterceptor变成一份新的RefreshTokenInterceptor,把下面几处地方改为return true即可:

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    /**
     * 通过addInterceptors方法来添加拦截器,registry是拦截器的注册器。
     * 用.excludePathPatterns来排除不需要拦截的路径。在这里code、login、
     * bloghot、shop、shopType、upload和voucher等都不需要拦截。
     */
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))
                .excludePathPatterns(//这里是可变排除
                        "/user/code",
                        "/user/login",
                        "/upload/**",
                        "/blog/hot",
                        "/shop/**",
                        "/shop-type/**",
                        "/voucher/**"
                        //带**的是全删
                ).order(1);//设置拦截器优先级 值越大优先级越低 值越小优先级越高 确保下面拦截器先执行
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
    }
}