栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【基于redis实现共享session登录】-Redis学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【基于redis实现共享session登录】-Redis学习笔记

前言

学习要善于做笔记,学完长时间不用,学会的知识又被遗忘了,本文是学习张老师redis课程记录,感兴趣的小伙伴可以去B站看原视频,本章节主要实现redis代替session实现登录功能

主要流程图


登录后,后端返回token给前端,前端拿到token后,将token存储到sessionStorage中,在前端添加拦截器,对所有后端请求添加请求头,将token携带到后端验证登录信息。

代码实现 pom文件

        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.apache.commons
            commons-pool2
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            mysql
            mysql-connector-java
            runtime
            5.1.47
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.3
        
        
        
            cn.hutool
            hutool-all
            5.7.17
        
        
        
            com.github.qcloudsms
            qcloudsms
            1.0.6
        
    
发送短信验证码,并将验证码存储到redis中
public Result sendCode(String phone, HttpSession session) {
        //校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式错误!");
        }
        //生成验证码
        String code = RandomUtil.randomNumbers(6);
        //保存数据到redis
        stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY + phone,
                code, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);

        //发送验证码,APPID,APPKEY,TEMPLATEID,SMSSING这个四个值都是申请腾讯云控制台上获取,获取路径见下图
        try {
            String[] params = {code, Long.toString(RedisConstants.LOGIN_CODE_TTL)};
            SmsSingleSender sender = new SmsSingleSender(RedisConstants.APPID, RedisConstants.APPKEY);
            SmsSingleSenderResult result = sender.sendWithParam("86", phone, RedisConstants.TEMPLATEID, params,
                    RedisConstants.SMSSING, "", "");

        } catch (HTTPException e) {
            e.getStackTrace();
        } catch (IOException e) {
            e.getStackTrace();
        }
        log.debug("发送验证码成功,验证码:{}", code);
        return Result.ok();
    }

登录功能

用户信息存储到redis有效期设置的为30分钟。

public Result log(LoginFormDTO loginForm, HttpSession session) {
        //获取手机号
        String phone = loginForm.getPhone();
        //校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            return Result.fail("手机号格式错误!");
        }
        //根据手机号查询用户信息
        User user = query().eq("phone", phone).one();

        //判断用户是否存在
        if (user == null) {
            //不存在,创建用户并保存
            createUserWithphone(phone);
        }
        //生成随机的token,作为登录的令牌,不带‘-’
        String token = UUID.randomUUID().toString(true);
        //将User转换成hashmap存储,此处使用的是hutool自动转换工具
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);

        Map userMap1 = BeanUtil.beanToMap(userDTO);
        //将User转换成hashmap存储,此处使用的是hutool自动转换换成map,
        // setIgnoreNullValue(true)忽略对象中为空的值。
        // setFieldValueEditor((a, b) -> b.toString()))将传入的userDTO对象中的值转换成String类型
        Map userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true)
                        .setFieldValueEditor((a, b) -> b.toString()));
        String tokenkey = RedisConstants.LOGIN_USER_KEY + token;
        //将用户信息以token为键存入redis
        stringRedisTemplate.opsForHash().putAll(tokenkey, userMap);
        //设置token有效期为30分钟
        stringRedisTemplate.expire(tokenkey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        //返回token
        return Result.ok(token);
    }
第一个拦截器,拦截所有请求
package com.hmdp.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;


public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //获取请求头中的token
        String token = request.getHeader("auth");
        //如果token为空直接放行
        if (StrUtil.isBlank(token)) {
            return true;
        }
        String tokenkey = RedisConstants.LOGIN_CODE_KEY + token;
        //根据token获取用户信息
        Map userMap = stringRedisTemplate.opsForHash().entries(tokenkey);
        //判断用户是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        //将map转换成userDto对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //保存用户信息导ThreadLocal
        UserHolder.saveUser(userDTO);
        //刷新token有效期
        stringRedisTemplate.expire(tokenkey, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
        //放行
        return true;
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
        //移除用户
        UserHolder.removeUser();
    }
}

第二个拦截器
package com.hmdp.utils;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        //判断是否需要拦截(ThreadLocal中是否有用户)
        if (UserHolder.getUser()==null){
            response.setStatus(401);
            //拦截
            return false;
        }
        //放行
        return true;
    }

}

配置拦截的路径,及拦截器执行顺序
package com.hmdp.config;

import com.hmdp.utils.LoginInterceptor;
import com.hmdp.utils.RefreshTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;


@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public void addInterceptors(InterceptorRegistry registry) {
        //第二个登录拦截器
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        "/user/login",
                        "user/code",
                        "upload/**"
                ).order(1); // order 值越大执行的优先级越低
        //第一个刷新token的拦截器,/** 拦截所有路径
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
    }
}

该实现是通过拦截器来实现登录有效期的刷新,如使用的用户较固定,如公司内部使用系统入OA等,登录信息也可不设置有效期,每天晚上通过定时任务来清理redis中的登录信息,具体清理时间也可根据业务要求来。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/970097.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号