解决验证码校验问题和 filter异常处理问题

pull/346/head
阿明 2 years ago
parent 5193598bb4
commit c90255f04f

@ -0,0 +1,98 @@
package com.ruoyi.web.admin.filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.util.ContentCachingRequestWrapper;
/**
* @author 1763113879@qq.com
* @version V2.1
* @since 2.1.0 2023/11/16 14:59
*/
//继承ContentCachingRequestWrapper
public class ContentCachingRequestWrapperNew extends ContentCachingRequestWrapper {
//原子变量,用来区分首次读取还是非首次
private AtomicBoolean isFirst = new AtomicBoolean(true);
public ContentCachingRequestWrapperNew(HttpServletRequest request) {
super(request);
}
public ContentCachingRequestWrapperNew(HttpServletRequest request, int contentCacheLimit) {
super(request, contentCacheLimit);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if(isFirst.get()){
//首次读取直接调父类的方法,这一次执行完之后 缓存流中有数据了
//后续读取就读缓存流里的。
isFirst.set(false);
return super.getInputStream();
}
//用缓存流构建一个新的输入流
return new ServletInputStreamNew(super.getContentAsByteArray());
}
//参考自 DelegatingServletInputStream
class ServletInputStreamNew extends ServletInputStream{
private InputStream sourceStream;
private boolean finished = false;
public ServletInputStreamNew(byte [] bytes) {
//构建一个普通的输入流
this.sourceStream = new ByteArrayInputStream(bytes);
}
@Override
public int read() throws IOException {
int data = this.sourceStream.read();
if (data == -1) {
this.finished = true;
}
return data;
}
@Override
public int available() throws IOException {
return this.sourceStream.available();
}
@Override
public void close() throws IOException {
super.close();
this.sourceStream.close();
}
@Override
public boolean isFinished() {
return this.finished;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
}
}

@ -1,5 +1,6 @@
package com.ruoyi.web.admin.filter;
import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.FilterChain;
@ -10,16 +11,18 @@ import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.Order;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.TokenConstants;
import com.ruoyi.common.core.context.SecurityContextHolder;
import com.ruoyi.common.core.exception.InnerAuthException;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.JwtUtils;
import com.ruoyi.common.core.utils.ServletUtils;
@ -29,7 +32,9 @@ import com.ruoyi.common.security.auth.AuthUtil;
import com.ruoyi.common.security.service.TokenService;
import com.ruoyi.system.api.model.LoginUser;
import com.ruoyi.web.admin.config.CustomHttpServletRequest;
import com.ruoyi.web.admin.config.properties.CaptchaProperties;
import com.ruoyi.web.admin.config.properties.IgnoreWhiteProperties;
import com.ruoyi.web.admin.service.ValidateCodeService;
import io.jsonwebtoken.Claims;
@ -54,6 +59,22 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private TokenService tokenService;
@Autowired
private ValidateCodeService validateCodeService;
@Autowired
private CaptchaProperties captchaProperties;
private static final String CODE = "code";
private static final String UUID = "uuid";
private final static String[] VALIDATE_URL = new String[] {"/auth/login", "/auth/register"};
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
@ -66,6 +87,27 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
}
String requestURI = request.getRequestURI();
// 非登录/注册请求或验证码关闭,不处理
if (StringUtils.equalsAnyIgnoreCase(requestURI, VALIDATE_URL) && captchaProperties.getEnabled()) {
//,HttpServletRequst中的body内容只会读取一次,但是可能某些情境下可能会读取多次,替换request
ContentCachingRequestWrapperNew wrappedRequest = new ContentCachingRequestWrapperNew(request);
try {
String rspStr = resolveBodyFromRequest(wrappedRequest);
JSONObject obj = JSON.parseObject(rspStr);
validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID));
} catch (Exception e) {
// throw new ServiceException(e.getMessage());
resolver.resolveException(request, response, null, e);
return;
}
request = wrappedRequest;
}
// 跳过不需要验证的路径
if (!StringUtils.matches(requestURI, ignoreWhite.getWhites())) {
@ -83,7 +125,6 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
boolean islogin = redisService.hasKey(getTokenKey(userkey));
if (!islogin) {
throw new ServiceException("登录状态已过期");
}
String userid = JwtUtils.getUserId(claims);
String username = JwtUtils.getUserName(claims);
@ -136,10 +177,8 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
return token;
}
private void addHeader(CustomHttpServletRequest customHttpServletRequest, String name, Object value)
{
if (value == null)
{
private void addHeader(CustomHttpServletRequest customHttpServletRequest, String name, Object value) {
if (value == null) {
return;
}
String valueStr = value.toString();
@ -147,6 +186,24 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
customHttpServletRequest.addHeader(name, valueEncode);
}
private String resolveBodyFromRequest(HttpServletRequest request) throws IOException {
// 直接从HttpServletRequest的Reader流中获取RequestBody
BufferedReader reader = request.getReader();
StringBuilder builder = new StringBuilder();
String line = reader.readLine();
while (line != null) {
builder.append(line);
line = reader.readLine();
}
reader.close();
String reqBody = builder.toString();
return reqBody;
// String requestBody = new String(request.getContentAsByteArray());
// return requestBody;
}
}

@ -0,0 +1,35 @@
package com.ruoyi.web.admin.handler;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.ruoyi.common.core.exception.CaptchaException;
import com.ruoyi.common.core.web.domain.AjaxResult;
/**
*
*
* @author ruoyi
*/
@RestControllerAdvice
public class GlobalAdminWebExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GlobalAdminWebExceptionHandler.class);
/**
*
*/
@ExceptionHandler(CaptchaException.class)
public AjaxResult handleCaptchaException(CaptchaException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}'", requestURI, e.getMessage());
return AjaxResult.error(e.getMessage());
}
}
Loading…
Cancel
Save