========================
开发记录:拦截器配置笔记(token认证拦截器和JWT校验拦截器)
1、创建配置类WebInterceptorConfig实现WebMvcConfigurer;
2、向InterceptorRegistry中依次注册拦截器;
3、总结:多个拦截器的执行顺序

1. 配置类WebInterceptorConfig

package com.tal.markms.config;

import com.tal.markms.interceptor.AuthenticationInterceptor;
import com.tal.markms.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class WebInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthenticationInterceptor())
                .addPathPatterns("/markms/login");
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**").excludePathPatterns("/markms/login");
    }
}

2.1 创建AuthenticationInterceptor拦截器

package com.tal.markms.interceptor;

import com.tal.markms.exception.BizException;
import com.tal.markms.exception.ErrorCode;
import com.tal.markms.utils.EmptyHelper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

import static com.tal.markms.common.CommonConstant.RPCID;
import static com.tal.markms.common.CommonConstant.TRACEID;


@Slf4j
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {

    private static String token;

    @Value("${server-access.token}")
    public void setToken(String token) {
        AuthenticationInterceptor.token = token;
    }

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

        log.info("AuthenticationInterceptor preHandle start ==== ");
        MDC.put("startTime", String.valueOf(System.currentTimeMillis()));

        String token = request.getHeader("token");
        if (EmptyHelper.isEmpty(token)) {
            log.info(" token is null ==== ");
            throw new BizException(ErrorCode.SystemError.ERROR_TOKEN_NULL);
        }
        if (!token.equals(AuthenticationInterceptor.token)) {
            log.info(" token is not match ==== ");
            throw new BizException(ErrorCode.SystemError.ERROR_TOKEN_NOT_MATCH);
        }
        String traceId = request.getHeader(TRACEID);
        if (EmptyHelper.isEmpty(traceId)) {
            traceId = UUID.randomUUID().toString().replace("-", "");
            log.info("traceId is null, generate traceId: {} ", traceId);
        }
        log.info("traceId:{}", traceId);
        MDC.put("traceId", traceId);

        String rpcId = request.getHeader(RPCID);
        if (EmptyHelper.isEmpty(rpcId)) {
            rpcId = "1.1";
            log.info("rpcId is null, generate rpcId: {} ", rpcId);
        }
        MDC.put("rpcId", rpcId);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("responseEnd, useTime:{}", System.currentTimeMillis() - Long.parseLong(MDC.get("startTime")));
        MDC.clear();
    }
}

2.2 创建JWTInterceptor拦截器

package com.tal.markms.interceptor;

