• token登录的实现


    token登录的实现

    我这种token只是简单的实现token,就是后端利用UUID 生成简单随机码,利用随机码作为在Redis中的键,然后存储的用户信息作为值,在每次合理请求的时候对token的有效时间进行刷新(利用拦截器),以确保用户信息的有效性。

    为什么要用token

    使用令牌(Token)进行身份验证和授权是一种常见的方式,特别适用于分布式和跨域请求的应用程序。

    1. 为什么要使用 Token:

    • 无状态性:Token 身份验证是无状态的,不需要在服务器端存储会话信息。每个请求都包含了身份信息,因此服务器不需要维护状态。
    • 跨域支持:Token 可以轻松处理跨域请求,因为令牌可以在 HTTP 请求的头部(通常是 Authorization 头部)中传递,而不受同源策略的限制。
    • 扩展性:Token 可以包含任意信息,因此可以用于传递用户权限、角色、过期时间等信息。

    2. Token 使用逻辑:

    • 用户登录成功后,生成一个 Token 并将用户信息存储在服务器端(如 Redis)以便快速验证和获取用户信息。
    • 向前端返回 Token。
    • 前端将 Token 存储在本地(通常是 sessionStorage 或 localStorage)。
    • 在每个后续的请求中,前端将 Token 通过请求头(通常是 Authorization 头)发送给后端。
    • 后端从请求头中获取 Token,验证其合法性,并使用 Token 获取用户信息。

    后端(Spring Boot)示例:

    定义login接口

    @GetMapping("/login")
        public BaseResponse<String> login(String username,String password){
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(User::getUsername,username);
            User one = userService.getOne(lambdaQueryWrapper);
            if(one == null){
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
            }
            String token = UUID.randomUUID().toString();
            redisTemplate.opsForHash().putAll("test:user:"+token, BeanUtil.beanToMap(one));
            redisTemplate.expire("test:user:" + token,2, TimeUnit.MINUTES);
            return ResultUtils.success(token);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    定义一个获取用户信息的其他接口

    @GetMapping("/get")
        public BaseResponse<User> get(){
            return ResultUtils.success(UserHolder.getValue());
        }
    
    • 1
    • 2
    • 3
    • 4

    创建拦截器:

    public class LoginInterceptor implements HandlerInterceptor {
        private RedisTemplate<String,Object> redisTemplate;
        public LoginInterceptor(RedisTemplate<String,Object> template){
            this.redisTemplate = template;
        }
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            // 获取 session
            String token = request.getHeader("Authorization");
                    // 检查用户是否已登录
            User user = new User();
            BeanUtil.fillBeanWithMap(redisTemplate.opsForHash().entries("test:user:"+token),user,false);
            if(user == null|| BeanUtil.isEmpty(user)){
                throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
            }
            redisTemplate.expire("test:user:"+token,2, TimeUnit.MINUTES);
            // 将用户数据存储到 ThreadLocal 中,以便在整个请求周期内访问
            UserHolder.setValue(user);
            // 进行其他逻辑验证,根据需求自行添加
            return true; // 允许请求继续执行
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                               ModelAndView modelAndView) throws Exception {
            // 在请求处理之后执行,可以对 ModelAndView 进行修改
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            // 在请求完成之后执行,用于资源清理等操作
    
            // 清理 ThreadLocal 中的用户数据,防止内存泄漏
            UserHolder.clear();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    注册拦截器:

    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
        @Resource
        private RedisTemplate<String,Object> redisTemplate;
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 登录拦截器
            registry.addInterceptor(new LoginInterceptor(redisTemplate))
                    .addPathPatterns("/**").excludePathPatterns("/user/login");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用结果截图

    结果返回了一个token,这个token前端拿到以后要保存到sessionStorage或者LocalStorage里面,再把这个token送到请求头中,就可以啦

    在这里插入图片描述

    另一个请求:

    设置请求头

    设置Authorization
    在这里插入图片描述
    然后发起就可以得到啦
    在这里插入图片描述

    如果没有authorization就是得到未登录结果

    在这里插入图片描述

    前端(Vue.js)示例:

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 相关阅读:
    MySQL的多表操作-外键约束
    在 Visual Studio Code (VS Code) 中设置
    Gitea+Jenkins+webhooks-前端自动化部署
    NSSCTF-Web题目5
    《面试求职系列》(三):如何写简历及求职技巧
    Python 解决tkinter的Menu菜单command参数与bind方法共用触发事件
    biopython--PDB.polypepide
    windows系统玩游戏找不到d3dx9_35.dll缺失的解决方法
    我有点想用JDK17了
    SpringBoot交友APP项目实战(详细介绍+案例源码) - 10.网关配置
  • 原文地址:https://blog.csdn.net/Go_ahead_forever/article/details/133147866