美文网首页
SpringBoot系列:4.session和鉴权—过滤器和拦截

SpringBoot系列:4.session和鉴权—过滤器和拦截

作者: dothetrick | 来源:发表于2020-11-29 22:49 被阅读0次

    内容概述

    本文主要介绍下,SpringBoot的web项目中,

    • 请求中session是如何被处理的 - 过滤器
    • 鉴权的实现原理 - 拦截器
    • 过滤器和拦截器的对比和应用场景

    1.session是如何被处理的 - 过滤器

    使用redis保存并共享session,可以实现集群内的登录信息共享。SpringBoot项目中,通过在application.yml增加redis的配置,即可实现对session的存储和修改。

    那么session是在何时被处理的?session的key又是如何生成的呢?这里实际使用了web项目中的过滤器。

    在SpringBoot的web项目中,启动的tomcat在处理http请求时,有一个很重要的类:ApplicationFilterChain。每个http请求在处理时都会通过这个类。这个类负责按顺序处理全部已注册的Filter,也就是过滤器。通过实现tomcat中的Filter接口,就可以定义一个过滤器。

    在SpringBoot中的web项目中,有几个默认的过滤器,其中一个就是用来处理session的:SessionRepositoryFilter

    SessionRepositoryFilter主要的成员是两个接口,都有多个可选的实现类,通过这两个成员就实现了对session的解析。

    • SessionRepository

      • 用来保存session数据的类,配置将session保存在redis中时,使用的就是RedisIndexedSessionRepository
    • HttpSessionIdResolver

      • 负责从HTTP请求中解析出sessionid。默认使用的是CookieHttpSessionIdResolver,可以看出是从cookie中解析。
      • 还有一个实现类是HeaderHttpSessionIdResolver,由此可以看出还可以将sessionid保存在header中。

    当然也可以实现一个自己的过滤器,主要有两种方式:

    • 实现Filter接口,并使用@WebFilter注解
      • 下面我们的实现中继承了OncePerRequestFilter这个类,这个类也是Filter接口的实现类,封装了一些功能,使用更方便。
    • 写一个创建@Bean的方法,返回FilterRegistrationBean的对象。

    下面我们使用第一种方式实现一个限制指定IP的过滤器:

    /**
     * ip黑名单
     */
    @WebFilter
    public class IPFilter extends OncePerRequestFilter { //这里继承OncePerRequestFilter
    
        private List<String> forbiddenIpList = new ArrayList<>();
    
        public IPFilter() {
            this.forbiddenIpList.add("10.112.13.167");
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String ip = request.getRemoteAddr();
            if (this.forbiddenIpList.contains(ip)) {
                response.setContentType("application/json;charset=UTF-8");
                PrintWriter out = response.getWriter();
                ObjectMapper objectMapper = new ObjectMapper();
                out.write(objectMapper.writeValueAsString(CommonResVo.fail(400, "禁止访问的ip")));
                out.flush();
            } else {
                filterChain.doFilter(request, response);
            }
        }
    }
    
    

    2.鉴权的实现原理 - 拦截器

    通过过滤器解析session后,就可以根据session中保存的内容,判断当前登录的用户权限。

    这里是通过一个拦截器实现的,在拦截器中可以直接通过HttpServletRequest.getSession()方法直接获取session的信息。

    一个简单的拦截器实现如下:

    @Component
    public class LoginAuthInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HttpSession session = request.getSession();
            Integer userId = (Integer) session.getAttribute("userId");
            if (userId == null) {
                authFailOutput(response, "登录信息不存在,请重新登录");
                return false;
            }
            return true;
        }
        /**
         * json输出
         */
        private void authFailOutput(HttpServletResponse response, String msg) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            PrintWriter out = response.getWriter();
            ObjectMapper objectMapper = new ObjectMapper();
            out.write(objectMapper.writeValueAsString(CommonResVo.fail(400, msg)));
            out.flush();
        }
    }
    

    定义之后要注册到处理流程中:

    @Configuration
    public class Interceptor implements WebMvcConfigurer {
    
      @Resource
      HandlerInterceptor loginAuthInterceptor;
    
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          // 添加一个拦截器,判断权限,排除掉登录接口
          registry.addInterceptor(loginAuthInterceptor)
                  .excludePathPatterns("/user/login","/user/register");
      }
    }
    

    3.过滤器和拦截器的对比和应用场景

    先来看下过滤器和拦截器的执行顺序,通过debug得到的执行顺序如下图:

    image

    在大部分场景中,过滤器和拦截器都是可互换的,使用哪个都可以。

    过滤器和拦截器也有些区别,这里不谈实现和规范的差异,就说下使用中可能涉及的区别:

    • 执行顺序不同:从上图中可以看到,过滤器是先于拦截器执行的,在组合使用实现功能时,要注意下顺序。
    • 可获得的参数不同:在拦截器中,可以获取当前请求绑定的Controller的处理方法,通过对方法分析可以实现一些特殊需求。比如在拦截器中按既定的格式返回空数据。

    最后对于过滤器和拦截器的应用场景,说下个人的总结。基于执行顺序,方法参数和SpringBoot中的一些实现类来看。

    • 过滤器基本是对于整个http请求的分析处理,比如ip过滤,session处理,header处理等。而不是针对特定的url,范围比拦截器更广。几乎所有的Http请求都要经过过滤器的处理。
    • 拦截器主要是针对一批特定的url做处理,不同的url应用不同的拦截器,例如鉴权的拦截器就不会处理注册或登录等不需要权限的url。这样看单个拦截器的范围比过滤器要小很多。

    以上内容属个人学习总结,如有不当之处,欢迎在评论中指正

    相关文章

      网友评论

          本文标题:SpringBoot系列:4.session和鉴权—过滤器和拦截

          本文链接:https://www.haomeiwen.com/subject/goslwktx.html