From e2c4a2405ea6a202795b31b0b7bb3c01cd7d7d1b Mon Sep 17 00:00:00 2001 From: wangxiang <1827945911@qq.com> Date: Sat, 16 Sep 2023 15:20:09 +0800 Subject: [PATCH] init: oot --- .idea/.gitignore | 8 + .idea/compiler.xml | 13 + .idea/dictionaries | 6 + .idea/encodings.xml | 8 + .idea/inspectionProfiles/Project_Default.xml | 8 + .idea/jarRepositories.xml | 30 + .idea/misc.xml | 15 + pom.xml | 49 ++ .../kicc/common/core/enums/CasSystemEnum.java | 43 ++ .../kicc/common/core/enums/ExceptionEnum.java | 35 ++ .../kicc/common/data/entity/CasUser.java | 186 +++++++ .../kicc/common/data/entity/KicsUser.java | 70 +++ .../annotation/EnableKiccResourceServer.java | 30 + .../security/common/annotation/Inner.java | 30 + .../common/aspect/SecurityInnerAspect.java | 59 ++ .../ResourceServerAutoConfiguration.java | 50 ++ .../SecurityMessageSourceConfiguration.java | 31 ++ .../config/TokenStoreAutoConfiguration.java | 28 + .../common/constant/SecurityConstants.java | 145 +++++ .../common/exception/KiccAuth2Exception.java | 33 ++ .../UnConfiguredUserDataException.java | 33 ++ .../common/exp/KiccBearerTokenExtractor.java | 38 ++ .../KiccLocalResourceServerTokenServices.java | 118 ++++ .../KiccResourceServerConfigurerAdapter.java | 76 +++ .../KiccSecurityBeanDefinitionRegistrar.java | 41 ++ .../common/exp/PermissionService.java | 59 ++ .../common/exp/PermitAllUrlProperties.java | 72 +++ .../exp/ResourceAuthExceptionEntryPoint.java | 59 ++ .../common/override/KiccRedisTokenStore.java | 518 ++++++++++++++++++ .../jackson2/SimpleGrantedAuthorityMixin.java | 32 ++ .../security/common/util/SecurityUtils.java | 178 ++++++ .../common/core/enums/CasSystemEnum.class | Bin 0 -> 1570 bytes .../common/core/enums/ExceptionEnum.class | Bin 0 -> 1848 bytes .../kicc/common/data/entity/CasUser.class | Bin 0 -> 13702 bytes .../kicc/common/data/entity/KicsUser.class | Bin 0 -> 13586 bytes .../annotation/EnableKiccResourceServer.class | Bin 0 -> 947 bytes .../security/common/annotation/Inner.class | Bin 0 -> 548 bytes .../common/aspect/SecurityInnerAspect.class | Bin 0 -> 2884 bytes .../ResourceServerAutoConfiguration.class | Bin 0 -> 2405 bytes .../SecurityMessageSourceConfiguration.class | Bin 0 -> 1472 bytes .../config/TokenStoreAutoConfiguration.class | Bin 0 -> 1187 bytes .../common/exception/KiccAuth2Exception.class | Bin 0 -> 898 bytes .../UnConfiguredUserDataException.class | Bin 0 -> 1012 bytes .../common/exp/KiccBearerTokenExtractor.class | Bin 0 -> 2402 bytes ...KiccLocalResourceServerTokenServices.class | Bin 0 -> 8470 bytes .../KiccResourceServerConfigurerAdapter.class | Bin 0 -> 7065 bytes .../KiccSecurityBeanDefinitionRegistrar.class | Bin 0 -> 1891 bytes .../common/exp/PermissionService.class | Bin 0 -> 3278 bytes .../common/exp/PermitAllUrlProperties.class | Bin 0 -> 5911 bytes .../exp/ResourceAuthExceptionEntryPoint.class | Bin 0 -> 3154 bytes .../common/override/KiccRedisTokenStore.class | Bin 0 -> 19353 bytes .../SimpleGrantedAuthorityMixin.class | Bin 0 -> 1148 bytes .../security/common/util/SecurityUtils.class | Bin 0 -> 10468 bytes target/kics-common-security.jar | Bin 0 -> 47783 bytes target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 22 + .../compile/default-compile/inputFiles.lst | 22 + 57 files changed, 2150 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/compiler.xml create mode 100644 .idea/dictionaries create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 .idea/misc.xml create mode 100755 pom.xml create mode 100644 src/main/java/com/cloud/kicc/common/core/enums/CasSystemEnum.java create mode 100644 src/main/java/com/cloud/kicc/common/core/enums/ExceptionEnum.java create mode 100644 src/main/java/com/cloud/kicc/common/data/entity/CasUser.java create mode 100644 src/main/java/com/cloud/kicc/common/data/entity/KicsUser.java create mode 100644 src/main/java/com/klab/security/common/annotation/EnableKiccResourceServer.java create mode 100644 src/main/java/com/klab/security/common/annotation/Inner.java create mode 100644 src/main/java/com/klab/security/common/aspect/SecurityInnerAspect.java create mode 100644 src/main/java/com/klab/security/common/config/ResourceServerAutoConfiguration.java create mode 100644 src/main/java/com/klab/security/common/config/SecurityMessageSourceConfiguration.java create mode 100644 src/main/java/com/klab/security/common/config/TokenStoreAutoConfiguration.java create mode 100644 src/main/java/com/klab/security/common/constant/SecurityConstants.java create mode 100644 src/main/java/com/klab/security/common/exception/KiccAuth2Exception.java create mode 100644 src/main/java/com/klab/security/common/exception/UnConfiguredUserDataException.java create mode 100644 src/main/java/com/klab/security/common/exp/KiccBearerTokenExtractor.java create mode 100644 src/main/java/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.java create mode 100644 src/main/java/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.java create mode 100644 src/main/java/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.java create mode 100644 src/main/java/com/klab/security/common/exp/PermissionService.java create mode 100644 src/main/java/com/klab/security/common/exp/PermitAllUrlProperties.java create mode 100644 src/main/java/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.java create mode 100644 src/main/java/com/klab/security/common/override/KiccRedisTokenStore.java create mode 100644 src/main/java/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.java create mode 100644 src/main/java/com/klab/security/common/util/SecurityUtils.java create mode 100644 target/classes/com/cloud/kicc/common/core/enums/CasSystemEnum.class create mode 100644 target/classes/com/cloud/kicc/common/core/enums/ExceptionEnum.class create mode 100644 target/classes/com/cloud/kicc/common/data/entity/CasUser.class create mode 100644 target/classes/com/cloud/kicc/common/data/entity/KicsUser.class create mode 100644 target/classes/com/klab/security/common/annotation/EnableKiccResourceServer.class create mode 100644 target/classes/com/klab/security/common/annotation/Inner.class create mode 100644 target/classes/com/klab/security/common/aspect/SecurityInnerAspect.class create mode 100644 target/classes/com/klab/security/common/config/ResourceServerAutoConfiguration.class create mode 100644 target/classes/com/klab/security/common/config/SecurityMessageSourceConfiguration.class create mode 100644 target/classes/com/klab/security/common/config/TokenStoreAutoConfiguration.class create mode 100644 target/classes/com/klab/security/common/exception/KiccAuth2Exception.class create mode 100644 target/classes/com/klab/security/common/exception/UnConfiguredUserDataException.class create mode 100644 target/classes/com/klab/security/common/exp/KiccBearerTokenExtractor.class create mode 100644 target/classes/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.class create mode 100644 target/classes/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.class create mode 100644 target/classes/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.class create mode 100644 target/classes/com/klab/security/common/exp/PermissionService.class create mode 100644 target/classes/com/klab/security/common/exp/PermitAllUrlProperties.class create mode 100644 target/classes/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.class create mode 100644 target/classes/com/klab/security/common/override/KiccRedisTokenStore.class create mode 100644 target/classes/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.class create mode 100644 target/classes/com/klab/security/common/util/SecurityUtils.class create mode 100644 target/kics-common-security.jar create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..6b2a714 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries b/.idea/dictionaries new file mode 100644 index 0000000..756544e --- /dev/null +++ b/.idea/dictionaries @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..c08b794 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..9c484ac --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..1f6bd13 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..ab7049d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100755 index 0000000..0a39cff --- /dev/null +++ b/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + com.kics + ${project.artifactId} + 1.0.0 + kics-common-security + jar + + + + com.cloud + kicc-common-data + 1.0.0 + + + org.springframework.boot + spring-boot-starter-aop + 2.6.3 + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + 2.1.8.RELEASE + + + + org.projectlombok + lombok + provided + + + + + + + + org.springframework.boot + spring-boot-dependencies + 2.6.3 + pom + import + + + + + diff --git a/src/main/java/com/cloud/kicc/common/core/enums/CasSystemEnum.java b/src/main/java/com/cloud/kicc/common/core/enums/CasSystemEnum.java new file mode 100644 index 0000000..b03c486 --- /dev/null +++ b/src/main/java/com/cloud/kicc/common/core/enums/CasSystemEnum.java @@ -0,0 +1,43 @@ +package com.cloud.kicc.common.core.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + *

+ * cas系统枚举 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/16 + */ +@Getter +@RequiredArgsConstructor +public enum CasSystemEnum { + + /** + * sso认证系统 + */ + KICC("KICC", "主kicc系统"), + + /** + * 子系统1 + */ + KICS("KICS", "子系统1"), + + /** + * 子系统2 + */ + KLAB("KLAB", "子系统2"); + + /** + * 名称 + */ + private final String name; + + /** + * 描述 + */ + private final String description; + +} diff --git a/src/main/java/com/cloud/kicc/common/core/enums/ExceptionEnum.java b/src/main/java/com/cloud/kicc/common/core/enums/ExceptionEnum.java new file mode 100644 index 0000000..48f31df --- /dev/null +++ b/src/main/java/com/cloud/kicc/common/core/enums/ExceptionEnum.java @@ -0,0 +1,35 @@ +package com.cloud.kicc.common.core.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + *

+ * API错误页面响应状态枚举 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/16 + */ +@Getter +@RequiredArgsConstructor +public enum ExceptionEnum { + + UNAUTHORIZED_ACCESS(401, "禁止访问"), + PAGE_NOT_ACCESS(403, "页面无法访问"), + PAGE_NOT_FOUND(404, "网页未找到"), + ERROR(500, "错误"), + NET_WORK_ERROR(10000, "前端Js错误"), + PAGE_NOT_DATA(10100, "无数据页面"); + + /** + * 状态 + */ + private final int value; + + /** + * 描述 + */ + private final String description; + +} diff --git a/src/main/java/com/cloud/kicc/common/data/entity/CasUser.java b/src/main/java/com/cloud/kicc/common/data/entity/CasUser.java new file mode 100644 index 0000000..1ec8cc8 --- /dev/null +++ b/src/main/java/com/cloud/kicc/common/data/entity/CasUser.java @@ -0,0 +1,186 @@ +package com.cloud.kicc.common.data.entity; + +import com.cloud.kicc.common.core.enums.CasSystemEnum; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + *

