Browse Source

init: oot

master
wangxiang 2 years ago
commit
e2c4a2405e
No known key found for this signature in database
GPG Key ID: 1BA7946AB6B232E4
  1. 8
      .idea/.gitignore
  2. 13
      .idea/compiler.xml
  3. 6
      .idea/dictionaries
  4. 8
      .idea/encodings.xml
  5. 8
      .idea/inspectionProfiles/Project_Default.xml
  6. 30
      .idea/jarRepositories.xml
  7. 15
      .idea/misc.xml
  8. 49
      pom.xml
  9. 43
      src/main/java/com/cloud/kicc/common/core/enums/CasSystemEnum.java
  10. 35
      src/main/java/com/cloud/kicc/common/core/enums/ExceptionEnum.java
  11. 186
      src/main/java/com/cloud/kicc/common/data/entity/CasUser.java
  12. 70
      src/main/java/com/cloud/kicc/common/data/entity/KicsUser.java
  13. 30
      src/main/java/com/klab/security/common/annotation/EnableKiccResourceServer.java
  14. 30
      src/main/java/com/klab/security/common/annotation/Inner.java
  15. 59
      src/main/java/com/klab/security/common/aspect/SecurityInnerAspect.java
  16. 50
      src/main/java/com/klab/security/common/config/ResourceServerAutoConfiguration.java
  17. 31
      src/main/java/com/klab/security/common/config/SecurityMessageSourceConfiguration.java
  18. 28
      src/main/java/com/klab/security/common/config/TokenStoreAutoConfiguration.java
  19. 145
      src/main/java/com/klab/security/common/constant/SecurityConstants.java
  20. 33
      src/main/java/com/klab/security/common/exception/KiccAuth2Exception.java
  21. 33
      src/main/java/com/klab/security/common/exception/UnConfiguredUserDataException.java
  22. 38
      src/main/java/com/klab/security/common/exp/KiccBearerTokenExtractor.java
  23. 118
      src/main/java/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.java
  24. 76
      src/main/java/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.java
  25. 41
      src/main/java/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.java
  26. 59
      src/main/java/com/klab/security/common/exp/PermissionService.java
  27. 72
      src/main/java/com/klab/security/common/exp/PermitAllUrlProperties.java
  28. 59
      src/main/java/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.java
  29. 518
      src/main/java/com/klab/security/common/override/KiccRedisTokenStore.java
  30. 32
      src/main/java/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.java
  31. 178
      src/main/java/com/klab/security/common/util/SecurityUtils.java
  32. BIN
      target/classes/com/cloud/kicc/common/core/enums/CasSystemEnum.class
  33. BIN
      target/classes/com/cloud/kicc/common/core/enums/ExceptionEnum.class
  34. BIN
      target/classes/com/cloud/kicc/common/data/entity/CasUser.class
  35. BIN
      target/classes/com/cloud/kicc/common/data/entity/KicsUser.class
  36. BIN
      target/classes/com/klab/security/common/annotation/EnableKiccResourceServer.class
  37. BIN
      target/classes/com/klab/security/common/annotation/Inner.class
  38. BIN
      target/classes/com/klab/security/common/aspect/SecurityInnerAspect.class
  39. BIN
      target/classes/com/klab/security/common/config/ResourceServerAutoConfiguration.class
  40. BIN
      target/classes/com/klab/security/common/config/SecurityMessageSourceConfiguration.class
  41. BIN
      target/classes/com/klab/security/common/config/TokenStoreAutoConfiguration.class
  42. BIN
      target/classes/com/klab/security/common/exception/KiccAuth2Exception.class
  43. BIN
      target/classes/com/klab/security/common/exception/UnConfiguredUserDataException.class
  44. BIN
      target/classes/com/klab/security/common/exp/KiccBearerTokenExtractor.class
  45. BIN
      target/classes/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.class
  46. BIN
      target/classes/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.class
  47. BIN
      target/classes/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.class
  48. BIN
      target/classes/com/klab/security/common/exp/PermissionService.class
  49. BIN
      target/classes/com/klab/security/common/exp/PermitAllUrlProperties.class
  50. BIN
      target/classes/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.class
  51. BIN
      target/classes/com/klab/security/common/override/KiccRedisTokenStore.class
  52. BIN
      target/classes/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.class
  53. BIN
      target/classes/com/klab/security/common/util/SecurityUtils.class
  54. BIN
      target/kics-common-security.jar
  55. 5
      target/maven-archiver/pom.properties
  56. 22
      target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
  57. 22
      target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@ @@ -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

