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

内容概述

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

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

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

使用redis保存并共享session,能够实现集群内的登陆信息共享。SpringBoot项目中,经过在application.yml增长redis的配置,便可实现对session的存储和修改。web

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

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

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

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

  • SessionRepositorysession

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

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

固然也能够实现一个本身的过滤器,主要有两种方式:ide

  • 实现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获得的执行顺序以下图:

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

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

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

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

  • 过滤器基本是对于整个http请求的分析处理,好比ip过滤,session处理,header处理等。而不是针对特定的url,范围比拦截器更广。几乎全部的Http请求都要通过过滤器的处理。
  • 拦截器主要是针对一批特定的url作处理,不一样的url应用不一样的拦截器,例如鉴权的拦截器就不会处理注册或登陆等不须要权限的url。这样看单个拦截器的范围比过滤器要小不少。

以上内容属我的学习总结,若有不当之处,欢迎在评论中指正

相关文章
相关标签/搜索