+ * CAS统一认证用户数据 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/16 + */ +@Setter +@Getter +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class CasUser extends User { + + private static final long serialVersionUID = 1L; + + /** 用户ID */ + private String id; + + /** 昵称 */ + private String nickName; + + /** 邮箱 */ + private String email; + + /** 手机号 */ + private String phone; + + /** 性别 */ + private String sex; + + /** 头像地址 */ + private String avatar; + + /** 最后登陆ip */ + private String loginIp; + + /** 最后登陆时间 */ + private LocalDateTime loginTime; + + /** 状态 */ + private String ssoStatus; + + /** 创建ID */ + private String ssoCreateById; + + /** 创建人 */ + private String ssoCreateByName; + + /** 创建时间 */ + private LocalDateTime ssoCreateTime; + + /** 更新ID */ + private String ssoUpdateById; + + /** 更新人 */ + private String ssoUpdateByName; + + /** 更新时间 */ + private LocalDateTime ssoUpdateTime; + + /** 备注 */ + private String remarks; + + /** 角色ID */ + private String roleId; + + /** 多租户ID */ + private String tenantId; + + /** sso扩展信息 */ + private Map exPrincipals = new ConcurrentHashMap<>(3); + + + public CasUser(String username, String password, Collection authorities) { + super(username, password, authorities); + } + + public CasUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + } + + public CasUser(String username, + String password, + boolean enabled, + boolean accountNonExpired, + boolean credentialsNonExpired, + boolean accountNonLocked, + Collection authorities, + String id, + String nickName, + String email, + String phone, + String sex, + String avatar, + String loginIp, + LocalDateTime loginTime, + String ssoStatus, + String ssoCreateById, + String ssoCreateByName, + LocalDateTime ssoCreateTime, + String ssoUpdateById, + String ssoUpdateByName, + LocalDateTime ssoUpdateTime, + String remarks) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.id = id; + this.nickName = nickName; + this.email = email; + this.phone = phone; + this.sex = sex; + this.avatar = avatar; + this.loginIp = loginIp; + this.loginTime = loginTime; + this.ssoStatus = ssoStatus; + this.ssoCreateById = ssoCreateById; + this.ssoCreateByName = ssoCreateByName; + this.ssoCreateTime = ssoCreateTime; + this.ssoUpdateById = ssoUpdateById; + this.ssoUpdateByName = ssoUpdateByName; + this.ssoUpdateTime = ssoUpdateTime; + this.remarks = remarks; + } + + public CasUser(String username, + String password, + boolean enabled, + boolean accountNonExpired, + boolean credentialsNonExpired, + boolean accountNonLocked, + Collection authorities, + String id, + String nickName, + String email, + String phone, + String sex, + String avatar, + String loginIp, + LocalDateTime loginTime, + String ssoStatus, + String ssoCreateById, + String ssoCreateByName, + LocalDateTime ssoCreateTime, + String ssoUpdateById, + String ssoUpdateByName, + LocalDateTime ssoUpdateTime, + String remarks, + String roleId, + String tenantId) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.id = id; + this.nickName = nickName; + this.email = email; + this.phone = phone; + this.sex = sex; + this.avatar = avatar; + this.loginIp = loginIp; + this.loginTime = loginTime; + this.ssoStatus = ssoStatus; + this.ssoCreateById = ssoCreateById; + this.ssoCreateByName = ssoCreateByName; + this.ssoCreateTime = ssoCreateTime; + this.ssoUpdateById = ssoUpdateById; + this.ssoUpdateByName = ssoUpdateByName; + this.ssoUpdateTime = ssoUpdateTime; + this.remarks = remarks; + this.roleId = roleId; + this.tenantId = tenantId; + } + +} diff --git a/src/main/java/com/cloud/kicc/common/data/entity/KicsUser.java b/src/main/java/com/cloud/kicc/common/data/entity/KicsUser.java new file mode 100644 index 0000000..c79a97a --- /dev/null +++ b/src/main/java/com/cloud/kicc/common/data/entity/KicsUser.java @@ -0,0 +1,70 @@ +package com.cloud.kicc.common.data.entity; + +import cn.hutool.core.util.ObjectUtil; +import com.cloud.kicc.common.core.constant.SecurityConstants; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import java.util.Date; +import java.util.List; + +@Getter +@Setter +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +public class KicsUser extends CasUser { + private String userId; + private String casUserId; + private String userCode; + private String loginName; + private String openId; + private Integer isIm; + private Integer isLogin; + private Date loginOutTime; + private String imageUrl; + private String pushId; + private String appUserId; + private Integer openState; + private String unionId; + private Integer userRole; + private Integer relationId; + private Integer detailRole; + private Integer createUserId; + private String createUserName; + private Date createDate; + private Integer modifyUserId; + private String modifyUserName; + private Date modifyDate; + private Integer state; + + // fixme: Extended Authorization Permissions 🚀 + private String[] permissions; + + public KicsUser() { + super(SecurityConstants.MOCK_USERNAME, SecurityConstants.MOCK_PASSWORD, AuthorityUtils.createAuthorityList()); + } + + @JsonCreator + public KicsUser(@JsonProperty("username") String username, + @JsonProperty("password") String password, + @JsonProperty("enabled") boolean enabled, + @JsonProperty("accountNonExpired") boolean accountNonExpired, + @JsonProperty("credentialsNonExpired") boolean credentialsNonExpired, + @JsonProperty("accountNonLocked") boolean accountNonLocked, + @JsonProperty("authorities") List authorities) { + super(ObjectUtil.defaultIfNull(username, SecurityConstants.MOCK_USERNAME), + ObjectUtil.defaultIfNull(password, SecurityConstants.MOCK_PASSWORD), + enabled, + accountNonExpired, + credentialsNonExpired, + accountNonLocked, + ObjectUtil.defaultIfNull(authorities, AuthorityUtils.createAuthorityList())); + } + +} diff --git a/src/main/java/com/klab/security/common/annotation/EnableKiccResourceServer.java b/src/main/java/com/klab/security/common/annotation/EnableKiccResourceServer.java new file mode 100644 index 0000000..2129054 --- /dev/null +++ b/src/main/java/com/klab/security/common/annotation/EnableKiccResourceServer.java @@ -0,0 +1,30 @@ +package com.klab.security.common.annotation; + +import com.klab.security.common.config.ResourceServerAutoConfiguration; +import com.klab.security.common.exp.KiccSecurityBeanDefinitionRegistrar; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Import; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; + +import java.lang.annotation.*; + +/** + *

+ * 扩展资源服务注解 + * 添加方法安全级别 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Documented +@Inherited +@EnableResourceServer +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@EnableGlobalMethodSecurity(prePostEnabled = true) +@Import({ ResourceServerAutoConfiguration.class, KiccSecurityBeanDefinitionRegistrar.class }) +public @interface EnableKiccResourceServer { + +} diff --git a/src/main/java/com/klab/security/common/annotation/Inner.java b/src/main/java/com/klab/security/common/annotation/Inner.java new file mode 100644 index 0000000..cac2721 --- /dev/null +++ b/src/main/java/com/klab/security/common/annotation/Inner.java @@ -0,0 +1,30 @@ +package com.klab.security.common.annotation; + +import java.lang.annotation.*; + +/** + *

+ * 服务调用不鉴权注解 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Inner { + + /** + * 是否AOP统一处理 + * @return false, true + */ + boolean value() default true; + + /** + * 为后续扩展做准备,需要特殊判空的字段(预留) + * @return {} + */ + String[] field() default {}; + +} diff --git a/src/main/java/com/klab/security/common/aspect/SecurityInnerAspect.java b/src/main/java/com/klab/security/common/aspect/SecurityInnerAspect.java new file mode 100644 index 0000000..59b5727 --- /dev/null +++ b/src/main/java/com/klab/security/common/aspect/SecurityInnerAspect.java @@ -0,0 +1,59 @@ +package com.klab.security.common.aspect; + +import cn.hutool.core.util.StrUtil; +import com.cloud.kicc.common.core.constant.SecurityConstants; +import com.klab.security.common.annotation.Inner; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.security.access.AccessDeniedException; + +import javax.servlet.http.HttpServletRequest; + +/** + *

+ * 内部接口调用注解切面处理 + * 设置是否是内部接口,不提供给外部访问 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Slf4j +@Aspect +@Configuration +@RequiredArgsConstructor + +public class SecurityInnerAspect implements Ordered { + + private final HttpServletRequest request; + + @SneakyThrows + @Around("@within(inner) || @annotation(inner)") + public Object around(ProceedingJoinPoint point, Inner inner) { + // 实际注入的inner实体由表达式后一个注解决定,即是方法上的@Inner注解实体,若方法上无@Inner注解,则获取类上的 + if (inner == null) { + Class clazz = point.getTarget().getClass(); + inner = AnnotationUtils.findAnnotation(clazz, Inner.class); + } + String header = request.getHeader(SecurityConstants.FROM); + // 设置是否是内部接口,不提供给外部访问 + if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_IN, header)) { + log.warn("访问接口 {} 没有权限", point.getSignature().getName()); + throw new AccessDeniedException("Access is denied"); + } + return point.proceed(); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE + 1; + } + +} diff --git a/src/main/java/com/klab/security/common/config/ResourceServerAutoConfiguration.java b/src/main/java/com/klab/security/common/config/ResourceServerAutoConfiguration.java new file mode 100644 index 0000000..1a5d82e --- /dev/null +++ b/src/main/java/com/klab/security/common/config/ResourceServerAutoConfiguration.java @@ -0,0 +1,50 @@ +package com.klab.security.common.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.klab.security.common.exp.*; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; + +/** + *

+ * 扩展资源服务器自动配置 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Configuration +@EnableConfigurationProperties(PermitAllUrlProperties.class) +public class ResourceServerAutoConfiguration { + + /** 校验接口是否存在权限 */ + @Bean("pms") + public PermissionService permissionService() { + return new PermissionService(); + } + + /** 对公开权限的请求获取token不进行校验 */ + @Bean + public KiccBearerTokenExtractor kiccBearerTokenExtractor(PermitAllUrlProperties urlProperties) { + return new KiccBearerTokenExtractor(urlProperties); + } + + /** 细粒化客户端认证异常处理 */ + @Bean + public ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint(ObjectMapper objectMapper) { + return new ResourceAuthExceptionEntryPoint(objectMapper); + } + + /** 扩展资源服务器令牌服务 */ + @Bean + @Primary + public ResourceServerTokenServices resourceServerTokenServices(TokenStore tokenStore, JdbcTemplate jdbcTemplate) { + return new KiccLocalResourceServerTokenServices(tokenStore, jdbcTemplate); + } + +} diff --git a/src/main/java/com/klab/security/common/config/SecurityMessageSourceConfiguration.java b/src/main/java/com/klab/security/common/config/SecurityMessageSourceConfiguration.java new file mode 100644 index 0000000..bef71d9 --- /dev/null +++ b/src/main/java/com/klab/security/common/config/SecurityMessageSourceConfiguration.java @@ -0,0 +1,31 @@ +package com.klab.security.common.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.context.MessageSource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import static org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type.SERVLET; + +/** + *

+ * 设置安全模块国际化配置 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Configuration +@ConditionalOnWebApplication(type = SERVLET) +public class SecurityMessageSourceConfiguration implements WebMvcConfigurer { + + @Bean + public MessageSource messageSource() { + ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.addBasenames("classpath:org/springframework/security/messages"); + return messageSource; + } + +} diff --git a/src/main/java/com/klab/security/common/config/TokenStoreAutoConfiguration.java b/src/main/java/com/klab/security/common/config/TokenStoreAutoConfiguration.java new file mode 100644 index 0000000..bc19e9e --- /dev/null +++ b/src/main/java/com/klab/security/common/config/TokenStoreAutoConfiguration.java @@ -0,0 +1,28 @@ +package com.klab.security.common.config; + +import com.cloud.kicc.common.core.constant.CacheConstants; +import com.klab.security.common.override.KiccRedisTokenStore; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.security.oauth2.provider.token.TokenStore; + +/** + *

+ * 令牌存储自动配置 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Configuration +public class TokenStoreAutoConfiguration { + + @Bean + public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory) { + KiccRedisTokenStore tokenStore = new KiccRedisTokenStore(redisConnectionFactory); + tokenStore.setPrefix(CacheConstants.OAUTH_ACCESS); + return tokenStore; + } + +} diff --git a/src/main/java/com/klab/security/common/constant/SecurityConstants.java b/src/main/java/com/klab/security/common/constant/SecurityConstants.java new file mode 100644 index 0000000..76de600 --- /dev/null +++ b/src/main/java/com/klab/security/common/constant/SecurityConstants.java @@ -0,0 +1,145 @@ +package com.klab.security.common.constant; + +/** + *

+ * 安全常量 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/29 + */ +public interface SecurityConstants { + + /** + * 角色前缀 + */ + String ROLE = "ROLE_"; + + /** + * 子系统sso角色权限查询声明 + */ + String SSO_PERMISSION_FIND_STATEMENT = "select distinct\n" + + "\tmenu_code \n" + + "from\n" + + "\tt_sys_role_menu sr\n" + + "\tleft join t_sys_menu sm \n" + + "\ton sr.menu_id = sm.menu_id\n" + + "where sm.state = 1 and sr.role_id in (select role_id from t_sys_role_user where user_id = ?) and coalesce(menu_code, '') <> ''"; + + String SSO_USER_FIND_STATEMENT = "select * from sso_enhanced_user_view where cas_user_id = ? and state = 1"; + + String SSO_USER_FIND_STATEMENT_BY_USER_ID = "select * from sso_enhanced_user_view where user_id = ? and state = 1"; + + /** + * 项目的license + */ + String PROJECT_LICENSE = "长沙康来生物有限公司"; + + /** + * 内部接口调用密钥 + */ + String FROM_IN = "kG8qA6qG1aP5aR3g"; + + /** + * 内部接口调用Key标志 + */ + String FROM = "from"; + + /** + * 请求header + */ + String HEADER_FROM_IN = FROM + "=" + FROM_IN; + + /** + * 默认登录URL + */ + String OAUTH_TOKEN_URL = "/oauth/token"; + + /** + * grant_type + */ + String REFRESH_TOKEN = "refresh_token"; + + /** + * 手机号登录 + */ + String APP = "app"; + + /** + * {bcrypt} 加密的特征码 + */ + String BCRYPT = "{bcrypt}"; + + /** + * sys_oauth_client_details 表的字段,不包括client_id、client_secret + */ + String CLIENT_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, " + + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, " + + "refresh_token_validity, additional_information, autoapprove"; + + /** + * JdbcClientDetailsService 查询语句 + */ + String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details"; + + /** + * 默认的查询语句 + */ + String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id"; + + /** + * 按条件client_id 查询 + */ + String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?"; + + /*** + * 资源服务器默认bean名称 + */ + String RESOURCE_SERVER_CONFIGURER = "resourceServerConfigurerAdapter"; + + /** + * 用户信息 + */ + String DETAILS_USER = "user_info"; + + /** + * 协议字段 + */ + String DETAILS_LICENSE = "license"; + + /** + * 验证码有效期,默认 60秒 + */ + long CODE_TIME = 60; + + /** + * 手机验证码长度 + */ + String PHONE_CODE_SIZE = "6"; + + /** + * 客户端模式 + */ + String CLIENT_CREDENTIALS = "client_credentials"; + + /** + * 客户端ID + */ + String CLIENT_ID = "clientId"; + + /** + * 绑定kicc康来内部多租户 + */ + String TENANT_ID = "1510456530575347712"; + + /** + * 模拟测试账户 + */ + String MOCK_USERNAME = "admin"; + + /** + * 模拟测试密码 + */ + String MOCK_PASSWORD = "kanglai123"; + +} diff --git a/src/main/java/com/klab/security/common/exception/KiccAuth2Exception.java b/src/main/java/com/klab/security/common/exception/KiccAuth2Exception.java new file mode 100644 index 0000000..a93a11b --- /dev/null +++ b/src/main/java/com/klab/security/common/exception/KiccAuth2Exception.java @@ -0,0 +1,33 @@ +package com.klab.security.common.exception; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.Getter; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; + +/** + *

+ * 自定义OAuth2Exception + *

+ * + * @Author: wangxiang4 + * @Date: 2022/2/17 + */ +public class KiccAuth2Exception extends OAuth2Exception { + + @Getter + private String errorCode; + + public KiccAuth2Exception(String msg) { + super(msg); + } + + public KiccAuth2Exception(String msg, Throwable t) { + super(msg, t); + } + + public KiccAuth2Exception(String msg, String errorCode) { + super(msg); + this.errorCode = errorCode; + } + +} diff --git a/src/main/java/com/klab/security/common/exception/UnConfiguredUserDataException.java b/src/main/java/com/klab/security/common/exception/UnConfiguredUserDataException.java new file mode 100644 index 0000000..ce9eeb8 --- /dev/null +++ b/src/main/java/com/klab/security/common/exception/UnConfiguredUserDataException.java @@ -0,0 +1,33 @@ +package com.klab.security.common.exception; + +import org.springframework.http.HttpStatus; + +/** + *

+ * 未配置用户数据 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/9/2 + */ +public class UnConfiguredUserDataException extends KiccAuth2Exception { + + public UnConfiguredUserDataException(String msg) { + super(msg); + } + + public UnConfiguredUserDataException(String msg, Throwable t) { + super(msg, t); + } + + @Override + public String getOAuth2ErrorCode() { + return "un_configured_user_data"; + } + + @Override + public int getHttpErrorCode() { + return HttpStatus.NETWORK_AUTHENTICATION_REQUIRED.value(); + } + +} diff --git a/src/main/java/com/klab/security/common/exp/KiccBearerTokenExtractor.java b/src/main/java/com/klab/security/common/exp/KiccBearerTokenExtractor.java new file mode 100644 index 0000000..19b34c9 --- /dev/null +++ b/src/main/java/com/klab/security/common/exp/KiccBearerTokenExtractor.java @@ -0,0 +1,38 @@ +package com.klab.security.common.exp; + +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import javax.servlet.http.HttpServletRequest; + +/** + *

+ * 重写 {@link BearerTokenExtractor} + * 对公开权限的请求不进行抓取校验直接返回null放过 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +public class KiccBearerTokenExtractor extends BearerTokenExtractor { + + private final PathMatcher pathMatcher; + + private final PermitAllUrlProperties urlProperties; + + public KiccBearerTokenExtractor(PermitAllUrlProperties urlProperties) { + this.urlProperties = urlProperties; + this.pathMatcher = new AntPathMatcher(); + } + + @Override + public Authentication extract(HttpServletRequest request) { + boolean match = urlProperties.getUrls().stream() + .anyMatch(url -> pathMatcher.match(url, request.getRequestURI())); + + return match ? null : super.extract(request); + } + +} diff --git a/src/main/java/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.java b/src/main/java/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.java new file mode 100644 index 0000000..99979e5 --- /dev/null +++ b/src/main/java/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.java @@ -0,0 +1,118 @@ +package com.klab.security.common.exp; + +import cn.hutool.json.JSONUtil; +import com.cloud.kicc.common.core.constant.SecurityConstants; +import com.cloud.kicc.common.core.enums.CasSystemEnum; +import com.cloud.kicc.common.core.jackson.KiccJavaTimeModule; +import com.cloud.kicc.common.data.entity.CasUser; +import com.cloud.kicc.common.data.entity.KicsUser; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.klab.security.common.exception.UnConfiguredUserDataException; +import com.klab.security.common.override.jackson2.SimpleGrantedAuthorityMixin; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.beans.BeanUtils; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 本地资源服务器令牌服务 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@RequiredArgsConstructor +public class KiccLocalResourceServerTokenServices implements ResourceServerTokenServices { + + private final TokenStore tokenStore; + + private final JdbcTemplate jdbcTemplate; + + @SneakyThrows + public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { + // 根据token加载身份验证 + OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken); + if (oAuth2Authentication == null) { + return null; + } + OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request(); + // 检测是否是属于认证的CasUser实体用户 + if (!(oAuth2Authentication.getPrincipal() instanceof CasUser)) { + return oAuth2Authentication; + } + CasUser casUser = (CasUser) oAuth2Authentication.getPrincipal(); + + // 设置SSO子系统扩展用户信息 + if (casUser.getExPrincipals().get(CasSystemEnum.KICS) == null) { + List userList = jdbcTemplate.query(com.klab.security.common.constant.SecurityConstants.SSO_USER_FIND_STATEMENT, new BeanPropertyRowMapper<>(KicsUser.class), casUser.getId()); + if (userList.isEmpty()) throw new UnConfiguredUserDataException("System user not found Contact your system administrator for configuration!"); + KicsUser user = userList.get(0); + List permissions = jdbcTemplate.queryForList(com.klab.security.common.constant.SecurityConstants.SSO_PERMISSION_FIND_STATEMENT, String.class, user.getUserId()); + List authorities = Arrays.stream(permissions.toArray(new String[0])) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + KicsUser kicsUser = new KicsUser( + casUser.getUsername(), + casUser.getPassword(), + casUser.isEnabled(), + casUser.isAccountNonExpired(), + casUser.isCredentialsNonExpired(), + casUser.isAccountNonLocked(), + authorities + ); + kicsUser.setPermissions(permissions.toArray(new String[0])); + BeanUtils.copyProperties(user, kicsUser); + casUser.getExPrincipals().put(CasSystemEnum.KICS, new ObjectMapper() + .registerModule(new KiccJavaTimeModule()) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .writeValueAsString(kicsUser)); + tokenStore.storeAccessToken(tokenStore.getAccessToken(oAuth2Authentication), oAuth2Authentication); + } + + // 覆盖casUser核心authorities + String str = casUser.getExPrincipals().get(CasSystemEnum.KICS); + if (!JSONUtil.isTypeJSON(str)) throw new UnConfiguredUserDataException("exPrincipals not json strings!"); + KicsUser kicsUser = new ObjectMapper() + .registerModule(new KiccJavaTimeModule()) + .addMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class) + .readValue(str, KicsUser.class); + CasUser exCasUser = new CasUser( + casUser.getUsername(), + SecurityConstants.MOCK_PASSWORD, + casUser.isEnabled(), + casUser.isAccountNonExpired(), + casUser.isCredentialsNonExpired(), + casUser.isAccountNonLocked(), + kicsUser.getAuthorities()); + BeanUtils.copyProperties(casUser, exCasUser); + + // 每次请求前都预先加载用户名密码身份验证令牌 + Authentication userAuthentication = new UsernamePasswordAuthenticationToken(exCasUser, "N/A", exCasUser.getAuthorities()); + OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, userAuthentication); + authentication.setAuthenticated(true); + return authentication; + } + + @Override + public OAuth2AccessToken readAccessToken(String accessToken) { + throw new UnsupportedOperationException("Not supported: read access token"); + } + +} diff --git a/src/main/java/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.java b/src/main/java/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.java new file mode 100644 index 0000000..a541b0c --- /dev/null +++ b/src/main/java/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.java @@ -0,0 +1,76 @@ +package com.klab.security.common.exp; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +/** + *

+ * 资源服务配置适配器 + * 覆盖内部不合适的实现,实现适配 + * 1. 支持remoteTokenServices 负载均衡,后期出现 + * 2. 支持 获取用户全部信息 + * 3. 接口对外暴露,不校验 Authentication Header 头 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Slf4j +public class KiccResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter { + + @Autowired + protected ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint; + + @Autowired + private PermitAllUrlProperties permitAllUrl; + + @Autowired + private KiccBearerTokenExtractor kiccBearerTokenExtractor; + + @Autowired + private ResourceServerTokenServices resourceServerTokenServices; + + /** + * 默认的配置,对外暴露 + * @param httpSecurity + */ + @Override + @SneakyThrows + public void configure(HttpSecurity httpSecurity) { + // 允许使用iframe 嵌套,避免swagger-ui 不被加载的问题 + httpSecurity.cors().and().headers().frameOptions().disable(); + // 设置所有的请求必须通过授权认证才可以访问 + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests(); + // 设置添加了系统内部调用注解,才对外提供访问,也可以理解SOA架构互相调用,以及配置了忽略url + permitAllUrl.getUrls().forEach(url -> registry.antMatchers(url).permitAll()); + registry.anyRequest().authenticated().and().csrf().disable(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.addAllowedOrigin("*"); // 允许所有来源 + configuration.addAllowedMethod("*"); // 允许所有请求方法 + configuration.addAllowedHeader("*"); // 允许所有请求头 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); // 对所有路径生效 + return source; + } + + @Override + public void configure(ResourceServerSecurityConfigurer resources) { + resources.authenticationEntryPoint(resourceAuthExceptionEntryPoint).tokenExtractor(kiccBearerTokenExtractor) + .tokenServices(resourceServerTokenServices); + } + +} diff --git a/src/main/java/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.java b/src/main/java/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.java new file mode 100644 index 0000000..63e163a --- /dev/null +++ b/src/main/java/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.java @@ -0,0 +1,41 @@ +package com.klab.security.common.exp; + +import com.klab.security.common.constant.SecurityConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +/** + *

+ * 安全 Bean 定义注册器 + * 加载自定义Bean + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Slf4j +public class KiccSecurityBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { + + /** + * 根据注解值动态注入资源服务器的相关属性 + * @param metadata 注解信息 + * @param registry 注册器 + */ + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { + // 覆盖资源认证服务配置适配器 + if (registry.isBeanNameInUse(SecurityConstants.RESOURCE_SERVER_CONFIGURER)) { + log.warn("本地存在资源服务器配置,覆盖默认配置:" + SecurityConstants.RESOURCE_SERVER_CONFIGURER); + return; + } + + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(KiccResourceServerConfigurerAdapter.class); + registry.registerBeanDefinition(SecurityConstants.RESOURCE_SERVER_CONFIGURER, beanDefinition); + + } + +} diff --git a/src/main/java/com/klab/security/common/exp/PermissionService.java b/src/main/java/com/klab/security/common/exp/PermissionService.java new file mode 100644 index 0000000..09b0760 --- /dev/null +++ b/src/main/java/com/klab/security/common/exp/PermissionService.java @@ -0,0 +1,59 @@ +package com.klab.security.common.exp; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.PatternMatchUtils; +import org.springframework.util.StringUtils; + +import java.util.Collection; + +/** + *

+ * 接口权限判断 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +public class PermissionService { + + /** + * 判断接口是否有任意xxx,xxx权限 + * @param permissions 权限 + * @return {boolean} + */ + public boolean hasPermission(String... permissions) { + if (ArrayUtil.isEmpty(permissions)) { + return false; + } + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + return false; + } + Collection authorities = authentication.getAuthorities(); + return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText) + .anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x)); + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 ,为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPerm(String permissions) { + if (StrUtil.isEmpty(permissions)) { + return false; + } + for (String permission : permissions.split(",")) { + if (permission != null && hasPermission(permission)) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/com/klab/security/common/exp/PermitAllUrlProperties.java b/src/main/java/com/klab/security/common/exp/PermitAllUrlProperties.java new file mode 100644 index 0000000..d89117f --- /dev/null +++ b/src/main/java/com/klab/security/common/exp/PermitAllUrlProperties.java @@ -0,0 +1,72 @@ +package com.klab.security.common.exp; + +import cn.hutool.core.util.ReUtil; +import com.klab.security.common.annotation.Inner; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; + +/** + *

+ * 资源服务器对外直接暴露URL,如果设置 contex-path 要特殊处理 + * 主要处理拿取加了Inner注解的方法上的url映射地址,以及配置文件中配置的security.oauth2.ignore.urls,进行对外开放不拦截 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@Slf4j +@ConfigurationProperties(prefix = "security.oauth2.ignore") +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware { + + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + @Getter + @Setter + private List urls = new ArrayList<>(); + + /** bean初始化后执行 */ + @Override + public void afterPropertiesSet() { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + // 处理所有controller请求映射方法 + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解,替代path variable 为 *,避免前端传递过来的参数变量被拦截 + Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class); + Optional.ofNullable(method).ifPresent(inner -> info.getPatternsCondition().getPatterns() + .forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, "*")))); + + // 获取类上边的注解,替代path variable 为 *,避免前端传递过来的参数变量被拦截 + Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class); + Optional.ofNullable(controller).ifPresent(inner -> info.getPatternsCondition().getPatterns() + .forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, "*")))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.applicationContext = context; + } + +} diff --git a/src/main/java/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.java b/src/main/java/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.java new file mode 100644 index 0000000..9d50c0c --- /dev/null +++ b/src/main/java/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.java @@ -0,0 +1,59 @@ +package com.klab.security.common.exp; + +import cn.hutool.http.HttpStatus; +import com.cloud.kicc.common.core.api.R; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.klab.security.common.exception.UnConfiguredUserDataException; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; + +/** + *

+ * 客户端认证异常处理 + * 可以根据 AuthenticationException 不同细化异常处理 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@RequiredArgsConstructor +public class ResourceAuthExceptionEntryPoint implements AuthenticationEntryPoint { + + private final ObjectMapper objectMapper; + + @Override + @SneakyThrows + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) { + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json; charset=utf-8"); + R result = new R<>(); + result.setCode(0); + response.setStatus(HttpStatus.HTTP_UNAUTHORIZED); + if (authException != null) { + result.setMsg("error"); + result.setData(authException.getMessage()); + } + + // 针对令牌过期返回特殊的 301 + if (authException instanceof InsufficientAuthenticationException) { + response.setStatus(1); + result.setMsg(""); + + // 未配置用户数据 + if (authException.getCause() instanceof UnConfiguredUserDataException) { + result.setMsg("error" + 1); + } + } + PrintWriter printWriter = response.getWriter(); + printWriter.append(objectMapper.writeValueAsString(result)); + } + +} diff --git a/src/main/java/com/klab/security/common/override/KiccRedisTokenStore.java b/src/main/java/com/klab/security/common/override/KiccRedisTokenStore.java new file mode 100644 index 0000000..81cb712 --- /dev/null +++ b/src/main/java/com/klab/security/common/override/KiccRedisTokenStore.java @@ -0,0 +1,518 @@ +package com.klab.security.common.override; + +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisZSetCommands; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; +import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy; +import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; +import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.*; + +/** + *

+ * @link + * 重写RedisTokenStore ,主要解决 #1814 oauth2中client_id_to_access数据膨胀问题 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +public class KiccRedisTokenStore implements TokenStore { + + private static final String ACCESS = "access:"; + + private static final String AUTH_TO_ACCESS = "auth_to_access:"; + + private static final String AUTH = "auth:"; + + private static final String REFRESH_AUTH = "refresh_auth:"; + + private static final String REFRESH = "refresh:"; + + private static final String REFRESH_TO_ACCESS = "refresh_to_access:"; + + private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access_z:"; + + private static final String UNAME_TO_ACCESS = "uname_to_access_z:"; + + private static final boolean springDataRedis_2_0 = ClassUtils.isPresent( + "org.springframework.data.redis.connection.RedisStandaloneConfiguration", + RedisTokenStore.class.getClassLoader()); + + private final RedisConnectionFactory connectionFactory; + + private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator(); + + private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy(); + + private String prefix = ""; + + private Method redisConnectionSet_2_0; + + public KiccRedisTokenStore(RedisConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + if (springDataRedis_2_0) { + this.loadRedisConnectionMethods_2_0(); + } + } + + public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) { + this.authenticationKeyGenerator = authenticationKeyGenerator; + } + + public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) { + this.serializationStrategy = serializationStrategy; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + private void loadRedisConnectionMethods_2_0() { + this.redisConnectionSet_2_0 = ReflectionUtils.findMethod(RedisConnection.class, "set", byte[].class, + byte[].class); + } + + private RedisConnection getConnection() { + return connectionFactory.getConnection(); + } + + private byte[] serialize(Object object) { + return serializationStrategy.serialize(object); + } + + private byte[] serializeKey(String object) { + return serialize(prefix + object); + } + + private OAuth2AccessToken deserializeAccessToken(byte[] bytes) { + return serializationStrategy.deserialize(bytes, OAuth2AccessToken.class); + } + + private OAuth2Authentication deserializeAuthentication(byte[] bytes) { + return serializationStrategy.deserialize(bytes, OAuth2Authentication.class); + } + + private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) { + return serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class); + } + + private byte[] serialize(String string) { + return serializationStrategy.serialize(string); + } + + private String deserializeString(byte[] bytes) { + return serializationStrategy.deserializeString(bytes); + } + + @Override + public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { + String key = authenticationKeyGenerator.extractKey(authentication); + byte[] serializedKey = serializeKey(AUTH_TO_ACCESS + key); + byte[] bytes; + RedisConnection conn = getConnection(); + try { + bytes = conn.get(serializedKey); + } finally { + conn.close(); + } + OAuth2AccessToken accessToken = deserializeAccessToken(bytes); + if (accessToken != null) { + OAuth2Authentication storedAuthentication = readAuthentication(accessToken.getValue()); + if ((storedAuthentication == null + || !key.equals(authenticationKeyGenerator.extractKey(storedAuthentication)))) { + // Keep the stores consistent (maybe the same user is + // represented by this authentication but the details have + // changed) + storeAccessToken(accessToken, authentication); + } + + } + return accessToken; + } + + @Override + public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { + return readAuthentication(token.getValue()); + } + + @Override + public OAuth2Authentication readAuthentication(String token) { + byte[] bytes; + RedisConnection conn = getConnection(); + try { + bytes = conn.get(serializeKey(AUTH + token)); + } finally { + conn.close(); + } + return deserializeAuthentication(bytes); + } + + @Override + public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) { + return readAuthenticationForRefreshToken(token.getValue()); + } + + public OAuth2Authentication readAuthenticationForRefreshToken(String token) { + RedisConnection conn = getConnection(); + try { + byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token)); + return deserializeAuthentication(bytes); + } finally { + conn.close(); + } + } + + @Override + public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { + byte[] serializedAccessToken = serialize(token); + byte[] serializedAuth = serialize(authentication); + byte[] accessKey = serializeKey(ACCESS + token.getValue()); + byte[] authKey = serializeKey(AUTH + token.getValue()); + byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication)); + byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication)); + byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + if (springDataRedis_2_0) { + try { + this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken); + this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth); + this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + else { + conn.set(accessKey, serializedAccessToken); + conn.set(authKey, serializedAuth); + conn.set(authToAccessKey, serializedAccessToken); + } + + if (token.getExpiration() != null) { + int seconds = token.getExpiresIn(); + long expirationTime = token.getExpiration().getTime(); + + if (!authentication.isClientOnly()) { + conn.zAdd(approvalKey, expirationTime, serializedAccessToken); + } + conn.zAdd(clientId, expirationTime, serializedAccessToken); + + conn.expire(accessKey, seconds); + conn.expire(authKey, seconds); + conn.expire(authToAccessKey, seconds); + conn.expire(clientId, seconds); + conn.expire(approvalKey, seconds); + } + else { + conn.zAdd(clientId, -1, serializedAccessToken); + if (!authentication.isClientOnly()) { + conn.zAdd(approvalKey, -1, serializedAccessToken); + } + } + OAuth2RefreshToken refreshToken = token.getRefreshToken(); + if (refreshToken != null && refreshToken.getValue() != null) { + byte[] auth = serialize(token.getValue()); + byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue()); + if (springDataRedis_2_0) { + try { + this.redisConnectionSet_2_0.invoke(conn, refreshToAccessKey, auth); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + else { + conn.set(refreshToAccessKey, auth); + } + if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken; + Date expiration = expiringRefreshToken.getExpiration(); + if (expiration != null) { + int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L) + .intValue(); + conn.expire(refreshToAccessKey, seconds); + } + } + } + conn.closePipeline(); + } finally { + conn.close(); + } + } + + private static String getApprovalKey(OAuth2Authentication authentication) { + String userName = authentication.getUserAuthentication() == null ? "" + : authentication.getUserAuthentication().getName(); + return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName); + } + + private static String getApprovalKey(String clientId, String userName) { + return clientId + (userName == null ? "" : ":" + userName); + } + + @Override + public void removeAccessToken(OAuth2AccessToken accessToken) { + removeAccessToken(accessToken.getValue()); + } + + @Override + public OAuth2AccessToken readAccessToken(String tokenValue) { + byte[] key = serializeKey(ACCESS + tokenValue); + byte[] bytes; + RedisConnection conn = getConnection(); + try { + bytes = conn.get(key); + } finally { + conn.close(); + } + return deserializeAccessToken(bytes); + } + + public void removeAccessToken(String tokenValue) { + byte[] accessKey = serializeKey(ACCESS + tokenValue); + byte[] authKey = serializeKey(AUTH + tokenValue); + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.get(accessKey); + conn.get(authKey); + conn.del(accessKey); + // Don't remove the refresh token - it's up to the caller to do that + conn.del(authKey); + List results = conn.closePipeline(); + byte[] access = (byte[]) results.get(0); + byte[] auth = (byte[]) results.get(1); + + OAuth2Authentication authentication = deserializeAuthentication(auth); + if (authentication != null) { + String key = authenticationKeyGenerator.extractKey(authentication); + byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + key); + byte[] unameKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication)); + byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId()); + conn.openPipeline(); + conn.del(authToAccessKey); + conn.zRem(unameKey, access); + conn.zRem(clientId, access); + conn.del(serialize(ACCESS + key)); + conn.closePipeline(); + } + } finally { + conn.close(); + } + } + + @Override + public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { + byte[] refreshKey = serializeKey(REFRESH + refreshToken.getValue()); + byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + refreshToken.getValue()); + byte[] serializedRefreshToken = serialize(refreshToken); + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + if (springDataRedis_2_0) { + try { + this.redisConnectionSet_2_0.invoke(conn, refreshKey, serializedRefreshToken); + this.redisConnectionSet_2_0.invoke(conn, refreshAuthKey, serialize(authentication)); + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + else { + conn.set(refreshKey, serializedRefreshToken); + conn.set(refreshAuthKey, serialize(authentication)); + } + if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken; + Date expiration = expiringRefreshToken.getExpiration(); + if (expiration != null) { + int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue(); + conn.expire(refreshKey, seconds); + conn.expire(refreshAuthKey, seconds); + } + } + conn.closePipeline(); + } finally { + conn.close(); + } + } + + @Override + public OAuth2RefreshToken readRefreshToken(String tokenValue) { + byte[] key = serializeKey(REFRESH + tokenValue); + byte[] bytes; + RedisConnection conn = getConnection(); + try { + bytes = conn.get(key); + } finally { + conn.close(); + } + return deserializeRefreshToken(bytes); + } + + @Override + public void removeRefreshToken(OAuth2RefreshToken refreshToken) { + removeRefreshToken(refreshToken.getValue()); + } + + public void removeRefreshToken(String tokenValue) { + byte[] refreshKey = serializeKey(REFRESH + tokenValue); + byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + tokenValue); + byte[] refresh2AccessKey = serializeKey(REFRESH_TO_ACCESS + tokenValue); + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.del(refreshKey); + conn.del(refreshAuthKey); + conn.del(refresh2AccessKey); + conn.closePipeline(); + } finally { + conn.close(); + } + } + + @Override + public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { + removeAccessTokenUsingRefreshToken(refreshToken.getValue()); + } + + private void removeAccessTokenUsingRefreshToken(String refreshToken) { + byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken); + List results; + RedisConnection conn = getConnection(); + try { + conn.openPipeline(); + conn.get(key); + conn.del(key); + results = conn.closePipeline(); + } finally { + conn.close(); + } + byte[] bytes = (byte[]) results.get(0); + String accessToken = deserializeString(bytes); + if (accessToken != null) { + removeAccessToken(accessToken); + } + } + + private List getZByteLists(byte[] key, RedisConnection conn) { + // Sorted Set expiration maintenance + long currentTime = System.currentTimeMillis(); + conn.zRemRangeByScore(key, 0, currentTime); + + List byteList; + Long size = conn.zCard(key); + assert size != null; + byteList = new ArrayList<>(size.intValue()); + Cursor cursor = conn.zScan(key, ScanOptions.NONE); + + while (cursor.hasNext()) { + RedisZSetCommands.Tuple t = cursor.next(); + + // Probably not necessary because of the maintenance at the beginning but why + // not... + if (t.getScore() == -1 || t.getScore() > currentTime) { + byteList.add(t.getValue()); + } + } + return byteList; + } + + /** + * Runs a maintenance of the RedisTokenStore. + *

+ * SortedSets UNAME_TO_ACCESS and CLIENT_ID_TO_ACCESS contains access tokens that can + * expire. This expiration is set as a score of the Redis SortedSet data structure. + * Redis does not support expiration of items in a container data structure. It + * supports only expiration of whole key. In case there is still new access tokens + * being stored into the RedisTokenStore before whole key gets expired, the expiration + * is prolonged and the key is not effectively deleted. To do "garbage collection" + * this method should be called once upon a time. + * @return how many items were removed + */ + public long doMaintenance() { + long removed = 0; + RedisConnection conn = getConnection(); + try { + // client_id_to_acccess maintenance + Cursor clientToAccessKeys = conn + .scan(ScanOptions.scanOptions().match(prefix + CLIENT_ID_TO_ACCESS + "*").build()); + while (clientToAccessKeys.hasNext()) { + byte[] clientIdToAccessKey = clientToAccessKeys.next(); + + removed += conn.zRemRangeByScore(clientIdToAccessKey, 0, System.currentTimeMillis()); + } + + // uname_to_access maintenance + Cursor unameToAccessKeys = conn + .scan(ScanOptions.scanOptions().match(prefix + UNAME_TO_ACCESS + "*").build()); + while (unameToAccessKeys.hasNext()) { + byte[] unameToAccessKey = unameToAccessKeys.next(); + + removed += conn.zRemRangeByScore(unameToAccessKey, 0, System.currentTimeMillis()); + } + } finally { + conn.close(); + } + return removed; + } + + @Override + public Collection findTokensByClientIdAndUserName(String clientId, String userName) { + byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(clientId, userName)); + List byteList; + RedisConnection conn = getConnection(); + try { + byteList = getZByteLists(approvalKey, conn); + } finally { + conn.close(); + } + if (byteList.size() == 0) { + return Collections.emptySet(); + } + List accessTokens = new ArrayList<>(byteList.size()); + for (byte[] bytes : byteList) { + OAuth2AccessToken accessToken = deserializeAccessToken(bytes); + accessTokens.add(accessToken); + } + return Collections.unmodifiableCollection(accessTokens); + } + + @Override + public Collection findTokensByClientId(String clientId) { + byte[] key = serializeKey(CLIENT_ID_TO_ACCESS + clientId); + List byteList; + RedisConnection conn = getConnection(); + try { + byteList = getZByteLists(key, conn); + } finally { + conn.close(); + } + if (byteList.size() == 0) { + return Collections.emptySet(); + } + List accessTokens = new ArrayList<>(byteList.size()); + for (byte[] bytes : byteList) { + OAuth2AccessToken accessToken = deserializeAccessToken(bytes); + accessTokens.add(accessToken); + } + return Collections.unmodifiableCollection(accessTokens); + } + +} diff --git a/src/main/java/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.java b/src/main/java/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.java new file mode 100644 index 0000000..558ae89 --- /dev/null +++ b/src/main/java/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.java @@ -0,0 +1,32 @@ + + +package com.klab.security.common.override.jackson2; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + *

+ * 反序列化扩展SSO用户权限 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/19 + */ +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, + getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, + isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class SimpleGrantedAuthorityMixin { + + /** + * Mixin Constructor. + * @param role the role + */ + @JsonCreator + public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) { + } + +} diff --git a/src/main/java/com/klab/security/common/util/SecurityUtils.java b/src/main/java/com/klab/security/common/util/SecurityUtils.java new file mode 100644 index 0000000..84d0197 --- /dev/null +++ b/src/main/java/com/klab/security/common/util/SecurityUtils.java @@ -0,0 +1,178 @@ +package com.klab.security.common.util; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.cloud.kicc.common.core.enums.CasSystemEnum; +import com.cloud.kicc.common.core.jackson.KiccJavaTimeModule; +import com.cloud.kicc.common.core.util.SpringContextHolderUtil; +import com.cloud.kicc.common.data.entity.CasUser; +import com.cloud.kicc.common.data.entity.KicsUser; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.klab.security.common.constant.SecurityConstants; +import com.klab.security.common.override.jackson2.SimpleGrantedAuthorityMixin; +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; +import org.springframework.beans.BeanUtils; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * 安全工具类 + *

+ * + * @Author: wangxiang4 + * @Since: 2023/8/24 + */ +@UtilityClass +public class SecurityUtils { + + /** + * 获取Authentication + */ + public Authentication getAuthentication() { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 获取用户 + */ + public CasUser getCasUser(Authentication authentication) { + Object principal = authentication.getPrincipal(); + if (principal instanceof CasUser) { + return (CasUser) principal; + } + return null; + } + + /** + * 获取CAS用户 + */ + public CasUser getCasUser() { + Authentication authentication = getAuthentication(); + if (authentication == null) { + return null; + } + return getCasUser(authentication); + } + + /** + * 获取完整用户 + * @param casSystemEnum SSO系统枚举 + * @param valueType 自定义扩展用户 + * @return T + */ + @SneakyThrows + public T getUser(CasSystemEnum casSystemEnum, Class valueType) { + CasUser casUser = getCasUser(); + if (casUser == null) return null; + String str = casUser.getExPrincipals().get(casSystemEnum); + if (JSONUtil.isJson(str)) + return new ObjectMapper() + .registerModule(new KiccJavaTimeModule()) + .addMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class) + .readValue(str, valueType); + return null; + } + + /** + * 获取SSO扩展用户 + */ + public KicsUser getUser() { + return getUser(CasSystemEnum.KICS, KicsUser.class); + } + + /** + * 获取用户角色信息 + * @return 角色集合 + */ + public List getRoles() { + Authentication authentication = getAuthentication(); + Collection authorities = authentication.getAuthorities(); + + List roleIds = new ArrayList<>(); + authorities.stream().filter(granted -> StrUtil.startWith(granted.getAuthority(), SecurityConstants.ROLE)) + .forEach(granted -> { + String id = StrUtil.removePrefix(granted.getAuthority(), SecurityConstants.ROLE); + roleIds.add(id); + }); + return roleIds; + } + + /** + * 对外开放接口临时登录会话 + * @param userId 用户id + * @return User 用户对象 + */ + @SneakyThrows + public KicsUser openInterfaceTemporaryLoginSession(String userId) { + KicsUser user = SpringContextHolderUtil.getBean(JdbcTemplate.class).queryForObject(SecurityConstants.SSO_USER_FIND_STATEMENT, KicsUser.class, userId); + if (user == null) throw new RuntimeException("用户不存在"); + List permissions = SpringContextHolderUtil.getBean(JdbcTemplate.class).queryForList(SecurityConstants.SSO_PERMISSION_FIND_STATEMENT, String.class, userId); + List authorities = Arrays.stream(permissions.toArray(new String[0])) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + CasUser casUser = new CasUser( + user.getUsername(), + SecurityConstants.BCRYPT + user.getPassword(), + true, + true, + true, + StrUtil.equals(user.getSsoStatus(), "0"), + authorities, + user.getCasUserId(), + user.getNickName(), + user.getEmail(), + user.getPhone(), + user.getSex(), + user.getAvatar(), + user.getLoginIp(), + user.getLoginTime(), + user.getSsoStatus(), + user.getSsoCreateById(), + user.getSsoCreateByName(), + user.getSsoCreateTime(), + user.getSsoUpdateById(), + user.getSsoUpdateByName(), + user.getSsoUpdateTime(), + user.getRemarks() + ); + + KicsUser kicsUser = new KicsUser( + casUser.getUsername(), + casUser.getPassword(), + casUser.isEnabled(), + casUser.isAccountNonExpired(), + casUser.isCredentialsNonExpired(), + casUser.isAccountNonLocked(), + authorities + ); + BeanUtils.copyProperties(user, kicsUser); + // 设置扩展用户数据 + casUser.getExPrincipals().put(CasSystemEnum.KICS, new ObjectMapper() + .registerModule(new KiccJavaTimeModule()) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .writeValueAsString(kicsUser)); + Authentication authentication = new UsernamePasswordAuthenticationToken(casUser, "N/A", casUser.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + return user; + } + + public static void main(String[] args) { + System.out.println(new BCryptPasswordEncoder().encode("123456")); + } + +} diff --git a/target/classes/com/cloud/kicc/common/core/enums/CasSystemEnum.class b/target/classes/com/cloud/kicc/common/core/enums/CasSystemEnum.class new file mode 100644 index 0000000000000000000000000000000000000000..5be2ad5d8b305c9640208042f116f6d499a154ab GIT binary patch literal 1570 zcmbVMZBNr+6g}O#ts7;7f*^dCzw>15I>btXL0Lv>qz_;{sVu3 zi4GCsqT#c@L&Lu?-rJ3^v3;5S(A($qp68x>dY}IK`~4??S*!;kA`!wgW&#)rU^avv z%rSVEZ})h4UqY{h2O(O|Gq@n3Pr@QUK9tZeVTm6f1+XkYe6o>D3Ybb6`J8HG^QB$& zjb#`rEpvIBLcvr`yOb-cNxi7OFFIy!ot6t#rp6()vP!|$bEW_#wWsgvYF4*1s^%0d zJ42ZryJpcSSZ^IGZwm;FrB_lf)`^;U*?v|3zMd_aMFBI>*niN)WPX=qMpBk-ZkBR8 zX5qQMlO-hJ?z{C$z*@AqhgkbUl~mr)vuV9xae=cc8PnNxjp~iYn#+)l&TEUB3sc@& zy=neM%eI^)0rM@?Z8Y!HjKtCcf|`}Fb*EIIhHtmns5MJkG_tko-Y9RgT9?Peu-L9U zOEeTAEng}a=3|T9R~m+El4nT96`t=Q87rXqnrv@AWsD#x<1z+h4Dv0CH36N!k1IUg zrw5hO%0~ucGJ0`|)cpDMp^7GC^fe;WGWr<_pN}?oExEp`J;DwK( z5+5Hrf)t-Pgg-tY9wP9C(E1)C?oPv$&puXVoH<&>*i#X4mp2XxcQ8@w?;ZKZ*V78b zZ6)Ycf=cKJ9S4wIpi@*jm9Ps$T%gORbSd2~p!k0f$6JQ1ZHr|@v~6D*igo~j1y(PNivrknfeDHhF{45 literal 0 HcmV?d00001 diff --git a/target/classes/com/cloud/kicc/common/core/enums/ExceptionEnum.class b/target/classes/com/cloud/kicc/common/core/enums/ExceptionEnum.class new file mode 100644 index 0000000000000000000000000000000000000000..aee7287b7224346713f2a60939be94770d4d1b0d GIT binary patch literal 1848 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 QuNMpk$LJ&S?)Fzd0)IgR@&Et; literal 0 HcmV?d00001 diff --git a/target/classes/com/cloud/kicc/common/data/entity/KicsUser.class b/target/classes/com/cloud/kicc/common/data/entity/KicsUser.class new file mode 100644 index 0000000000000000000000000000000000000000..81e4316b15313324697ca82a98436f920cff557d GIT binary patch literal 13586 zcmd^Ed7N9SV>@;oCz*7-)DycTX&|yjo{_DQrdm=D zg@9Z3rRfHtfs{faffAsr@wF=w;EzqMwDyVt~aO7K1F-vRKDrJ&O%2HnJFEv57^B#W0J_A+aTj za)iZbRE&vn)+Sg?vY29#X0esUHWu4i>|n8z#V!`tvDnRG4~xAlEEdx&_OaN{;sA^5 zSsY|>h{da-;;^`ZJ3qqOjUjQ9pkS?%E6>aeO3t3k+?`1mGR1}TT(z7pE=&uGWHXgh z8W3jW;nI8#&4toJzIZIN1U*<<&K22#KVO+y64d4LGgGYQ7II}c3gs)a%n=lm+~c+C zt$C(*D0r=!FQjkCRCBoCDAH)b3Q(WTL8p2ZE63~kHQs*9y^zM9WrcJ8Q_NvTgIR<+Vkm3E?a~56|7S^m$o}wNzdh%mJ7L?${B>}{2@JtmFWW< z{B!w*Vy0Rv<5OI`+V#6eqv5Y$p2YHE)11y%@>usn#bOCDrvhzChQrxRrJ5^WSSqB? zWwPfh2!~7^l)eS^!yNu4-jG9$&Q4@_r9f`*nr_YOi86L%xw-8rCbakgM7o zr+Z}d%231h0_3oBytHYC+6-)z2Zq{e?C!WeXF||tq|Kbw^MP z?%dy7d)MC^HTU;Mwf?=KtM7ftJqY`AuW}So>CKJ}yVrVb9MD}jp|MK;ZS3EPBh>0~ z1l=7+sJi0_RVR+Pg4TNoXPA5&{IT!v&D#TrjtQry$;LhVZ)KuL~3gpi3G|1hgmMz#iFcRo6~iL)XpCs%zES(6zfMDkoFXIjNe1uEp`u zvgeOxmSsYThe=4>j1(`MDIVdy2X!%1SyZu(1a$^m>%_^Kqerm!pa;vjO07^u_E+Cj z(UfcZL}asKM^mmn5lYz`Adph=9!6=$x5QHYLVk9=R+vGQ@8Q%FAz-W^^VS!YUOP1#yqF= zaXTlU;@6fSPTSMx3z;)2FRiQ~fkRr4$3qnvX?jj)t%r334#q%cekqT4RP;PvYK2UG z+x8vEmz&>iAu$sZw}@BA=uR;k5=Ud=m^dz|TjJ#9cDPh1;7~>S$e0r`Iz+FE(P0)h zusFivMiw{G%`uu`aSOrWEVmtHag4=rHk@GXHLRUvF~{Oo7N^*7nrW})>UVJUHm=^z z)jQ~Qg2q~Z$i(O$=>0Kz16@GO#^^V>vO*VP^xZ7p$l`lgd@tP-qwiz!{d6%#Z{oH$ zv-klPKgfn3V(o`n`w$LnC1~{J=b%%chHs@VFG1F7UM%%T$|<~=5fEJ}j_b|*UJ^9yeHlC7Uq7q%yYE^3 zLcKCucRjJ3xRFo8b=hKiu~sdW3MztSY^izi_z>~)xw9FZ7&B*&)e5NZad)V1Z&!!9 z#rm?{6dghmS-O-qYdk(yLRU__5hKK#F%(< z{jluyqK?kcMjE1n_=T#U4BlkW4Blt5#8#6fcAG4*;be(DCrfNQSz_nO5}Qw!*nhIb z7L+A+p)9cxWr@8gOKeA3Vn@mnn^Kn8m$JmxlqGhjEU`gli9ISyY*Sfcr=r|Ng6^d6 zg#0el4v-%jQO8))NcwyqBmZRzJVL<_NXA`|QOuNRJ0)lbWuV0rp3YDfHTE?RAMEQB z6rnK6$+7XIahXDolYb)VQ_9*Ne@~z%XohqbvNik&MYtm$-38f80m%In$DlS~aYvy~ z;2h&lPb$Z{Q*=Q&Oj5uyNoS$)5GQGY7U7GvJT$-5&Ou|plXM`#i}wWj)BTW^%tL8K<)cIo?b*J;^xL zJ;?3N+m2CG?O=bl98==kk>Smw|J6~<#><>o5>@dWMoGk?wf&V#(6nLOc1M)v1H-q=h|dy?Nl@6_aJ9MpX4gvQ1{ zLea5idQ#GR6{LP`g=Fr)2mViaJ8Em$5how)=%>9M#aed6*$X?m&)ZSFWk;O;u%n;x zcGTXoBhH`L(Yxq=NWLT+7CX_hBTm5B(a(B2>S);!XK?K3=jZ{*D@N4WvLj9b+0nbb z*C^StBhDe&(a(E3>T1~$C!y@HJGTXw`*EIaxI@5SzE*%7C~?C2N0N7UQ0BhI7Q z(R;l|)Yq~jPQ=;KFL^uaZ`l!N^6coBy&ah?JL0DRJ9^OD(Ll?N_$|SX9`au7H7z^h z#|S(674I!F*s>#j&9I|i^`6?=mL2hPh#kGpdur=icEs-~cJyoBQ(ND%BYu#vqldkx zwxMN5{Gww=zfQjadBu6Ov1Ld6BxFbU6-GG78GXxK(n8nL2ggh5daikMLsXw+&0Xw-_CZ48QO5HuHP z%!&gvX0@Af2DNKY+-!G&#;pWE<5q{6U{Hq!CCm;NXu|3QXu?XGoeWB9P^X!6fhMgk zfF`YOvx`C98q{TWyFgP`4?t5^ui3+(UJdFodtIQk)dx`8>Noos)UQE(X1@!x)iMFv zY7Lksg9bFnGzVOuZPprqwpoMb8U_t&&>C~l1=?<{1!%jq&Rol&bsDtRT;~Gqu+{^# z!`fi3XV3->T5oP}fp%IO0orK|nHw22q(K|aAs1+uwF#hIR?6JOpp*t}GE*+lb=ELI z*IAp*VFqp1pkZ^f3$)wX0?=-2#N5K55e?d6j<`U3tWkjWSYzfWgT^#y)I?m@!*{PW z4$xj}!W?JNga(cC64ybNHHoTaO__2ZOli=hIpqRPTWNr%t*vI7J#E#Xw7Jy<+6TzK z%d}ta9{bHPy&M3uK)#kJ_O5a8L0DTZ!x5rnY58)RXgHpab>BDIA(U|YG z^bu%&y2)qJC1?RU>f1?=KnqgA*F%?~g{bUH(4)}8^fu$0^igOLy3hCu{T8$+T{1pP zk3nmr$BZZGW6)ys1>-^bIJ7uDZM+A+`+Shv1sNCVacBtduG0@@bi39P_hLK`ujMjG{3&_<1Cam0TC+L$kh-?aZ4+PE)<-;n+W+JtW$ zKc{~Y+N5tAes=#`Xj8ru__AMumiC>dae5lsR{A@9@i5JV!p~8Ai2nY0>J8B|_!}0_ zQ@>Ip|6kMu;u!yPpsp#}i;|yT0luGjtGItQlUoHpo=JecRS^6i!lLqRhu{2_o7la; z41j!3$Min`etD-2c1$l~Cx<)6AEMCYB|2~yw{N!mY%u-W66SkOnYLH~X~GpJNntZ! zn?|glGzIG>TzrZ(YK5c;cb;a2Y}1$(mZossgqu*Y#;u4n;S$uWh;5p%qS6$tn{YKM z)}+-YO}G^`tIak|Sutsf)lD(ml(yp1glkf>;yFAx*e9H7jA8 zwp$(2)KNF#HdU-0R;M)KBGs%;+qBb4N>j3K!u6_HyR0s0!p*8#UAE~ut6Q47>n7Z{ zinZJ7ktSTVn$=^Q_E^2r)LS><0#>ZOR-ZKC?$xY5+hke&($rr!;YL=hY0H!*T*jJZ zN)tCCs&Lw0f7kv&@1s7bB2&k&4~ObM(U%do0b>Qn(U%dqQKL%#jF|S}u)2f33e8Uk zjDz$wXaTy-7^bg73sTjH(l?-m=w9&+`X;n6y-$3e{smfuo)nMLze0=Bm&Cj2-=MY8 zx5S(2-=W1suUMr2fEE|q#8LWBXzgNFY^VQ%mJkb~kNz84hj8%MoyJEeqx zX7V8zewLc%ZPUbv7t!Iqs9DpaFG6=byc9w^X?t-ej-QtSX;-nAbme$^d7O3_e>sPa&z7Lt zEnluOu0Vf@zcT9l@+xixt(_-YbMB0M6$ITyN6TF}y{wL-JL+6bM^0a>rs?jItL)C{ z@oJd5)2`Kb>hyb+WZiZ5Rdwz3e)W7kfS#)zfHQ_yKd~5uJMe)%*07deMXF zdu4-hR^}Qw(nCV-|4PKco7I>QtRUg~^r)#vsVq9>H(A#$fW+#S-gTFD`S zLHPA^r*TSA&;BpPzZxXy`?gyt!$)l+p-)SoJr)}_x8A);dojU<7j>iyh?eol(&^1p)e=d zQ$`i)h8U~NJK-qE!Q48gc5>d9GgzPO@R#TurDyPZay1oVvids=7%Y$#;sKqO%J(wd vqwgUE^Z=}o-6vZm?!ixB2oK>AtQCIRY9gG5r6Gf}vu2qk;nzI)0e+OR zmqg(};$eEy)m7EgJzw7+p8#%f?4ao2(7};Fb}DsD0_k$4D^R#ICbZJ3&{SzCV{Ox{ z5KJ2?(gT5Fxzc+a%c*p=G$XfTBV|Un0=Z5YM?T#v&0e{>F%(@VG=Y*eZtO;bR|db4+8(psbDJv6HV8Sk}K6F6TjYLjIR55|(x zKa^Iel|N&m(|&&P>J4_iSS_iBew@682F$MbVc@=L*>@-8$C0u#mysYe?$U7c-3a6t zOKkSXIpEUY9Tkm8g4Nh-qwBg@$uXX!2+2G+6h3-l5Wa`U)}ExxZ=z_0~~FkpZzgcbv-U}7YOB|r!<2~j|dou1C5lbP;GcTW}; zMR7x`)He%DA1uo^i&9)5S|z?&<^M1x|G`qf+dY{~LLg)+-81K&`O3%D4= ztGJ}#wQ>wau@0}x6NCP7ZQc;Y;QZObENjoWlc(>!GG%r1Ja7^oV zQYq7I6;NDrB4>KOz~1gLeNvy+Jky;_o4z*c`&q4vkN!~57b zZPTN=z>5&-?C=kQe#dFY%E$D7RFh!t7Qr?WZirmQTT5m(QcQ;s{%vbKG5+Apr_PRAaxQaLxM z@>YgcMFzGC2U&r-C+-Xd6DnMIDs~_tAR8!bDZ_D1&71LjGb7D=zHa-45~T4!z-*O{ zxK2iauOf#@6>XRjSUY~AWul{b;=}>H_mJM##LQ2tn89TgPhq=2_20MeKltqS+{fSi z^~u+XE7uZpcfOkY;^y3!Hy+&jRK*osRd7wk8+cQ}bro;nZGpqVlvxs&Lf`9#VS1j{ z5xkC?wq+)dO&exbjsW@-$`h8CNCs;vj^KuhcfiH<3msc%O9k($cn|LjB$hK9#3m>v zGby1u1)2(hEBG`&iyqm%3O-PA6CcvS1s}rcv`7>V5|)tkWu>VoP{B$c&|N+SHq#< z)Xq`e?U(15ZJ4da#ERt-TxGobt(2|%IhSK%J7fM(E(`1AyYvz~h+)EUA)BFMiX>?e-&Xoy$`sPinptCWNo zP1Jvmk{Bhe{BQH{JYVg2flo$_FoGW-60iD}uadyuRe`7_@JC{CId4&LPFS#{xo8{yxs%CxHI}lq@9F literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/config/ResourceServerAutoConfiguration.class b/target/classes/com/klab/security/common/config/ResourceServerAutoConfiguration.class new file mode 100644 index 0000000000000000000000000000000000000000..08ed5b698a0a1f74f8c777730c59df8e408701ff GIT binary patch literal 2405 zcmbVO>rN9v6#j++#R^_fQ4}xWtq3xRcne;F24fVJ0Fn@YO}7IM-Q8(sr&!)ff00P^ z0emRqneCR;vI|I)?p*eq?|kRl{{Hjh7l3IzDd0H9^SE0;hg&BK=mgJWvVd;f%VVm5 z9^B7kI*$ho-A`4h>~n@(add^D^OY`3hLiIulpktANt$I*^6933UVFk{5k|Rla@lFW zsEA>9-qQiE_@cxk>D7#~TbynJ9nuo6sdc_6BV99|T#{x}n%SDwui_UqBdpTl48!TF zG=YjDIyoWbk%Xg0E}SNTG_4gn1=)&Zy*sbXI*+PGh3jiZ z1oEpk701!ay5(Y5l)se1k?PtMevOFassU+Vs3^~)H->_uD>9s`TO&NH4MSKw1W`Nd z`=5-zU^Mw^m5gQ%aPM7)Jz~~ZYXxF>a3uQ-S51-`%E_0xx+kkHA9JBKTMJr+mLX`k zwk9GgO+E1WhVUv8rM4`rD5#j8eds~;^eH%nn9!1=!tmlK_$|IrtR3?vUxx8izzpvgCR4>zx@p0vK$tDZ ze<>aVuaz4qH~#f?&s3P$5SxPG;bG0RkCb<*iY2Y>Zp%}mlV0R=p&M-fV6=2iEs(QMuq!x4xFdlM|B>>SWzs zJi{rMPwtCwJsBAckB>Mv>6dEqKj#vu(3CzxN49iKs(MC$jILl9?x)y&x0yrO3q#ic&z_7vqB%BSlPz3&fVx5TB;op$u$iH?y-K|4LsZ z(dZBGM;Xs-3jxyLjh8vs`OeHa-^`DnU%mr)f`vT#aM!_99vLg|_2Ir%ryV?S@X)~w zL+^8`quFbsH7iS?va7kZP|1M&=eq0fUF*V5V@XU^=%nBkS@hb?z6 z;B_|^exjv0bji~S6$!O1n{Kt^UK4T5o1z*f+84`dEYaM^P%Sd_w@ybH7D}^TsGDva zX{nmqnzzJ3sP`4vec{*Ac5>$T&3L@a_c_DUKP_{F&$<;lVRnb5 zaRGCfXDI(a_s<+>qrNMAL%7`zaUkpy)BAxi$4~1N+h}ZS`~EQ*gm&7*zgT+jo$u$O{Y@^F6_|oU{~5y-`rI-ki4?RJ$Z|&@rBA-9zs?x}u8@BBv!Wnkg1 zAxrBxonZnam`q`d?X4ZyK9WPYPVhG<)0Q70nMIM-3`SG$5NS8b9;4{Ez-`>YEi&_D LPN0Ozmt(&GHL}90 literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/config/TokenStoreAutoConfiguration.class b/target/classes/com/klab/security/common/config/TokenStoreAutoConfiguration.class new file mode 100644 index 0000000000000000000000000000000000000000..2fd7b021672431cd418308ee0d1574180bc8f420 GIT binary patch literal 1187 zcmb_b+invv5Is&WX+oe)OE2M?a*-NHv4prt4Is4@eIW(ZR6#sC8#i&2wJWc;w9oty zyif@wK7fxxjJIWj)D#toUCH*0ea?)}jDP(6`W?U{+|6SGSF*U8M+VojxITd!et9#C zau$mWw}^X}3kIPoC+z2}Hjstn&7WbJTTJ#PGZx zn2`7SqQxT_#8$aOPM*+cn)Ht9@NLtRy5WqKtFbfdi8HpsDWhu)d6#Z8d|av<+u_lk zRl2idMJPWQ+v9EF1h=xSBG0L1;IVwmJBaSY-Vg!BIILBMQlz;V5xed~zGuz8YD>$L z@PFh;Da|FJPBAmA4W<64>=>S&EtuJt)>2mdl?sC7dIRk!uhLemoeE`BMT+XMsA6I*gnQeIrCpXvfz@(3KQ5u}FzxkHlN{RbCN6AfsrdfZMpk zP)m8zW;l1FGE8|Lcwguaf79Bf7G!w&Kf0CPT`rRDt(80KfMIs2J_yus{vFgRO)Mfy zZ`pqt5CC}^FObD)W;Bo8I|BQhY!_%8Pnr^1pESR$6T)f literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/exception/KiccAuth2Exception.class b/target/classes/com/klab/security/common/exception/KiccAuth2Exception.class new file mode 100644 index 0000000000000000000000000000000000000000..ab2ec0df1a252c18850e5160781cdfca595022b8 GIT binary patch literal 898 zcmbV~T~FIE6oy~-D{GU%=qL;r`?wmUXqKi;yI?{>Q;`rAXaaHXW?t&i#K=j<_OCW+ z6VfjB1Nc#B$4O`lMO>gr&N;`&k6*v`#pU0B0N!G?fhwNZVX2M^mL04(c*;6pv!fjmtWMSCg;wufo|P3Njaddg7} zGWfTMnUHtXH~P%(x@i@%l+@ok%kb)MP+^0mc%e)?W3%Sh)+dl7yBddjXac#TtOJYF zCwOfw_V568J3R7GgTwIdUT!dSvv9PAfGCAc&;vTBRgF2JLFA@9ON%uq)Cm?aurfIVu# z+C`Z@!13D~=P2ETEzzRo4E7>#8^N3cMoTe=CT(T0j|=Q4$}QH-B>EeFP#&XavZ+k5+-_^k;;x`oB^BNkMKvEi2AtKre47fQ-zrU5oz#O^Lf4 literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/exception/UnConfiguredUserDataException.class b/target/classes/com/klab/security/common/exception/UnConfiguredUserDataException.class new file mode 100644 index 0000000000000000000000000000000000000000..03d0ff1d3e72e860223226f6bd15cbec3327f0fd GIT binary patch literal 1012 zcmbtSU2hUW6g^WuwnemnRIDHUv<9fU(dbi6Bhob`C6K~WAIY$6m$k4bN_@j(>SV~B3OcQ64J9F=xd*3fFUX3P%jlvfs5C7OSpj*MdRE7AEiXDUzxB7VjT% z;VOR;jLM!nVt8A%{DFSb=N)}yTY+%pC!Hb#-_z}nmOYd%?OI;h_l{jB5O%jUvc(>k z+_*U1VTcSyj=JWrvKWe26gz`6d%{0cRgF7flDfO{l-M0x)=Poxy)cCEMc8XLUzmYU zv|vaPc3;Zj3^I~0R2ULXKM@#1S=snUwS7J0JmjS)_ zYRxd;*Bb}zQp?;oYG$QeGAs33yI~x*Dh*?gVXJx#B6{}NDenh1))A*qt40(|2}U+S z-Y({70`cPZIoMa)&EW>w#5kHl3~40EzZ0Tx6Sv4CJQk564#kz8a8(eY324RbC+C>E zOqQjjr7_uB2%C~=6EaFg2Dj-dhAd&j5}(q_h&Game4!<*FFp<}kLi-(@q~_1tvQOQ c?w-@CQnhc0en;%{rSz4VbZwe`cZ^#816RT8L;wH) literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/exp/KiccBearerTokenExtractor.class b/target/classes/com/klab/security/common/exp/KiccBearerTokenExtractor.class new file mode 100644 index 0000000000000000000000000000000000000000..a7b818324803c8a626060876f456cf8d14c4f773 GIT binary patch literal 2402 zcmbVNTUQfT6#h;U7)VAgsc03YpeQ6*hkB{D!M1=}s6kQ!wqkE5lLHx<%%mqL6dv0D z(O=M)KD299S?F5*0bTt`U0vO0W`slnvYLmPoXfs^dw<`azy5vkCx9E^N%Ua68z+!4 zkWC^UE+&$=f_GE63M)@)Zt1JHQ}_TMrjWywfjb61Vo24vsw{A2SA=A^ocHCr zRj*0MTVIpBDt_?ghSgAxYZdnIXJp?N_Z0mzS)%up81*+ZOFq zea{kGHLDs$uMp)Ci;;ku!k~m+EYy=G|`Jb1JfpEFl*qhiF>$D5NCW} z)u~&x1)(Z_x!&7}Px~;}Yhb<)pP6`oynzK153y+CECvk}Onio>Sb8dK(#2|$ zX4e<59g^KEdLyV+ zY2q=yHgN$L87>5i){Ww?vA(?{73}4CcJ&2|4TX zdR>1a{%%ep@)v4!j(cTS)JH?ZuxTyBq$$@H!{zOoXuf{@!!YF0wh=3u@y~MCEjmOA z1K&l>3=?gM+=_=`=s+D?aASl-&_f^U7}9#E(Z95wh4hYt(P%tL&jDJk&^ku%f$R=g zcH$Xg*_Vhv-a+CSI)0~v7*5eMjRZZ<(DN+1F-Yd;aGG{bg~oWB&h&2}2tpqVJqDu? zKT?P|osDJxKcVRGOCtjcn*hO~?ch3DxTfd+yJF$c0Z*(;t1nmjThAG<-a(^)h zI2^j#3phrJoTor%2<;Hwp>&4HK8e^r7-4q}jP%7c@uE}~$y%qQSZ{ZTM-fKgl`i#RR^mSgye-bu1u#W#3D=}gkvr1g7r=Nhi# F#(xR{uz3Ig literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.class b/target/classes/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.class new file mode 100644 index 0000000000000000000000000000000000000000..7685c78e02bd878af7e8a30428f2b4900a8a162e GIT binary patch literal 8470 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 MO{~x07x0V!2gMG0+W-In literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.class b/target/classes/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.class new file mode 100644 index 0000000000000000000000000000000000000000..2cd97f782463124d38e7044535ec74e1620081ed GIT binary patch literal 7065 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/klab/security/common/util/SecurityUtils.class b/target/classes/com/klab/security/common/util/SecurityUtils.class new file mode 100644 index 0000000000000000000000000000000000000000..0e5f8c7cd02fe4b6d54d79355962be9a0720ad45 GIT binary patch literal 10468 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 DnCD!k literal 0 HcmV?d00001 diff --git a/target/kics-common-security.jar b/target/kics-common-security.jar new file mode 100644 index 0000000000000000000000000000000000000000..58bfefe96ec97364483f4215018c8e2261bf284b GIT binary patch literal 47783 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 S0mcsf%Y@EkH=%GaF#iuIOp1m8 literal 0 HcmV?d00001 diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..9b6200a --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Wed Sep 13 07:01:30 CST 2023 +version=2.0-KICS +groupId=com.kics +artifactId=kics-common-security diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..2685af1 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,22 @@ +com/klab/security/common/util/SecurityUtils.class +com/klab/security/common/config/TokenStoreAutoConfiguration.class +com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.class +com/klab/security/common/exception/UnConfiguredUserDataException.class +com/klab/security/common/exp/PermitAllUrlProperties.class +com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.class +com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.class +com/cloud/kicc/common/core/enums/CasSystemEnum.class +com/klab/security/common/aspect/SecurityInnerAspect.class +com/cloud/kicc/common/data/entity/KicsUser.class +com/klab/security/common/override/KiccRedisTokenStore.class +com/klab/security/common/annotation/EnableKiccResourceServer.class +com/cloud/kicc/common/core/enums/ExceptionEnum.class +com/klab/security/common/exp/PermissionService.class +com/cloud/kicc/common/data/entity/CasUser.class +com/klab/security/common/exp/KiccBearerTokenExtractor.class +com/klab/security/common/config/SecurityMessageSourceConfiguration.class +com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.class +com/klab/security/common/config/ResourceServerAutoConfiguration.class +com/klab/security/common/annotation/Inner.class +com/klab/security/common/exp/KiccLocalResourceServerTokenServices.class +com/klab/security/common/exception/KiccAuth2Exception.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..f8a968f --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,22 @@ +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/cloud/kicc/common/core/enums/ExceptionEnum.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/override/KiccRedisTokenStore.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exp/PermitAllUrlProperties.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/aspect/SecurityInnerAspect.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/annotation/EnableKiccResourceServer.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exception/KiccAuth2Exception.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/cloud/kicc/common/core/enums/CasSystemEnum.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/config/ResourceServerAutoConfiguration.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exception/UnConfiguredUserDataException.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/config/SecurityMessageSourceConfiguration.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/cloud/kicc/common/data/entity/CasUser.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/annotation/Inner.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exp/KiccBearerTokenExtractor.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/config/TokenStoreAutoConfiguration.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/util/SecurityUtils.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/klab/security/common/exp/PermissionService.java +/Users/wangxiang/IdeaProjects/org/kics-server/kics-common/kics-common-security/src/main/java/com/cloud/kicc/common/data/entity/KicsUser.java