原文连接:http://www.360doc.com/document/19/0106/20/39911641_807065847.shtml
原文连接中有对 可重复读 和 验签业务逻辑的大致说明
代码实现步骤
第一步
编写 RequestWrapper类 extends HttpServletRequestWrapper
package com.xihongshi.common.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
/**
* @author mgq
* @since 2021-03-30 9:59
*/
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
/**
* 存储body数据
*/
private final byte[] body;
public RequestWrapper(final HttpServletRequest request) {
super(request);
// 将body数据存储起来
String bodyStr=getBodyString(request);
// 初始化body
body = bodyStr.getBytes(Charset.defaultCharset());
}
public String getBodyString(ServletRequest request) {
try {
return inputStream2String(request.getInputStream());
} catch (IOException e) {
log.error("",e);
throw new RuntimeException(e);
}
}
public String getBodyString() {
final InputStream byteArrayInputStream = new ByteArrayInputStream(body);
return inputStream2String(byteArrayInputStream);
}
private String inputStream2String(InputStream inputStream) {
StringBuilder sb = new StringBuilder();
BufferedReader reder = null;
try {
reder=new BufferedReader(new InputStreamReader(inputStream,Charset.defaultCharset()));
String line;
while ((line=reder.readLine())!=null){
sb.append(line);
}
} catch (Exception e) {
log.error("",e);
throw new RuntimeException(e);
}finally {
if (reder!=null) {
try {
reder.close();
} catch (IOException e) {
log.error("",e);
}
}
}
return sb.toString();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(final ReadListener readListener) {
}
@Override
public int read() throws IOException {
return inputStream.read();
}
};
}
}
第二步
编写 ReplaceStreamFilter类,implements Filter
package com.xihongshi.common.filter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author mgq
* @since 2021-03-30 10:20
*/
@Slf4j
public class ReplaceStreamFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("StreamFilter初始化");
}
@Override
public void doFilter(final ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = new RequestWrapper((HttpServletRequest) request);
chain.doFilter(requestWrapper,response);
}
@Override
public void destroy() {
log.info("StreamFilter销毁");
}
}
第三步
编写JWTInterceptor类,implements HandlerInterceptor 用于测试从request的body中取数据,验证后续接口是否能接收到@requestBody中的数据
package com.xihongshi.client.interceptor;
import com.auth0.jwt.JWT;
import com.xihongshi.common.constants.RedisConstants;
import com.xihongshi.common.errcode.BizErrorCodeEnum;
import com.xihongshi.common.exception.BizException;
import com.xihongshi.common.exception.InvalidSessionException;
import com.xihongshi.common.filter.RepeatableRequestWrapper;
import com.xihongshi.common.filter.RequestWrapper;
import com.xihongshi.utils.JwtUtils;
import com.xihongshi.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.SignUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStream;
/**
*
* @author: mengshaoshuai
* @date 2021年3月12日 下午1:51:20
*/
@Slf4j
public class JWTInterceptor implements HandlerInterceptor {
@Value("${client.auth.test.enabled:false}")
private boolean authTestEnabled = false;
@Value("${client.auth.test.accountId:}")
private Integer authTestAccountId;
@Autowired
JwtUtils jwtUtils;
@Autowired
RedisUtil redisUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("token");
String sb = new RequestWrapper(request).getBodyString();
System.out.println(sb);
try {
jwtUtils.verify(token);
} catch (Exception e) {
if(authTestEnabled) {
//test
request.setAttribute("unionId", authTestAccountId+"");
request.setAttribute("accountId", authTestAccountId+"");
}else{
throw new InvalidSessionException(BizErrorCodeEnum.JWT_TOKEN_ERROR);
}
return true;
}
String id = JWT.decode(token).getAudience().get(0);
String tknow = (String)redisUtil.get(RedisConstants.JWT_KEY_ACCOUNT+id);
if(tknow==null || !tknow.equals(token)) {
throw new BizException(BizErrorCodeEnum.JWT_TOKEN_EXPIRE);
}
String mobile = JWT.decode(token).getClaim("mobile").asString();
request.setAttribute("accountId", id);
request.setAttribute("unionId", mobile);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
clearMdc();
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
clearMdc();
}
/**
* 清理日志上下文
*/
protected void clearMdc() {
try {
MDC.clear();
} catch (Exception e) {
e.printStackTrace();
}
}
}
第四步
过滤配置类
注意:第四不和第五步必须配置,要不然1和2步骤不生效
package com.xihongshi.client.interceptor;
import com.xihongshi.common.filter.RepeatableReadFilter;
//import com.xihongshi.common.filter.ReplaceStreamFilter;
import com.xihongshi.common.filter.ReplaceStreamFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
/**
* 过滤配置类
* @author mgq
* @since 2021-03-30 10:52
*/
@Configuration
public class FilterConfig {
/**
* 注册过滤器
* @return
*/
@Bean
public FilterRegistrationBean someFilterRegistration(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(replaceStreamFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.setName("streamFilter");
return filterRegistrationBean;
}
/**
* 实例化StreamFilter
* @return
*/
@Bean(name="replaceStreamFilter")
public Filter replaceStreamFilter(){
return new ReplaceStreamFilter();
}
}
第五步
拦截器配置
package com.xihongshi.client.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器配置
* @author mgq
* @since 2021-03-30 11:12
*/
@Slf4j
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Bean
public JWTInterceptor getJWTInterceptor(){
return new JWTInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getJWTInterceptor())
.addPathPatterns("/**");
}
}
第六步
随便写一个controller接口,在接口中调用参数是@requestbody 接收的json对象