元のリンク:http://www.360doc.com/document/19/0106/20/39911641_807065847.shtml
元のリンクには、繰り返し可能な読み取りと検証のビジネスロジックの一般的な説明があります。
コードの実装手順
最初の一歩
编写RequestWrapperクラスは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クラスを記述し、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销毁");
}
}
3番目のステップ
JWTInterceptorクラスを記述し、HandlerInterceptorを実装して、リクエストの本文からデータをフェッチするためのテストを行い、後続のインターフェイスが@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();
}
}
}
4番目のステップ
フィルタ構成クラス
注:4番目と5番目のステップを構成する必要はありません。構成しないと、ステップ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();
}
}
5番目のステップ
インターセプター構成
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("/**");
}
}
6番目のステップ
コントローラインターフェイスを自由に記述してください。インターフェイスの呼び出しパラメータは、@ requestbodyが受信したjsonオブジェクトです。