13
.idea/compiler.xml

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="kics-common-security" />
</profile>
</annotationProcessing>
</component>
</project>

6
.idea/dictionaries

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectDictionaryState">
<dictionary name="wangxiang" />
</component>
</project>

8
.idea/encodings.xml

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/&gt;/Users/wangxiang/IdeaProjects/org/kics-common-security/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

8
.idea/inspectionProfiles/Project_Default.xml

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpringJavaConstructorAutowiringInspection" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="SpringJavaInjectionPointsAutowiringInspection" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
</profile>
</component>

30
.idea/jarRepositories.xml

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="private" />
<option name="name" value="kangalai nexus" />
<option name="url" value="http://192.168.3.10:7779/repository/kanglai-release/" />
</remote-repository>
<remote-repository>
<option name="id" value="public" />
<option name="name" value="aliyun nexus" />
<option name="url" value="http://maven.aliyun.com/nexus/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

15
.idea/misc.xml

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="GoVcsConfiguration">
<option name="GO_FMT" value="false" />
</component>
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
</project>

49
pom.xml

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kics</groupId>
<name>${project.artifactId}</name>
<version>1.0.0</version>
<artifactId>kics-common-security</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.cloud</groupId>
<artifactId>kicc-common-data</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!-- spring boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>

43
src/main/java/com/cloud/kicc/common/core/enums/CasSystemEnum.java

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
package com.cloud.kicc.common.core.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
*<p>
* cas系统枚举
*</p>
*
* @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;
}

35
src/main/java/com/cloud/kicc/common/core/enums/ExceptionEnum.java

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
package com.cloud.kicc.common.core.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
*<p>
* API错误页面响应状态枚举
*</p>
*
* @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;
}

186
src/main/java/com/cloud/kicc/common/data/entity/CasUser.java

