大众点评项目 基于Session的短信登录
- 需求:基于Redis实现短信验证登录
- 基于Redis的短信登录实战
- 优化为Redis login方法
- 配置拦截器实现双重验证
- 配置类实现异步/排序 拦截
- 结果展示
SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则
- 基础+实战的Demo和Coding上传到我的代码仓库
- 在原有基础上加入一些设计模式,stream+lamdba等新的糖
- 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式
代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Session-链接: RedisProjectDemo
Redis优化-链接: RedisProject
需求:基于Redis实现短信验证登录
当出现了Tomcat集群如何处理session?
基于Redis的短信登录实战
优化为Redis login方法
- 通过Redis来存放验证码
- 使用UUID实现token作为key,获取对应的User信息
- 注意BeanUtil.beanToMap的API使用
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
String phone = loginForm.getPhone();
if ((RegexUtils.isPhoneInvalid(phone))) {
return Result.fail("手机格式错误!");
}
//通过Redis来存放验证码
String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
String code = loginForm.getCode();
// Object cacheCode = session.getAttribute("code");
if (cacheCode == null || !cacheCode.toString().equals(code)) {
return Result.fail("验证码错误!");
}
User user = query().eq("phone", phone).one();
if(user==null){
user = createUserWithPhone(phone);
}
// session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
String token = UUID.randomUUID().toString(true);
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
Map<String, Object> stringObjectMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
CopyOptions.create().setIgnoreNullValue(true)
.setFieldValueEditor((fieldName, fieldValue)->{
return fieldValue.toString();
}));
// String tokenKey = LOGIN_USER_KEY + token;
String tokenKey = LOGIN_USER_KEY + "1" ;
//使用UUID实现token作为key,获取对应的User信息
stringRedisTemplate.opsForHash().putAll(tokenKey, stringObjectMap);
stringRedisTemplate.expire(token, LOGIN_USER_TTL, TimeUnit.SECONDS);
return Result.ok();
}
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;
}
配置拦截器实现双重验证
- 设定两层优化器,第一层作为登录验证,获取ThreadLocal中的信息,不断更新Redis
- 第二层为了拦截部分资源信息
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 {
if (UserHolder.getUser() == null) {
response.setStatus(401);
return false;
}
return true;
}
}
public class RefreshInterceptor implements HandlerInterceptor {
private StringRedisTemplate stringRedisTemplate;
public RefreshInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// String token = request.getHeader("authorization");
String token = "1";
if (StrUtil.isBlank(token)) {
return true;
}
/* UserDTO userDTO = (UserDTO) request.getSession().getAttribute("user");
if (userDTO==null) {
response.setStatus(401);
return false;
}*/
String key = LOGIN_USER_KEY + token;
Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
if (userMap == null) {
return true;
}
UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
UserHolder.saveUser(userDTO);
stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.SECONDS);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
System.out.println("123");
}
}
配置类实现异步/排序 拦截
通过两种方式实现顺序 拦截器
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// TODO 通过异步请求实现
/* CompletableFuture<Object> objectCompletableFuture = CompletableFuture.supplyAsync(() -> {
InterceptorRegistration interceptorRegistration = registry.addInterceptor(
new RefreshInterceptor(stringRedisTemplate))
.addPathPatterns("/**");
System.out.println("第一步拦截");
return interceptorRegistration;
});
objectCompletableFuture.thenAccept((result)->{
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/shop/**",
"voucher/**",
"shop-type/**",
"upload/**",
"/blog/hot",
"/user/code",
"/user/login"
);
System.out.println("第二步");
});*/
registry.addInterceptor(
new RefreshInterceptor(stringRedisTemplate))
.addPathPatterns("/**").order(0);
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/shop/**",
"voucher/**",
"shop-type/**",
"upload/**",
"/blog/hot",
"/user/code",
"/user/login"
).order(1);
}
}
结果展示