IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 分布式会话拦截器 -> 正文阅读

[Java知识库]分布式会话拦截器

一、构建基本拦截器

需要新建的类的位置结构:

1、新建自己的拦截类 UserTokenInterceptor 实现 HandlerInterceptor 拦截器接口,实现三个方法

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class UserTokenInterceptor implements HandlerInterceptor {
    /**
     * 拦截请求,在controller调用之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("验证不通过,请求被拦截。。。");
        /**
         * false: 验证不通过,请求被拦截,不能继续访问
         * true:  验证通过,请求放行。
         */
        return false;
    }

    /**
     * 请求访问controller之后,渲染视图之前
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 请求访问controller之后,渲染视图之后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2、新建自定义类实现 WebMvcConfigurer 接口

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    //注册拦截器
    @Bean
    public UserTokenInterceptor userTokenInterceptor(){
        return new UserTokenInterceptor();
    }

    /**
     * 注册拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(userTokenInterceptor())
                .addPathPatterns("/hello");

        //添加注册器
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

这里先测试拦截 /hello 地址。其controller如下,其实看不看都无所谓(你也可以设置成拦截自己的任意一个地址)

?3、启动springboot项目,访问刚刚被拦截的地址 localhost:8088/hello 。因为上面第1步preHandle

方法,设置了返回false,请求拦截。查看控制台,打印内容。基本拦截器完成。


(以下是举例使用,具体看个人情况)

二、拦截器具体逻辑实现(我这里实现会话判断)

1、在上述? 一、1. UserTokenInterceptor的覆盖方法 preHandle()?中,填写自己的拦截逻辑

import com.xpf.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class UserTokenInterceptor implements HandlerInterceptor {


    public static final String REDIS_USER_TOKEN = "redis_user_token";
    
    @Autowired
    private RedisOperator redisOperator;
    
    /**
     * 拦截请求,在controller调用之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//        System.out.println("验证不通过,请求被拦截。。。");

        //用户一登录,就会把用户token记录到redis和cookie中,所以前端可以拿到用户信息。
        //且在需要判断用户权限的地方,在访问后端url时,都会把用户id和token存放到Header中,一起传入后端
        String userId = request.getHeader("headerUserId");
        String userToken = request.getHeader("headerUserToken");

        if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)){
            String uniqueToken = redisOperator.get(REDIS_USER_TOKEN+":"+userId);
            if (StringUtils.isBlank(uniqueToken)){
                System.out.println("redis没有信息,请登录。。。");
                return false;
            }else {
                if (!uniqueToken.equals(userToken)){
                    System.out.println("账号在异地登录或不同设备登录,请重新登录");
                    return false;
                }
            }
        }else {
            System.out.println("请登录。。。");
            return true;
        }

        /**
         * false: 验证不通过,请求被拦截,不能继续访问
         * true:  验证通过,请求放行。
         */
        return true;
    }

    /**
     * 请求访问controller之后,渲染视图之前
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 请求访问controller之后,渲染视图之后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

与前端约定的header,存储用户id和用户token。

用户一登录,就会把用户token记录到redis和cookie中,所以前端可以拿到用户信息。?且在需要判断用户权限的地方,在访问后端url时,都会把用户id和token存放到Header中,一起传入后端。

只有当用户登录,且前端cookie中的token和redis中token相同时,才能访问后端的接口,否则被拦截器拦截。

2、添加需要拦截的路径

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    //注册拦截器
    @Bean
    public UserTokenInterceptor userTokenInterceptor(){
        return new UserTokenInterceptor();
    }

    /**
     * 注册拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(userTokenInterceptor())
                .addPathPatterns("/hello")
                .addPathPatterns("/shopcart/add")
                .addPathPatterns("/shopcart/del")
                .addPathPatterns("/address/list")
                .addPathPatterns("/address/add")
                .addPathPatterns("/address/update")
                .addPathPatterns("/address/setDefault")
                .addPathPatterns("/address/delete")
                .addPathPatterns("/orders/*")
                .addPathPatterns("/center/*")
                .addPathPatterns("/userInfo/*")
                .addPathPatterns("/myorders/*")
                .addPathPatterns("/mycomments/*")
                .excludePathPatterns("/myorders/deliver")
                .excludePathPatterns("/orders/notifyMerchantOrderPaid")
        ;

        //添加注册器
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

三、优化错误的返回

从上述二中实现拦截逻辑后,当被拦截时,应该将提醒登录的信息返回给前端,但是 boolean?preHandle() 方法返回值是boolean类型。和我们在整个项目约定的统一返回类型JSONResult不一致,因此需要使用 OutputStream 字符输出流,写到前端。

import com.xpf.utils.JSONResult;
import com.xpf.utils.JsonUtils;
import com.xpf.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

public class UserTokenInterceptor implements HandlerInterceptor {


    public static final String REDIS_USER_TOKEN = "redis_user_token";
    
    @Autowired
    private RedisOperator redisOperator;
    
    /**
     * 拦截请求,在controller调用之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//        System.out.println("验证不通过,请求被拦截。。。");

        //用户一登录,就会把用户token记录到redis和cookie中,所以前端可以拿到用户信息。
        //且在需要判断用户权限的地方,在访问后端url时,都会把用户id和token存放到Header中,一起传入后端
        String userId = request.getHeader("headerUserId");
        String userToken = request.getHeader("headerUserToken");

        if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)){
            String uniqueToken = redisOperator.get(REDIS_USER_TOKEN+":"+userId);
            if (StringUtils.isBlank(uniqueToken)){
//                System.out.println("redis没有信息,请登录。。。");
                returnErrorResponse(response, JSONResult.errorMsg("redis中没有信息,请登录..."));
                return false;
            }else {
                if (!uniqueToken.equals(userToken)){
//                    System.out.println("账号在异地登录或不同设备登录,请重新登录");
                    returnErrorResponse(response, JSONResult.errorMsg("账号在异地登录或不同设备登录,请重新登录"));
                    return false;
                }
            }
        }else {
//            System.out.println("请登录。。。");
            returnErrorResponse(response,JSONResult.errorMsg("请登录..."));
            return false;
        }

        /**
         * false: 验证不通过,请求被拦截,不能继续访问
         * true:  验证通过,请求放行。
         */
        return true;
    }

    /*

     */
    public void returnErrorResponse(HttpServletResponse response, JSONResult result){
        OutputStream out = null;

        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("text/json");
            out = response.getOutputStream();
            out.write(JsonUtils.objectToJson(result).getBytes(StandardCharsets.UTF_8));
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if (out != null)
                    out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 请求访问controller之后,渲染视图之前
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 请求访问controller之后,渲染视图之后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

四、测试

1、访问 /hello

?2、访问需要登录的页面,前端拿到写入的信息,在做一个弹框提示

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:17:51  更:2022-03-10 22:19:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:34:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码