赞
踩
集群的session共享问题:多台tomcat并不共享session存储空间,当请求切换到不同tomcat服务时导致数据丢失的问题。
session的替代方案应该满足:
使用redis代替session:
生成验证码之后,以手机号为key,验证码为value,类型为string存入redis。
- @Resource
- private StringRedisTemplate stringRedisTemplate;
-
- public Result sendcode(String phone, HttpSession session){
- //1.判断手机是否正确
- if (RegexUtils.isPhoneInvalid(phone)) {
- //验证失败,返回错误信息
- return Result.fail("手机格式输入错误!");
- }
- //2.生成验证码
- String code = RandomUtil.randomNumbers(6);
- //3.把验证码保存到redis,设置短信验证码有效期
- stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
-
- //4.发送验证码
- log.debug("发送短信验证码成功,验证码{}",code);
- return Result.ok();
- }
以手机号码为key去redis读取验证码,校验是否一致,如果一致以随机生成的token为key,用户信息以hash类型为value存入redis数据库,并返回给客户端(一般是浏览器)
- @Override
- public Result login(LoginFormDTO loginForm, HttpSession session) {
- //1.校验手机
- String phone = loginForm.getPhone();
- if (RegexUtils.isPhoneInvalid(phone)) {
- //验证失败,返回错误信息
- return Result.fail("手机格式输入错误!");
- }
- //TODO 2.从redis获取验证码并校验验证码
- String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
- if (cacheCode == null || !cacheCode.equals(loginForm.getCode())){
- return Result.fail("验证码错误请重试!");
- }
- // 3.查询手机是否在数据库里
- User user = query().eq("phone", phone).one();
- //4.如果不在就把该用户注册进数据库
- if (user == null){
- user = creatUserByPhone(phone);
-
- }
- // 5.把用户放入redis
- //5.1随机生成token,作为登录令牌
- String token = UUID.fastUUID().toString(true);
- // 5.2将user对象转换为Hashmap存储
- UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
- Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(), CopyOptions.create().
- setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue) -> fieldValue.toString()));
- // 5.3存储到redis
- stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token,userMap);
- //5.4设置token有效期
- stringRedisTemplate.expire(LOGIN_USER_KEY+token,LOGIN_USER_TTL,TimeUnit.MINUTES);
-
- // 6返回token给客户端
- return Result.ok(token);
-
-
- }
因为在短信验证码登陆注册功能中把用户的token保存在了客户端(一般是浏览器),那么用户每次访问都会携带这个token,就可以根据这个token去redis里面查询用户信息。
一开始的拦截器设置是要用户有操作才会刷新用户的token保存时间,优化拦截器之后无论访问的是不是需要用户登录的页面都可以刷新token有效期
- public class RefreshTokenInterceptor implements HandlerInterceptor {
-
- /*
- 因为这个类RefreshTokenInterceptor对象是我们自己在需要使用到它的时候手动New出来的,
- 不是由spring创建的,所以没办法用@Resource/@Autowired这些注解来注入StringRedisTemplate属性
- 那谁来注入这个StringRedisTemplate呢?就看是谁用到了RefreshTokenInterceptor类的对象,
- 就在用到的那里注入那谁来注入这个StringRedisTemplate,比如这里是在MvcConfig类中
- */
- private StringRedisTemplate stringRedisTemplate;
-
- public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
- this.stringRedisTemplate = stringRedisTemplate;
- }
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- //获取请求头中的token,根据前端代码中定义的变量名来取
- String token = request.getHeader("authorization");
- //判断token是否为空
- if (StrUtil.isBlank(token)) {
-
- return true;
-
- }
- //基于token获取redis用户
- Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);
- //判断用户是否存在
- //如果不存在
- if (userMap.isEmpty()) {
-
- return true;
- }
- //将查询到的Hash数据转化为UserDTO对象
- UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
- //用户存在,保存到threadlocal
- UserHolder.saveUser(userDTO);
- //刷新token有效期
- stringRedisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);
- return true;
- }
-
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- //移除用户
- UserHolder.removeUser();
- }
- }
- public class LoginInterceptor implements HandlerInterceptor {
-
-
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- //判断是否需要拦截(ThreadLocal中是否有用户)
- if(UserHolder.getUser() == null){
- response.setStatus(401);
- return false;
- }
- return true;
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。