mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	reset 回滚 修改spring源码上下文持有者(存在数据未清理内存泄漏问题)
This commit is contained in:
		@@ -71,6 +71,7 @@ public class UserActionListener implements SaTokenListener {
 | 
			
		||||
        logininforEvent.setUsername(username);
 | 
			
		||||
        logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
 | 
			
		||||
        logininforEvent.setMessage(MessageUtils.message("user.login.success"));
 | 
			
		||||
        logininforEvent.setRequest(ServletUtils.getRequest());
 | 
			
		||||
        SpringUtils.context().publishEvent(logininforEvent);
 | 
			
		||||
        // 更新登录信息
 | 
			
		||||
        loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
 | 
			
		||||
 
 | 
			
		||||
@@ -19,10 +19,7 @@ import org.dromara.common.core.enums.LoginType;
 | 
			
		||||
import org.dromara.common.core.enums.TenantStatus;
 | 
			
		||||
import org.dromara.common.core.exception.ServiceException;
 | 
			
		||||
import org.dromara.common.core.exception.user.UserException;
 | 
			
		||||
import org.dromara.common.core.utils.DateUtils;
 | 
			
		||||
import org.dromara.common.core.utils.MessageUtils;
 | 
			
		||||
import org.dromara.common.core.utils.SpringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.*;
 | 
			
		||||
import org.dromara.common.log.event.LogininforEvent;
 | 
			
		||||
import org.dromara.common.mybatis.helper.DataPermissionHelper;
 | 
			
		||||
import org.dromara.common.redis.utils.RedisUtils;
 | 
			
		||||
@@ -142,6 +139,7 @@ public class SysLoginService {
 | 
			
		||||
        logininforEvent.setUsername(username);
 | 
			
		||||
        logininforEvent.setStatus(status);
 | 
			
		||||
        logininforEvent.setMessage(message);
 | 
			
		||||
        logininforEvent.setRequest(ServletUtils.getRequest());
 | 
			
		||||
        SpringUtils.context().publishEvent(logininforEvent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import org.dromara.common.core.exception.user.CaptchaException;
 | 
			
		||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
			
		||||
import org.dromara.common.core.exception.user.UserException;
 | 
			
		||||
import org.dromara.common.core.utils.MessageUtils;
 | 
			
		||||
import org.dromara.common.core.utils.ServletUtils;
 | 
			
		||||
import org.dromara.common.core.utils.SpringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.log.event.LogininforEvent;
 | 
			
		||||
@@ -107,6 +108,7 @@ public class SysRegisterService {
 | 
			
		||||
        logininforEvent.setUsername(username);
 | 
			
		||||
        logininforEvent.setStatus(status);
 | 
			
		||||
        logininforEvent.setMessage(message);
 | 
			
		||||
        logininforEvent.setRequest(ServletUtils.getRequest());
 | 
			
		||||
        SpringUtils.context().publishEvent(logininforEvent);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -99,13 +99,6 @@
 | 
			
		||||
            <artifactId>transmittable-thread-local</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>jakarta.faces</groupId>
 | 
			
		||||
            <artifactId>jakarta.faces-api</artifactId>
 | 
			
		||||
            <version>4.1.0</version>
 | 
			
		||||
            <optional>true</optional>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
</project>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,161 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2002-2023 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      https://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.web.context.request;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.ttl.TransmittableThreadLocal;
 | 
			
		||||
import jakarta.faces.context.FacesContext;
 | 
			
		||||
import org.springframework.lang.Nullable;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Holder class to expose the web request in the form of a thread-bound
 | 
			
		||||
 * {@link RequestAttributes} object. The request will be inherited
 | 
			
		||||
 * by any child threads spawned by the current thread if the
 | 
			
		||||
 * {@code inheritable} flag is set to {@code true}.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>Use {@link RequestContextListener} or
 | 
			
		||||
 * {@link org.springframework.web.filter.RequestContextFilter} to expose
 | 
			
		||||
 * the current web request. Note that
 | 
			
		||||
 * already exposes the current request by default.
 | 
			
		||||
 *
 | 
			
		||||
 * 修改 spring 上下文存储方式 将 ThreadLocal 替换为 TransmittableThreadLocal
 | 
			
		||||
 * 支持线程上下文切换变量传递 异步获取 spring 上下文
 | 
			
		||||
 *
 | 
			
		||||
 * @author Juergen Hoeller
 | 
			
		||||
 * @author Rod Johnson
 | 
			
		||||
 * @since 2.0
 | 
			
		||||
 * @see RequestContextListener
 | 
			
		||||
 * @see org.springframework.web.filter.RequestContextFilter
 | 
			
		||||
 */
 | 
			
		||||
public abstract class RequestContextHolder {
 | 
			
		||||
 | 
			
		||||
	private static final boolean jsfPresent =
 | 
			
		||||
			ClassUtils.isPresent("jakarta.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
 | 
			
		||||
 | 
			
		||||
    // ThreadLocal 替换为 TransmittableThreadLocal
 | 
			
		||||
	private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
 | 
			
		||||
			new TransmittableThreadLocal<>();
 | 
			
		||||
 | 
			
		||||
	private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
 | 
			
		||||
			new TransmittableThreadLocal<>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Reset the RequestAttributes for the current thread.
 | 
			
		||||
	 */
 | 
			
		||||
	public static void resetRequestAttributes() {
 | 
			
		||||
		requestAttributesHolder.remove();
 | 
			
		||||
		inheritableRequestAttributesHolder.remove();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Bind the given RequestAttributes to the current thread,
 | 
			
		||||
	 * <i>not</i> exposing it as inheritable for child threads.
 | 
			
		||||
	 * @param attributes the RequestAttributes to expose
 | 
			
		||||
	 * @see #setRequestAttributes(RequestAttributes, boolean)
 | 
			
		||||
	 */
 | 
			
		||||
	public static void setRequestAttributes(@Nullable RequestAttributes attributes) {
 | 
			
		||||
		setRequestAttributes(attributes, false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Bind the given RequestAttributes to the current thread.
 | 
			
		||||
	 * @param attributes the RequestAttributes to expose,
 | 
			
		||||
	 * or {@code null} to reset the thread-bound context
 | 
			
		||||
	 * @param inheritable whether to expose the RequestAttributes as inheritable
 | 
			
		||||
	 * for child threads (using an {@link InheritableThreadLocal})
 | 
			
		||||
	 */
 | 
			
		||||
	public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
 | 
			
		||||
		if (attributes == null) {
 | 
			
		||||
			resetRequestAttributes();
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			if (inheritable) {
 | 
			
		||||
				inheritableRequestAttributesHolder.set(attributes);
 | 
			
		||||
				requestAttributesHolder.remove();
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				requestAttributesHolder.set(attributes);
 | 
			
		||||
				inheritableRequestAttributesHolder.remove();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return the RequestAttributes currently bound to the thread.
 | 
			
		||||
	 * @return the RequestAttributes currently bound to the thread,
 | 
			
		||||
	 * or {@code null} if none bound
 | 
			
		||||
	 */
 | 
			
		||||
	@Nullable
 | 
			
		||||
	public static RequestAttributes getRequestAttributes() {
 | 
			
		||||
		RequestAttributes attributes = requestAttributesHolder.get();
 | 
			
		||||
		if (attributes == null) {
 | 
			
		||||
			attributes = inheritableRequestAttributesHolder.get();
 | 
			
		||||
		}
 | 
			
		||||
		return attributes;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Return the RequestAttributes currently bound to the thread.
 | 
			
		||||
	 * <p>Exposes the previously bound RequestAttributes instance, if any.
 | 
			
		||||
	 * Falls back to the current JSF FacesContext, if any.
 | 
			
		||||
	 * @return the RequestAttributes currently bound to the thread
 | 
			
		||||
	 * @throws IllegalStateException if no RequestAttributes object
 | 
			
		||||
	 * is bound to the current thread
 | 
			
		||||
	 * @see #setRequestAttributes
 | 
			
		||||
	 * @see ServletRequestAttributes
 | 
			
		||||
	 * @see FacesRequestAttributes
 | 
			
		||||
	 * @see jakarta.faces.context.FacesContext#getCurrentInstance()
 | 
			
		||||
	 */
 | 
			
		||||
	public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
 | 
			
		||||
		RequestAttributes attributes = getRequestAttributes();
 | 
			
		||||
		if (attributes == null) {
 | 
			
		||||
			if (jsfPresent) {
 | 
			
		||||
				attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
 | 
			
		||||
			}
 | 
			
		||||
			if (attributes == null) {
 | 
			
		||||
				throw new IllegalStateException("No thread-bound request found: " +
 | 
			
		||||
						"Are you referring to request attributes outside of an actual web request, " +
 | 
			
		||||
						"or processing a request outside of the originally receiving thread? " +
 | 
			
		||||
						"If you are actually operating within a web request and still receive this message, " +
 | 
			
		||||
						"your code is probably running outside of DispatcherServlet: " +
 | 
			
		||||
						"In this case, use RequestContextListener or RequestContextFilter to expose the current request.");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return attributes;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Inner class to avoid hard-coded JSF dependency.
 | 
			
		||||
 	 */
 | 
			
		||||
	private static class FacesRequestAttributesFactory {
 | 
			
		||||
 | 
			
		||||
		@Nullable
 | 
			
		||||
		public static RequestAttributes getFacesRequestAttributes() {
 | 
			
		||||
			try {
 | 
			
		||||
				FacesContext facesContext = FacesContext.getCurrentInstance();
 | 
			
		||||
				return (facesContext != null ? new FacesRequestAttributes(facesContext) : null);
 | 
			
		||||
			}
 | 
			
		||||
			catch (NoClassDefFoundError err) {
 | 
			
		||||
				// typically for com/sun/faces/util/Util if only the JSF API jar is present
 | 
			
		||||
				return null;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -13,6 +13,7 @@ import org.aspectj.lang.annotation.AfterReturning;
 | 
			
		||||
import org.aspectj.lang.annotation.AfterThrowing;
 | 
			
		||||
import org.aspectj.lang.annotation.Aspect;
 | 
			
		||||
import org.aspectj.lang.annotation.Before;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
			
		||||
import org.dromara.common.core.utils.ServletUtils;
 | 
			
		||||
import org.dromara.common.core.utils.SpringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
@@ -20,6 +21,7 @@ import org.dromara.common.json.utils.JsonUtils;
 | 
			
		||||
import org.dromara.common.log.annotation.Log;
 | 
			
		||||
import org.dromara.common.log.enums.BusinessStatus;
 | 
			
		||||
import org.dromara.common.log.event.OperLogEvent;
 | 
			
		||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
			
		||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
			
		||||
import org.springframework.http.HttpMethod;
 | 
			
		||||
import org.springframework.validation.BindingResult;
 | 
			
		||||
@@ -86,6 +88,16 @@ public class LogAspect {
 | 
			
		||||
 | 
			
		||||
            // *========数据库日志=========*//
 | 
			
		||||
            OperLogEvent operLog = new OperLogEvent();
 | 
			
		||||
            operLog.setTenantId(LoginHelper.getTenantId());
 | 
			
		||||
            operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
 | 
			
		||||
            // 请求的地址
 | 
			
		||||
            String ip = ServletUtils.getClientIP();
 | 
			
		||||
            operLog.setOperIp(ip);
 | 
			
		||||
            operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
 | 
			
		||||
            LoginUser loginUser = LoginHelper.getLoginUser();
 | 
			
		||||
            operLog.setOperName(loginUser.getUsername());
 | 
			
		||||
            operLog.setDeptName(loginUser.getDeptName());
 | 
			
		||||
 | 
			
		||||
            if (e != null) {
 | 
			
		||||
                operLog.setStatus(BusinessStatus.FAIL.ordinal());
 | 
			
		||||
                operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
 | 
			
		||||
@@ -94,6 +106,8 @@ public class LogAspect {
 | 
			
		||||
            String className = joinPoint.getTarget().getClass().getName();
 | 
			
		||||
            String methodName = joinPoint.getSignature().getName();
 | 
			
		||||
            operLog.setMethod(className + "." + methodName + "()");
 | 
			
		||||
            // 设置请求方式
 | 
			
		||||
            operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
 | 
			
		||||
            // 处理设置注解上的参数
 | 
			
		||||
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
 | 
			
		||||
            // 设置消耗时间
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@ package org.dromara.common.log.event;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
 | 
			
		||||
import java.io.Serial;
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
 | 
			
		||||
@@ -37,6 +39,11 @@ public class LogininforEvent implements Serializable {
 | 
			
		||||
     */
 | 
			
		||||
    private String message;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 请求体
 | 
			
		||||
     */
 | 
			
		||||
    private HttpServletRequest request;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 其他参数
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ public class SysLogininforServiceImpl implements ISysLogininforService {
 | 
			
		||||
    @Async
 | 
			
		||||
    @EventListener
 | 
			
		||||
    public void recordLogininfor(LogininforEvent logininforEvent) {
 | 
			
		||||
        HttpServletRequest request = ServletUtils.getRequest();
 | 
			
		||||
        HttpServletRequest request = logininforEvent.getRequest();
 | 
			
		||||
        final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
 | 
			
		||||
        final String ip = ServletUtils.getClientIP(request);
 | 
			
		||||
        // 客户端信息
 | 
			
		||||
 
 | 
			
		||||
@@ -3,23 +3,18 @@ package org.dromara.system.service.impl;
 | 
			
		||||
import cn.hutool.core.util.ArrayUtil;
 | 
			
		||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
			
		||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
			
		||||
import org.dromara.common.core.utils.MapstructUtils;
 | 
			
		||||
import org.dromara.common.core.utils.ServletUtils;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.ip.AddressUtils;
 | 
			
		||||
import org.dromara.common.log.enums.BusinessStatus;
 | 
			
		||||
import org.dromara.common.log.event.OperLogEvent;
 | 
			
		||||
import org.dromara.common.mybatis.core.page.PageQuery;
 | 
			
		||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
 | 
			
		||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
			
		||||
import org.dromara.common.core.utils.StringUtils;
 | 
			
		||||
import org.dromara.common.core.utils.ip.AddressUtils;
 | 
			
		||||
import org.dromara.common.log.event.OperLogEvent;
 | 
			
		||||
import org.dromara.system.domain.SysOperLog;
 | 
			
		||||
import org.dromara.system.domain.bo.SysOperLogBo;
 | 
			
		||||
import org.dromara.system.domain.vo.SysOperLogVo;
 | 
			
		||||
import org.dromara.system.mapper.SysOperLogMapper;
 | 
			
		||||
import org.dromara.system.service.ISysOperLogService;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.context.event.EventListener;
 | 
			
		||||
import org.springframework.scheduling.annotation.Async;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
@@ -49,18 +44,6 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
 | 
			
		||||
    @EventListener
 | 
			
		||||
    public void recordOper(OperLogEvent operLogEvent) {
 | 
			
		||||
        SysOperLogBo operLog = MapstructUtils.convert(operLogEvent, SysOperLogBo.class);
 | 
			
		||||
        operLog.setTenantId(LoginHelper.getTenantId());
 | 
			
		||||
        operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
 | 
			
		||||
        // 请求的地址
 | 
			
		||||
        String ip = ServletUtils.getClientIP();
 | 
			
		||||
        operLog.setOperIp(ip);
 | 
			
		||||
        HttpServletRequest request = ServletUtils.getRequest();
 | 
			
		||||
        operLog.setOperUrl(StringUtils.substring(request.getRequestURI(), 0, 255));
 | 
			
		||||
        LoginUser loginUser = LoginHelper.getLoginUser();
 | 
			
		||||
        operLog.setOperName(loginUser.getUsername());
 | 
			
		||||
        operLog.setDeptName(loginUser.getDeptName());
 | 
			
		||||
        // 设置请求方式
 | 
			
		||||
        operLog.setRequestMethod(request.getMethod());
 | 
			
		||||
        // 远程查询操作地点
 | 
			
		||||
        operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp()));
 | 
			
		||||
        insertOperlog(operLog);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user