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。這樣看單個攔截器的範圍比過濾器要小不少。

以上內容屬我的學習總結,若有不當之處,歡迎在評論中指正

相關文章
相關標籤/搜索