天天看點

spring boot 使用 HandlerInterceptor# 背景# DEMO

# 背景

在實際項目中,接口出于安全考慮,都會有驗簽的計算。目前接觸的項目來看基本都是時間戳+幹擾因子 然後md5計算的方式。現在學習,寫一個簡單demo,

其實如果不引入攔截器的話,驗簽計算全部在controller層實作也是可以的,但每個請求都需要去做一次計算,這種把公共功能的抽離,針對于所有請求前的判斷,個人感覺有點切面的意思;

# DEMO

核心點:

1. controller層還是和原來的一模一樣,不做修改

2. 建立一個ApiSignInterceptor 類 ,實作HandlerInterceptor 接口,完成 驗簽計算的核心代碼;

3. 建立一個WebConfig類,繼承WebMvcConfigurationSupport類,引入步驟2中建立的攔截器;

前言:

jdk8+spring boot2.0 版本 如果低版本些許不一緻

show CODE

controller層:

@RestController
public class PeopleController {

    @GetMapping(value = "/1/people/{people_id}")
    public String getPeopleInfo(@PathVariable(value = "people_id", required = true) String peopleId) {
        return "hello world, this is people info of " + peopleId;
    }


    @GetMapping(value = "/2/people/{people_id}")
    public String getPeopleInfoV2(@PathVariable(value = "people_id", required = true) String peopleId) {
        return "hello THIS is v2 world, this is people info V2 of " + peopleId;
    }
}      

沒有任何變化,簡單demo例子

攔截器,ApiSignInterceptor :

public class ApiSignInterceptor implements HandlerInterceptor {


    private final static String SEPERATOR = "_";
    private final static String SECRET = "jwentest";
    private final static String NO_PERMISSION_ERROR_MESSAGE = "Api Token Error, You have no permission to access this api";


    // md5計算
    private String md5Hex(String data) {
        return DigestUtils.md5Hex(data).toLowerCase();
    }

    private String getSign(String t) {
        return md5Hex(t + SEPERATOR + SECRET);
    }

    // sign計算,t為時間戳,sign為md5(t+"_"+"jwentest")

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        try {
            String t = request.getParameter("t");
            String sign = request.getParameter("sign");

            if (t.isEmpty() || sign.isEmpty()) {
                response.sendError(403, NO_PERMISSION_ERROR_MESSAGE);
                return false;
            }

            String expectedSign = getSign(t);

            if (!expectedSign.equals(sign)) {
                response.sendError(403, NO_PERMISSION_ERROR_MESSAGE);
                return false;
            }

        } catch (Throwable t) {
            response.sendError(403, NO_PERMISSION_ERROR_MESSAGE);
            return false;
        }

        return true;

    }

}      

其中HandlerInterceptor 接口定義了三個方法,第一次看到我有點懵逼了,為啥接口定義的方法裡面會有方法體呢,為什麼可以不實作所有的方法了的,原因是JDK8中可以這樣寫了:

default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }      

本次我們使用到的是preHandle方法,三個方法的執行順序如下:

preHandler -> Controller -> postHandler -> model渲染-> afterCompletion

是以可以在進入controller層之前攔截判斷是否符合我們的安全要求;

使用,WebConfig 類:

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ApiSignInterceptor()).addPathPatterns("/1/people/**").excludePathPatterns("/2/people/**");
        super.addInterceptors(registry);
    }
}      

這裡是在項目引入攔截器,

@Configuration ,config形式加載在容器中

其中addPathPatterns 和 excludePathPatterns 方法,從方法名就可以看出來,是針對攔截器的範圍控制,上面的代碼就是針對/1/people/** 生效,對/2/people/**  不生效

目錄結構如下:

spring boot 使用 HandlerInterceptor# 背景# DEMO

雖千萬人,吾往矣!