mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-13 10:35:07 +08:00
Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/activiti
Conflicts: sql/ruoyi-vue-pro.sql yudao-admin-server/src/main/resources/application.yaml yudao-admin-server/src/main/resources/mybatis-config/mybatis-config.xml yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.framework.security.core;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
@ -28,14 +29,6 @@ public class LoginUser implements UserDetails {
|
||||
* 关联 {@link UserTypeEnum}
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 部门编号
|
||||
*/
|
||||
private Long deptId;
|
||||
/**
|
||||
* 角色编号数组
|
||||
*/
|
||||
private Set<Long> roleIds;
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
@ -53,20 +46,39 @@ public class LoginUser implements UserDetails {
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
|
||||
// ========== UserTypeEnum.ADMIN 独有字段 ==========
|
||||
// TODO 芋艿:可以通过定义一个 Map<String, String> exts 的方式,去除管理员的字段。不过这样会导致系统比较复杂,所以暂时不去掉先;
|
||||
/**
|
||||
* 角色编号数组
|
||||
*/
|
||||
private Set<Long> roleIds;
|
||||
/**
|
||||
* 部门编号
|
||||
*/
|
||||
private Long deptId;
|
||||
/**
|
||||
* 所属岗位
|
||||
*/
|
||||
private Set<Long> postIds;
|
||||
|
||||
/**
|
||||
* group 目前指岗位代替
|
||||
*/
|
||||
// TODO jason:这个字段,改成 postCodes 明确更好哈
|
||||
private List<String> groups;
|
||||
|
||||
|
||||
// TODO @芋艿:怎么去掉 deptId
|
||||
// ========== 上下文 ==========
|
||||
/**
|
||||
* 上下文字段,不进行持久化
|
||||
*
|
||||
* 1. 用于基于 LoginUser 维度的临时缓存
|
||||
*/
|
||||
@JsonIgnore
|
||||
private Map<String, Object> context;
|
||||
|
||||
@Override
|
||||
@JsonIgnore// 避免序列化
|
||||
@ -91,6 +103,7 @@ public class LoginUser implements UserDetails {
|
||||
List<GrantedAuthority> list = new ArrayList<>(1);
|
||||
// 设置 ROLE_ACTIVITI_USER 角色,保证 activiti7 在 Security 验证时,可以通过。参考 https://juejin.cn/post/6972369247041224712 文章
|
||||
// TODO 芋艿:这里估计得优化下
|
||||
// TODO @芋艿:看看有没更优化的方案
|
||||
list.add(new SimpleGrantedAuthority("ROLE_ACTIVITI_USER"));
|
||||
return list;
|
||||
}
|
||||
@ -113,4 +126,17 @@ public class LoginUser implements UserDetails {
|
||||
return true; // 返回 true,不依赖 Spring Security 判断
|
||||
}
|
||||
|
||||
// ========== 上下文 ==========
|
||||
|
||||
public void setContext(String key, Object value) {
|
||||
if (context == null) {
|
||||
context = new HashMap<>();
|
||||
}
|
||||
context.put(key, value);
|
||||
}
|
||||
|
||||
public <T> T getContext(String key, Class<T> type) {
|
||||
return MapUtil.get(context, key, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -15,14 +15,16 @@ import lombok.Getter;
|
||||
public enum DataScopeEnum {
|
||||
|
||||
ALL(1), // 全部数据权限
|
||||
|
||||
DEPT_CUSTOM(2), // 指定部门数据权限
|
||||
DEPT_ONLY(3), // 部门数据权限
|
||||
DEPT_AND_CHILD(4), // 部门及以下数据权限
|
||||
DEPT_SELF(5); // 仅本人数据权限
|
||||
|
||||
SELF(5); // 仅本人数据权限
|
||||
|
||||
/**
|
||||
* 范围
|
||||
*/
|
||||
private final Integer score;
|
||||
private final Integer scope;
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.framework.security.core.util;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
@ -11,6 +12,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -93,13 +95,17 @@ 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());
|
||||
}
|
||||
// TODO @芋道源码 该值被赋值给 user task 中assignee , username 显示更直白一点
|
||||
// TODO @jason:有办法设置 userId,然后 activiti 有地方读取到 username 么?毕竟 username 只是 User 的登陆账号,并不能绝对像 userId 代表一个用户
|
||||
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
|
||||
|
Reference in New Issue
Block a user