分布式会话拦截器

一、构建基本拦截器

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

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、访问需要登录的页面,前端拿到写入的信息,在做一个弹框提示

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值