解决验证码校验问题和 filter异常处理问题
This commit is contained in:
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;
|
package com.ruoyi.web.admin.filter;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
|
|
@ -10,16 +11,18 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.filter.OncePerRequestFilter;
|
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.CacheConstants;
|
||||||
import com.ruoyi.common.core.constant.SecurityConstants;
|
import com.ruoyi.common.core.constant.SecurityConstants;
|
||||||
import com.ruoyi.common.core.constant.TokenConstants;
|
import com.ruoyi.common.core.constant.TokenConstants;
|
||||||
import com.ruoyi.common.core.context.SecurityContextHolder;
|
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.exception.ServiceException;
|
||||||
import com.ruoyi.common.core.utils.JwtUtils;
|
import com.ruoyi.common.core.utils.JwtUtils;
|
||||||
import com.ruoyi.common.core.utils.ServletUtils;
|
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.common.security.service.TokenService;
|
||||||
import com.ruoyi.system.api.model.LoginUser;
|
import com.ruoyi.system.api.model.LoginUser;
|
||||||
import com.ruoyi.web.admin.config.CustomHttpServletRequest;
|
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.config.properties.IgnoreWhiteProperties;
|
||||||
|
import com.ruoyi.web.admin.service.ValidateCodeService;
|
||||||
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
|
|
||||||
|
|
@ -54,6 +59,22 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
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
|
@Override
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
@ -66,6 +87,27 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
String requestURI = request.getRequestURI();
|
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())) {
|
if (!StringUtils.matches(requestURI, ignoreWhite.getWhites())) {
|
||||||
|
|
||||||
|
|
@ -83,7 +125,6 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
boolean islogin = redisService.hasKey(getTokenKey(userkey));
|
boolean islogin = redisService.hasKey(getTokenKey(userkey));
|
||||||
if (!islogin) {
|
if (!islogin) {
|
||||||
throw new ServiceException("登录状态已过期");
|
throw new ServiceException("登录状态已过期");
|
||||||
|
|
||||||
}
|
}
|
||||||
String userid = JwtUtils.getUserId(claims);
|
String userid = JwtUtils.getUserId(claims);
|
||||||
String username = JwtUtils.getUserName(claims);
|
String username = JwtUtils.getUserName(claims);
|
||||||
|
|
@ -96,7 +137,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
SecurityContextHolder.setUserName(username);
|
SecurityContextHolder.setUserName(username);
|
||||||
SecurityContextHolder.setUserKey(userkey);
|
SecurityContextHolder.setUserKey(userkey);
|
||||||
|
|
||||||
CustomHttpServletRequest customHttpServletRequest = new CustomHttpServletRequest(request);
|
CustomHttpServletRequest customHttpServletRequest = new CustomHttpServletRequest(request);
|
||||||
// 设置用户信息到请求
|
// 设置用户信息到请求
|
||||||
addHeader(customHttpServletRequest, SecurityConstants.USER_KEY, userkey);
|
addHeader(customHttpServletRequest, SecurityConstants.USER_KEY, userkey);
|
||||||
addHeader(customHttpServletRequest, SecurityConstants.DETAILS_USER_ID, userid);
|
addHeader(customHttpServletRequest, SecurityConstants.DETAILS_USER_ID, userid);
|
||||||
|
|
@ -136,10 +177,8 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addHeader(CustomHttpServletRequest customHttpServletRequest, String name, Object value)
|
private void addHeader(CustomHttpServletRequest customHttpServletRequest, String name, Object value) {
|
||||||
{
|
if (value == null) {
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String valueStr = value.toString();
|
String valueStr = value.toString();
|
||||||
|
|
@ -147,6 +186,24 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
|
||||||
customHttpServletRequest.addHeader(name, valueEncode);
|
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…
Reference in New Issue