@ -0,0 +1,186 @@ @@ -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;
/**
*<p>
* CAS统一认证用户数据
*</p>
*
* @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<CasSystemEnum, String> exPrincipals = new ConcurrentHashMap<>(3);
public CasUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public CasUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}
public CasUser(String username,
String password,
boolean enabled,
boolean accountNonExpired,
boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<? extends GrantedAuthority> 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<? extends GrantedAuthority> 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;
}
}

70
src/main/java/com/cloud/kicc/common/data/entity/KicsUser.java

@ -0,0 +1,70 @@ @@ -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<SimpleGrantedAuthority> authorities) {
super(ObjectUtil.defaultIfNull(username, SecurityConstants.MOCK_USERNAME),
ObjectUtil.defaultIfNull(password, SecurityConstants.MOCK_PASSWORD),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
ObjectUtil.defaultIfNull(authorities, AuthorityUtils.createAuthorityList()));
}
}

30
src/main/java/com/klab/security/common/annotation/EnableKiccResourceServer.java

@ -0,0 +1,30 @@ @@ -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.*;
/**
*<p>
* 扩展资源服务注解
* 添加方法安全级别
*</p>
*
* @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 {
}

30
src/main/java/com/klab/security/common/annotation/Inner.java

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
package com.klab.security.common.annotation;
import java.lang.annotation.*;
/**
*<p>
* 服务调用不鉴权注解
*</p>
*
* @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 {};
}

59
src/main/java/com/klab/security/common/aspect/SecurityInnerAspect.java

@ -0,0 +1,59 @@ @@ -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;
/**
*<p>
* 内部接口调用注解切面处理
* 设置是否是内部接口,不提供给外部访问
*</p>
*
* @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;
}
}

50
src/main/java/com/klab/security/common/config/ResourceServerAutoConfiguration.java

@ -0,0 +1,50 @@ @@ -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;
/**
*<p>
* 扩展资源服务器自动配置
*</p>
*
* @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);
}
}

31
src/main/java/com/klab/security/common/config/SecurityMessageSourceConfiguration.java

@ -0,0 +1,31 @@ @@ -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;
/**
*<p>
* 设置安全模块国际化配置
*</p>
*
* @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;
}
}

28
src/main/java/com/klab/security/common/config/TokenStoreAutoConfiguration.java

@ -0,0 +1,28 @@ @@ -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;
/**
*<p>
* 令牌存储自动配置
*</p>
*
* @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;
}
}

145
src/main/java/com/klab/security/common/constant/SecurityConstants.java

@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
package com.klab.security.common.constant;
/**
*<p>
* 安全常量
*</p>
*
* @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_idclient_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";
}

33
src/main/java/com/klab/security/common/exception/KiccAuth2Exception.java

@ -0,0 +1,33 @@ @@ -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;
/**
*<p>
* 自定义OAuth2Exception
*</p>
*
* @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;
}
}

33
src/main/java/com/klab/security/common/exception/UnConfiguredUserDataException.java

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
package com.klab.security.common.exception;
import org.springframework.http.HttpStatus;
/**
*<p>
* 未配置用户数据
*</p>
*
* @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();
}
}

38
src/main/java/com/klab/security/common/exp/KiccBearerTokenExtractor.java

@ -0,0 +1,38 @@ @@ -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;
/**
*<p>
* 重写 {@link BearerTokenExtractor}
* 对公开权限的请求不进行抓取校验直接返回null放过
*</p>
*
* @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);
}
}

118
src/main/java/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.java

@ -0,0 +1,118 @@ @@ -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;
/**
*<p>
* 本地资源服务器令牌服务
*</p>
*
* @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<KicsUser> 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<String> permissions = jdbcTemplate.queryForList(com.klab.security.common.constant.SecurityConstants.SSO_PERMISSION_FIND_STATEMENT, String.class, user.getUserId());
List<SimpleGrantedAuthority> 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");
}
}

76
src/main/java/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.java

@ -0,0 +1,76 @@ @@ -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;
/**
*<p>
* 资源服务配置适配器
* 覆盖内部不合适的实现,实现适配
* 1. 支持remoteTokenServices 负载均衡,后期出现
* 2. 支持 获取用户全部信息
* 3. 接口对外暴露不校验 Authentication Header
*</p>
*
* @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<HttpSecurity>.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);
}
}

41
src/main/java/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.java

@ -0,0 +1,41 @@ @@ -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;
/**
*<p>
* 安全 Bean 定义注册器
* 加载自定义Bean
*</p>
*
* @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);
}
}

59
src/main/java/com/klab/security/common/exp/PermissionService.java

@ -0,0 +1,59 @@ @@ -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;
/**
*<p>
* 接口权限判断
*</p>
*
* @Author: wangxiang4
* @Since: 2023/8/24
*/
public class PermissionService {
/**
* 判断接口是否有任意xxxxxx权限
* @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<? extends GrantedAuthority> 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;
}
}

72
src/main/java/com/klab/security/common/exp/PermitAllUrlProperties.java

@ -0,0 +1,72 @@ @@ -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;
/**
*<p>
* 资源服务器对外直接暴露URL,如果设置 contex-path 要特殊处理
* 主要处理拿取加了Inner注解的方法上的url映射地址,以及配置文件中配置的security.oauth2.ignore.urls,进行对外开放不拦截
*</p>
*
* @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<String> urls = new ArrayList<>();
/** bean初始化后执行 */
@Override
public void afterPropertiesSet() {
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> 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;
}
}

59
src/main/java/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.java

