1. 增加 Job 的多租户的能力

This commit is contained in:
YunaiV
2021-12-05 10:44:17 +08:00
parent 535d3c9c01
commit 6cd9b3bf7e
19 changed files with 327 additions and 10 deletions

View File

@ -1,15 +1,18 @@
package cn.iocoder.yudao.framework.security.config;
import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect;
import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy;
import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter;
import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;
import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;
import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
@ -85,4 +88,17 @@ public class YudaoSecurityAutoConfiguration {
return new JWTAuthenticationTokenFilter(securityProperties, securityFrameworkService, globalExceptionHandler);
}
/**
* 声明调用 {@link SecurityContextHolder#setStrategyName(String)} 方法,
* 设置使用 {@link TransmittableThreadLocalSecurityContextHolderStrategy} 作为 Security 的上下文策略
*/
@Bean
public MethodInvokingFactoryBean securityContextHolderMethodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(SecurityContextHolder.class);
methodInvokingFactoryBean.setTargetMethod("setStrategyName");
methodInvokingFactoryBean.setArguments(TransmittableThreadLocalSecurityContextHolderStrategy.class.getName());
return methodInvokingFactoryBean;
}
}

View File

@ -0,0 +1,48 @@
package cn.iocoder.yudao.framework.security.core.context;
import com.alibaba.ttl.TransmittableThreadLocal;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.util.Assert;
/**
* 基于 TransmittableThreadLocal 实现的 Security Context 持有者策略
* 目的是,避免 @Async 等异步执行时,原生 ThreadLocal 的丢失问题
*
* @author 芋道源码
*/
public class TransmittableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
/**
* 使用 TransmittableThreadLocal 作为上下文
*/
private static final ThreadLocal<SecurityContext> contextHolder = new TransmittableThreadLocal<>();
@Override
public void clearContext() {
contextHolder.remove();
}
@Override
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
@Override
public void setContext(SecurityContext context) {
Assert.notNull(context, "Only non-null SecurityContext instances are permitted");
contextHolder.set(context);
}
@Override
public SecurityContext createEmptyContext() {
return new SecurityContextImpl();
}
}

View File

@ -95,13 +95,14 @@ public class SecurityFrameworkUtils {
loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 设置到上下文
//何时调用 SecurityContextHolder.clearContext. spring security filter 应该会调用 clearContext
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号;
// 原因是Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息
WebFrameworkUtils.setLoginUserId(request, loginUser.getId());
WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType());
// TODO @jason使用 userId 会不会更合适哈?
// TODO @芋艿activiti 需要使用 ttl 上下文
// TODO @jason清理问题
if (Objects.equals(UserTypeEnum.ADMIN.getValue(), loginUser.getUserType())) {
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
}