import com.tal.markms.config.JWTWebConfig;
import com.tal.markms.utils.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class JWTInterceptor implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) {
        try {
            String token = httpServletRequest.getHeader("JWT");

            if (token == null) {
                refuseResult(httpServletResponse);
                return false;
            }
            if(!JWTUtils.verify(token)){
                //先检查并更新 后用更新后的再次校验
                if(JWTWebConfig.checkAndRefreshSecretKey()||!JWTUtils.verify(token)){
                    refuseResult(httpServletResponse);
                    return false;
                }
            }
            return true;
        }catch (Exception e){
            try {
                refuseResult(httpServletResponse);
                return false;
            }catch (Exception exception){
                return false;
            }
        }
    }

    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }

    public void refuseResult(HttpServletResponse httpServletResponse){
        try {
            httpServletResponse.setContentType("text/html;charset=UTF-8");
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setStatus(200);
            httpServletResponse.getWriter().print("{\"code\":2001,\"message\":\"登录信息有误或已失效,请重新登录\"}");
            httpServletResponse.getWriter().flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
创建JWT配置类设置JWT公钥、私钥、加密算法、过期时间:
package com.tal.markms.config;

import com.auth0.jwt.algorithms.Algorithm;
import com.tal.markms.enums.EnvEnum;
import com.tal.markms.factory.AlgorithmFactory;
import com.tal.markms.interceptor.JWTInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.json.JacksonJsonParser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;


@Configuration
public class JWTWebConfig implements WebMvcConfigurer {

    public static String PUBLIC_KEY = "2580eb1e23fa21a84117bbd865f038a5";
    public static String PRIVATE_KEY = "2580eb1e23fa21a84117bbd865f038a5";
    public static final long EXPIRES_TIME = 60*60*1000*24*5L;

    @Value("${jwt.interceptor.paths:}")
    String interceptorPaths;

    @Value("${jwt.interceptor.exclude.paths:}")
    String excludePaths;

    @Value("${jwt.algorithm:}")
    String algorithmStr;

    public static String url;


    private static Algorithm algorithm;

    EnvEnum envEnum;

    @Value("${jwt.public_key:}")
    public void setPublicKey(String arg){
        PUBLIC_KEY =arg;
    }

    @Value("${jwt.private_key:}")
    public void setPrivateKey(String arg){
        PRIVATE_KEY =arg;
    }

    @Value("${jwt.url:}")
    public void setURL(String arg){
        url=arg;
    }

    @Value("${jwt.env:}")
    public void setEnv(String env) {
        if(env == null || "".equals(env)){
            env = "DEV";
        }
        envEnum = EnvEnum.valueOf(env.toUpperCase());
    }

    @PostConstruct
    protected void init() throws Exception {
        if(!EnvEnum.OTHER.equals(envEnum)){
            url = envEnum.getKeyURL();
        }
        //other环境下不配置url不会更新public_key 以配置文件为主
        if(url == null||"".equals(url)) {
            algorithm = AlgorithmFactory.getInstance(algorithmStr, PUBLIC_KEY,PRIVATE_KEY);
        }

        checkAndRefreshSecretKey();
        System.out.println("JWT url load success, url is "+url);
        System.out.println("JWT SECRET_KEY load success, key is "+ PUBLIC_KEY);
    }

    public static boolean checkAndRefreshSecretKey(){
        try {
            if(url == null || "".equals(url)) {
                return true;
            }
            String str = getSecretKey(url);
            JacksonJsonParser parser = new JacksonJsonParser();
            Map result = parser.parseMap(str);
            Map dataMap = (Map)result.get("data");
            String newPublicKey = (String)dataMap.get("publicKey");
            String algorithmName = (String)dataMap.get("algorithm");
            if(PUBLIC_KEY.equals(newPublicKey)) {
                return true;
            }
            PUBLIC_KEY = newPublicKey;
            algorithm = AlgorithmFactory.getInstance(algorithmName, PUBLIC_KEY,null);
            return false;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    public static String getSecretKey(String urlStr){
        if(urlStr == null||"".equals(urlStr)) {
            return null;
        }
        String result = "";
        BufferedReader in = null;
        try {
            URL url = new URL(urlStr);
            // 打开和URL之间的连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            // 建立实际的连接
            connection.connect();
            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                System.out.println("connect failed for code " + connection.getResponseCode());
                return null;
            }
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
            return result;
        } catch (Exception e) {
            System.out.println("JWT SECRET_KEY get failed" + e);
            e.printStackTrace();
            return null;
        } finally {
            // 使用finally块来关闭输入流
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

    public static Algorithm getAlgorithm(){
        return algorithm;
    }

    /**
     * 添加jwt拦截器
     * @param registry
     */
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration registration = null;
        if(!(interceptorPaths == null||"".equals(interceptorPaths))) {
            registration = registry.addInterceptor(JWTInterceptor());
            System.out.println("加载拦截器 路径为="+interceptorPaths);
            String[] interceptorPathArray=interceptorPaths.split(",");
            registration.addPathPatterns(interceptorPathArray);
        }
        if(!(excludePaths == null||"".equals(excludePaths))) {
            System.out.println("加载拦截器 排除路径="+excludePaths);
            String[] excludePathArray=excludePaths.split(",");
            registration.excludePathPatterns(excludePathArray);
        }

    }

    /**
     * jwt拦截器
     * @return
     */
    @Bean
    public JWTInterceptor JWTInterceptor() {
        return new JWTInterceptor();
    }
}

注意:在 Spring 配置中添加了多个拦截器,拦截器的执行顺序如下:

  • preHandle 方法
    preHandle 方法是按照配置的顺序依次执行的。
    如果某个 preHandle 方法返回 false,那么后续的 preHandle 不会被执行,请求也不会到达控制器。
  • postHandle 和 afterCompletion 方法
    postHandle 和 afterCompletion 方法的执行顺序与 preHandle 相反,即按照配置顺序的倒序执行。
    这是因为 postHandle 和 afterCompletion 负责处理响应,在处理完请求后再逆序执行。
  • 异常处理
    如果在拦截器链中或控制器中抛出异常,afterCompletion 方法仍然会被执行,用于进行资源清理等操作。

示例

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new Interceptor1());
        registry.addInterceptor(new Interceptor2());
        registry.addInterceptor(new Interceptor3());
    }
}
  • 执行顺序:
    preHandle: Interceptor1 -> Interceptor2 -> Interceptor3
    postHandle: Interceptor3 -> Interceptor2 -> Interceptor1
    afterCompletion: Interceptor3 -> Interceptor2 -> Interceptor1

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部