SpringBoot配置自定义拦截器实现过程详解

在系统中经常需要在处理用户请求之前和之后执行一些行为,例如检测用户的权限,或者将请求的信息记录到日志中,即平时所说的权限检测及日志记录,下面这篇文章主要给大家介绍了关于在SpringBoot项目中整合拦截器的相关资料,需要的朋友可以参考

1. HttpServletRequest包装类

因为HttpServletRequest只能读取一次,所以需要对request进行包装,变成可重复读的request.

package net.lesscoding.interceptor;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
/**
 *  由于流只能读取一次,所以使用此包装类对HttpServletRequest对象进行包装,读取完之后再将
 *  内容塞回去,不影响后续springmvc的参数处理。
 */
public class RequestWrapper extends HttpServletRequestWrapper {
    private String body;
    public RequestWrapper(HttpServletRequest request) {
        super(request);
        if (request.getHeader("Content-Type") != null
                && request.getHeader("Content-Type").contains("multipart/form-data")){
            try{
                request.getParts();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        StringBuilder stringBuilder = new StringBuilder();
        try (InputStream inputStream = request.getInputStream();
             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))){
            char[] charBuffer = new char[128];
            int bytesRead = -1;
            while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                stringBuilder.append(charBuffer, 0, bytesRead);
            }
        }catch (NullPointerException ex){
            stringBuilder.append("");
        } catch (Exception ex) {
        }
        body = stringBuilder.toString();
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;

    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
}

2. 使用Filter将request传递下去

因为filter是在request前边执行的,所以我们需要使用一个filter将我们包装好的request传递下去,让后边使用的request都是我们包装好的,防止出现流已经被读取的错误出现

package net.lesscoding.filter;
import net.lesscoding.interceptor.RequestWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
 * @apiNote 传递request的过滤器
 */
public class RepeatedlyReadFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        if(request instanceof HttpServletRequest){
            requestWrapper = new RequestWrapper((HttpServletRequest) request);
        }
        chain.doFilter( requestWrapper == null ? request : requestWrapper,response );
    }
    @Override
    public void destroy() {
    }
}

3. 添加拦截器

这里我们添加一个判断用户登录状态的拦截器,从 request中获取token信息,查询redis如果redis存在则用户已经登录,否则就返回false

package net.lesscoding.interceptor;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
/**
 * @apiNote 登录拦截器
 */
@Configuration
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("AccessToken");
        String redisToken = String.valueOf(redisTemplate.opsForValue().get(token));
        if(StrUtil.isBlank(redisToken)){
            throw new RuntimeException("token失效,请重新登录");
        }
        // 这里为了方便 成功之后就刷新在redis中的时间
        redisTemplate.opsForValue().set("token",redisToken,30, TimeUnit.MINUTES);
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

4. 全局异常处理器

使用全局异常处理器捕获拦截器抛出的异常信息,做统一返回

package net.lesscoding.config;
import lombok.extern.slf4j.Slf4j;
import net.lesscoding.common.Result;
import net.lesscoding.common.ResultFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
 * @apiNote 全局异常处理器
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(value = RuntimeException.class)
    public Result runtimeExceptionHandler(RuntimeException e){
        e.printStackTrace();
        log.error("{}", e.getMessage());
        return ResultFactory.buildThrowable(e);
    }
    @ExceptionHandler(value = Exception.class)
    public Result exceptionHandler(Exception e){
        e.printStackTrace();
        log.error("{}", e.getMessage());
        return ResultFactory.buildThrowable(e);
    }
    @ExceptionHandler(value = Throwable.class)
    public Result exceptionHandler(Throwable t){
        t.printStackTrace();
        log.error("{}", t.getMessage());
        return ResultFactory.buildThrowable(t);
    }
}

5. 配置拦截器

对拦截器进行注册,指定拦截哪些url请求

package net.lesscoding.config;
import net.lesscoding.filter.RepeatedlyReadFilter;
import net.lesscoding.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * @apiNote 对项目进行配置拦截器
 */
@Configuration
public class LoginConfiguration implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Bean
    public FilterRegistrationBean repeatedlyReadFilter(){
        FilterRegistrationBean registration = new FilterRegistrationBean();
        RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter();
        registration.setFilter(repeatedlyReadFilter);
        registration.addUrlPatterns("/*");
        return registration;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                // 拦截所有请求
                .addPathPatterns("/**")
                // 排除登录注册修改密码等接口
                .excludePathPatterns("/oauth/**","/login/**","logout/**","/common/**", "options","/try/**","/user/updatePwd")
                // 判处swagger等接口
                .excludePathPatterns("/doc.html","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**","/favicon.ico")
        ;
    }
}

到此这篇关于SpringBoot配置自定义拦截器实现过程详解的文章就介绍到这了,更多相关SpringBoot自定义拦截器内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!

本文标题为:SpringBoot配置自定义拦截器实现过程详解

基础教程推荐