苍穹外卖先写到这里了,剩下的部分先不学了,感兴趣的可以自行去看一下什么的,好累,纠结了半天最终还是决定做黑马点评,每天学的我想紫砂,烂大街就烂大街吧呜呜呜。
黑马点评实战
超级全的笔记,而且博主还会很温柔耐心的帮人解决问题:
看图按步骤写下去,过关斩将,正难则反,就会越写越顺畅了。
然而只是简单那么一会儿,今晚学的还是难的,笔记全写代码里面了。
短信登录部分
@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);
}
}