@ -0,0 +1,59 @@ @@ -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;
/**
*<p>
* 客户端认证异常处理
* 可以根据 AuthenticationException 不同细化异常处理
*</p>
*
* @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<String> 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));
}
}

518
src/main/java/com/klab/security/common/override/KiccRedisTokenStore.java

@ -0,0 +1,518 @@ @@ -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.*;
/**
*<p>
* @link <a href="https://github.com/spring-projects/spring-security-oauth/pull/1660"/>
* 重写RedisTokenStore ,主要解决 #1814 oauth2中client_id_to_access数据膨胀问题
*</p>
*
* @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<Object> 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<Object> 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<byte[]> getZByteLists(byte[] key, RedisConnection conn) {
// Sorted Set expiration maintenance
long currentTime = System.currentTimeMillis();
conn.zRemRangeByScore(key, 0, currentTime);
List<byte[]> byteList;
Long size = conn.zCard(key);
assert size != null;
byteList = new ArrayList<>(size.intValue());
Cursor<RedisZSetCommands.Tuple> 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.
* <p>
* 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<byte[]> 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<byte[]> 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<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(clientId, userName));
List<byte[]> byteList;
RedisConnection conn = getConnection();
try {
byteList = getZByteLists(approvalKey, conn);
} finally {
conn.close();
}
if (byteList.size() == 0) {
return Collections.emptySet();
}
List<OAuth2AccessToken> accessTokens = new ArrayList<>(byteList.size());
for (byte[] bytes : byteList) {
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
}
@Override
public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
byte[] key = serializeKey(CLIENT_ID_TO_ACCESS + clientId);
List<byte[]> byteList;
RedisConnection conn = getConnection();
try {
byteList = getZByteLists(key, conn);
} finally {
conn.close();
}
if (byteList.size() == 0) {
return Collections.emptySet();
}
List<OAuth2AccessToken> accessTokens = new ArrayList<>(byteList.size());
for (byte[] bytes : byteList) {
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
}
}

32
src/main/java/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.java

@ -0,0 +1,32 @@ @@ -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;
/**
*<p>
* 反序列化扩展SSO用户权限
*</p>
*
* @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) {
}
}

178
src/main/java/com/klab/security/common/util/SecurityUtils.java

@ -0,0 +1,178 @@ @@ -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;
/**
*<p>
* 安全工具类
*</p>
*
* @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> T getUser(CasSystemEnum casSystemEnum, Class<T> 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<String> getRoles() {
Authentication authentication = getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> 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<String> permissions = SpringContextHolderUtil.getBean(JdbcTemplate.class).queryForList(SecurityConstants.SSO_PERMISSION_FIND_STATEMENT, String.class, userId);
List<SimpleGrantedAuthority> 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"));
}
}

BIN
target/classes/com/cloud/kicc/common/core/enums/CasSystemEnum.class

Binary file not shown.

BIN
target/classes/com/cloud/kicc/common/core/enums/ExceptionEnum.class

Binary file not shown.

BIN
target/classes/com/cloud/kicc/common/data/entity/CasUser.class

Binary file not shown.

BIN
target/classes/com/cloud/kicc/common/data/entity/KicsUser.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/annotation/EnableKiccResourceServer.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/annotation/Inner.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/aspect/SecurityInnerAspect.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/config/ResourceServerAutoConfiguration.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/config/SecurityMessageSourceConfiguration.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/config/TokenStoreAutoConfiguration.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exception/KiccAuth2Exception.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exception/UnConfiguredUserDataException.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exp/KiccBearerTokenExtractor.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exp/KiccLocalResourceServerTokenServices.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exp/KiccResourceServerConfigurerAdapter.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exp/KiccSecurityBeanDefinitionRegistrar.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exp/PermissionService.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exp/PermitAllUrlProperties.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/exp/ResourceAuthExceptionEntryPoint.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/override/KiccRedisTokenStore.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/override/jackson2/SimpleGrantedAuthorityMixin.class

Binary file not shown.

BIN
target/classes/com/klab/security/common/util/SecurityUtils.class

Binary file not shown.

BIN
target/kics-common-security.jar

Binary file not shown.

5
target/maven-archiver/pom.properties

@ -0,0 +1,5 @@ @@ -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

22
target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst

@ -0,0 +1,22 @@ @@ -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

22
target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst

@ -0,0 +1,22 @@ @@ -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
Loading…
Cancel
Save