commit e2c4a2405ea6a202795b31b0b7bb3c01cd7d7d1b Author: wangxiang <1827945911@qq.com> Date: Sat Sep 16 15:20:09 2023 +0800 init: oot 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 0000000..5be2ad5 Binary files /dev/null and b/target/classes/com/cloud/kicc/common/core/enums/CasSystemEnum.class differ 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 0000000..aee7287 Binary files /dev/null and b/target/classes/com/cloud/kicc/common/core/enums/ExceptionEnum.class differ diff --git a/target/classes/com/cloud/kicc/common/data/entity/CasUser.class b/target/classes/com/cloud/kicc/common/data/entity/CasUser.class new file mode 100644 index 0000000..50aa7a2 Binary files /dev/null and b/target/classes/com/cloud/kicc/common/data/entity/CasUser.class differ 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 0000000..81e4316 Binary files /dev/null and b/target/classes/com/cloud/kicc/common/data/entity/KicsUser.class differ diff --git a/target/classes/com/klab/security/common/annotation/EnableKiccResourceServer.class b/target/classes/com/klab/security/common/annotation/EnableKiccResourceServer.class new file mode 100644 index 0000000..cfff9e8 Binary files /dev/null and b/target/classes/com/klab/security/common/annotation/EnableKiccResourceServer.class differ diff --git a/target/classes/com/klab/security/common/annotation/Inner.class b/target/classes/com/klab/security/common/annotation/Inner.class new file mode 100644 index 0000000..24bd821 Binary files /dev/null and b/target/classes/com/klab/security/common/annotation/Inner.class differ diff --git a/target/classes/com/klab/security/common/aspect/SecurityInnerAspect.class b/target/classes/com/klab/security/common/aspect/SecurityInnerAspect.class new file mode 100644 index 0000000..e9ed075 Binary files /dev/null and b/target/classes/com/klab/security/common/aspect/SecurityInnerAspect.class differ 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 0000000..08ed5b6 Binary files /dev/null and b/target/classes/com/klab/security/common/config/ResourceServerAutoConfiguration.class differ diff --git a/target/classes/com/klab/security/common/config/SecurityMessageSourceConfiguration.class b/target/classes/com/klab/security/common/config/SecurityMessageSourceConfiguration.class new file mode 100644 index 0000000..3934ed4 Binary files /dev/null and b/target/classes/com/klab/security/common/config/SecurityMessageSourceConfiguration.class differ 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 0000000..2fd7b02 Binary files /dev/null and b/target/classes/com/klab/security/common/config/TokenStoreAutoConfiguration.class differ 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 0000000..ab2ec0d Binary files /dev/null and b/target/classes/com/klab/security/common/exception/KiccAuth2Exception.class differ 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 0000000..03d0ff1 Binary files /dev/null and b/target/classes/com/klab/security/common/exception/UnConfiguredUserDataException.class differ 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 0000000..a7b8183 Binary files /dev/null and b/target/classes/com/klab/security/common/exp/KiccBearerTokenExtractor.class differ 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 0000000..7685c78 Binary files /dev/null and b/target/classes/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.class differ 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 0000000..2cd97f7 Binary files /dev/null and b/target/classes/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.class differ diff --git a/target/classes/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.class b/target/classes/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.class new file mode 100644 index 0000000..daba592 Binary files /dev/null and b/target/classes/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.class differ diff --git a/target/classes/com/klab/security/common/exp/PermissionService.class b/target/classes/com/klab/security/common/exp/PermissionService.class new file mode 100644 index 0000000..ed8ca81 Binary files /dev/null and b/target/classes/com/klab/security/common/exp/PermissionService.class differ diff --git a/target/classes/com/klab/security/common/exp/PermitAllUrlProperties.class b/target/classes/com/klab/security/common/exp/PermitAllUrlProperties.class new file mode 100644 index 0000000..7a1898d Binary files /dev/null and b/target/classes/com/klab/security/common/exp/PermitAllUrlProperties.class differ diff --git a/target/classes/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.class b/target/classes/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.class new file mode 100644 index 0000000..31d3fe1 Binary files /dev/null and b/target/classes/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.class differ diff --git a/target/classes/com/klab/security/common/override/KiccRedisTokenStore.class b/target/classes/com/klab/security/common/override/KiccRedisTokenStore.class new file mode 100644 index 0000000..6defed4 Binary files /dev/null and b/target/classes/com/klab/security/common/override/KiccRedisTokenStore.class differ diff --git a/target/classes/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.class b/target/classes/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.class new file mode 100644 index 0000000..7ca748f Binary files /dev/null and b/target/classes/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.class differ 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 0000000..0e5f8c7 Binary files /dev/null and b/target/classes/com/klab/security/common/util/SecurityUtils.class differ diff --git a/target/kics-common-security.jar b/target/kics-common-security.jar new file mode 100644 index 0000000..58bfefe Binary files /dev/null and b/target/kics-common-security.jar differ 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