From 93188e50d84e88a98a985fd8a51b234381040e6a Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Fri, 10 Nov 2023 17:31:28 +0800 Subject: [PATCH 01/30] =?UTF-8?q?fix(Security):=20=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin4j-dependencies/pom.xml | 2 +- security-spring-boot-starter/pom.xml | 2 +- .../configuration/SecurityConfiguration.java | 102 +++--------------- .../UserTokenServiceConfiguration.java | 7 +- .../MultiCheckUsernamePasswordService.java | 68 ++++++++++++ .../UsernamePasswordUserDetailsService.java | 49 +-------- 6 files changed, 90 insertions(+), 140 deletions(-) create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiCheckUsernamePasswordService.java diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 94b9e26..acd1fe9 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -155,7 +155,7 @@ com.admin4j.framework security-spring-boot-starter - ${admin4j.version} + 0.9.2 com.admin4j.framework diff --git a/security-spring-boot-starter/pom.xml b/security-spring-boot-starter/pom.xml index c79e38a..eafc511 100644 --- a/security-spring-boot-starter/pom.xml +++ b/security-spring-boot-starter/pom.xml @@ -10,7 +10,7 @@ com.admin4j.framework security-spring-boot-starter jar - + 0.9.2 security-spring-boot-starter diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java index 9fb8460..7d43d3d 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java @@ -16,11 +16,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Lazy; -import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -78,24 +74,13 @@ public class SecurityConfiguration { @Autowired(required = false) List userDetailServices; @Autowired - @Lazy - AuthenticationManager authenticationManager; + AuthenticationConfiguration authenticationConfiguration; @Autowired(required = false) ActuatorFilter actuatorFilter; @Autowired(required = false) CorsFilter corsFilter; - /** - * 认证管理器,登录的时候参数会传给 authenticationManager - */ - @Bean(name = BeanIds.AUTHENTICATION_MANAGER) - public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { - - return authenticationConfiguration.getAuthenticationManager(); - } - - /** * 取消ROLE_前缀 */ @@ -105,15 +90,15 @@ public AuthenticationManager authenticationManager(AuthenticationConfiguration a // return new GrantedAuthorityDefaults(""); //} - /** - * 设置中文配置 - */ - @Bean - public ReloadableResourceBundleMessageSource messageSource() { - ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); - messageSource.setBasename("classpath:org/springframework/security/messages_zh_CN"); - return messageSource; - } + // /** + // * 设置中文配置 + // */ + // @Bean + // public ReloadableResourceBundleMessageSource messageSource() { + // ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + // messageSource.setBasename("classpath:org/springframework/security/messages_zh_CN"); + // return messageSource; + // } /** * 安全配置 @@ -122,7 +107,7 @@ public ReloadableResourceBundleMessageSource messageSource() { @ConditionalOnMissingBean(SecurityFilterChain.class) public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { -// FilterSecurityInterceptor + // FilterSecurityInterceptor httpSecurity // 关闭cors .cors().disable() @@ -166,7 +151,7 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti MultiAuthenticationFilter authenticationFilter = new MultiAuthenticationFilter(multiAuthenticationProperties, formLoginProperties); - authenticationFilter.setAuthenticationManager(authenticationManager); + authenticationFilter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager()); authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler); authenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); @@ -258,68 +243,5 @@ private void ignoringRequestMatcherRegistry(ExpressionUrlAuthorizationConfigurer expressionInterceptUrlRegistry.antMatchers(i, anonymousUrl.get(i)).permitAll(); }); } - } - /** - * 网络安全配置,忽略部分路径(如静态文件路径) - */ - //@Bean - //@ConditionalOnMissingBean(WebSecurityCustomizer.class) - // public WebSecurityCustomizer webSecurityCustomizer() { - // return (web) -> { - // - // //设置匿名访问url - // WebSecurity.IgnoredRequestConfigurer ignoring = web.ignoring(); - // - // if (securityIgnoringUrls != null && !securityIgnoringUrls.isEmpty()) { - // securityIgnoringUrls.forEach(url -> { - // - // if (url.ignoringUrls() == null || url.ignoringUrls().length == 0) { - // return; - // } - // - // if (url.support() == null) { - // ignoring.antMatchers(url.ignoringUrls()); - // } else { - // ignoring.antMatchers(url.support(), url.ignoringUrls()); - // } - // }); - // } - // - // if (ignoringUrlProperties != null) { - // - // if (ignoringUrlProperties.getUris() != null && ignoringUrlProperties.getUris().length > 0) { - // ignoring.antMatchers(ignoringUrlProperties.getUris()); - // } - // if (ignoringUrlProperties.getGet() != null && ignoringUrlProperties.getGet().length > 0) { - // ignoring.antMatchers(HttpMethod.GET, ignoringUrlProperties.getGet()); - // } - // - // if (ignoringUrlProperties.getPost() != null && ignoringUrlProperties.getPost().length > 0) { - // ignoring.antMatchers(HttpMethod.POST, ignoringUrlProperties.getPost()); - // } - // if (ignoringUrlProperties.getPut() != null && ignoringUrlProperties.getPut().length > 0) { - // ignoring.antMatchers(HttpMethod.PUT, ignoringUrlProperties.getPut()); - // } - // if (ignoringUrlProperties.getPatch() != null && ignoringUrlProperties.getPatch().length > 0) { - // ignoring.antMatchers(HttpMethod.PATCH, ignoringUrlProperties.getPatch()); - // } - // if (ignoringUrlProperties.getDelete() != null && ignoringUrlProperties.getDelete().length > 0) { - // ignoring.antMatchers(HttpMethod.DELETE, ignoringUrlProperties.getDelete()); - // } - // } - // - // //AnonymousAccess 注解 - // if (anonymousAccessUrl != null) { - // - // Map anonymousUrl = anonymousAccessUrl.getAnonymousUrl(); - // anonymousUrl.keySet().forEach(i -> { - // - // ignoring.antMatchers(i, anonymousUrl.get(i)); - // }); - // } - // }; - //} - - } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java index 2ac9259..7ef4647 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java @@ -6,6 +6,7 @@ import com.admin4j.framework.security.filter.ActuatorFilter; import com.admin4j.framework.security.jwt.JwtUserDetailsService; import com.admin4j.framework.security.jwt.JwtUserTokenService; +import com.admin4j.framework.security.mult.MultiCheckUsernamePasswordService; import com.admin4j.framework.security.mult.UsernamePasswordUserDetailsService; import com.admin4j.framework.security.properties.ActuatorProperties; import com.admin4j.framework.security.properties.FormLoginProperties; @@ -49,7 +50,7 @@ public PasswordEncoder passwordEncoder() { * @return */ @Bean - @ConditionalOnMissingBean(UsernamePasswordUserDetailsService.class) + @ConditionalOnMissingBean(MultiCheckUsernamePasswordService.class) @ConditionalOnBean(UserDetailsService.class) @ConditionalOnProperty(prefix = "admin4j.security.multi", name = "enable", matchIfMissing = true) public UsernamePasswordUserDetailsService usernamePasswordUserDetailsService( @@ -58,9 +59,9 @@ public UsernamePasswordUserDetailsService usernamePasswordUserDetailsService( FormLoginProperties formLoginProperties) { return new UsernamePasswordUserDetailsService( - userDetailsService, passwordEncoder, - formLoginProperties + formLoginProperties, + userDetailsService ); } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiCheckUsernamePasswordService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiCheckUsernamePasswordService.java new file mode 100644 index 0000000..17f5c57 --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiCheckUsernamePasswordService.java @@ -0,0 +1,68 @@ +package com.admin4j.framework.security.mult; + +import com.admin4j.common.Prioritized; +import com.admin4j.framework.security.properties.FormLoginProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.SpringSecurityMessageSource; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * 检查账号密码是否正确 + * + * @author andanyang + * @since 2023/11/10 15:53 + */ +@Slf4j +@RequiredArgsConstructor +public abstract class MultiCheckUsernamePasswordService implements MultiUserDetailsService { + + protected final PasswordEncoder passwordEncoder; + protected final FormLoginProperties formLoginProperties; + protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); + + @Override + public int getPriority() { + return Prioritized.MAX_PRIORITY; + } + + /** + * 该类是否支持指定的authType登录方式 + * + * @param authType 指定的authType登录方式 + * @return + */ + @Override + public String support() { + return MultiAuthenticationFilter.DEFAULT_AUTH_TYPE; + } + + /** + * 加载用户前的认证。 + * - 比如认证短信验证码是否正确等 + * - 接口限速 + * 等 + * + * @param multiAuthenticationToken + * @return 返回是否认证成功 + */ + @Override + public boolean preVerify(MultiAuthenticationToken multiAuthenticationToken) { + return true; + } + + @Override + public void postLoadUser(MultiAuthenticationToken multiAuthenticationToken) { + UserDetails principal = (UserDetails) multiAuthenticationToken.getPrincipal(); + + // 认证密码是否正确 + if (!passwordEncoder.matches(multiAuthenticationToken.getAuthParameter(formLoginProperties.getPasswordParameter()), principal.getPassword())) { + log.debug("Failed to authenticate since password does not match stored value"); + throw new BadCredentialsException(this.messages + .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + } +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/UsernamePasswordUserDetailsService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/UsernamePasswordUserDetailsService.java index 7d657e2..a97d135 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/UsernamePasswordUserDetailsService.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/UsernamePasswordUserDetailsService.java @@ -1,12 +1,7 @@ package com.admin4j.framework.security.mult; -import com.admin4j.common.Prioritized; import com.admin4j.framework.security.properties.FormLoginProperties; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; @@ -18,42 +13,16 @@ * @since 2023/6/2 13:43 */ @Slf4j -@RequiredArgsConstructor -public class UsernamePasswordUserDetailsService implements MultiUserDetailsService { +public class UsernamePasswordUserDetailsService extends MultiCheckUsernamePasswordService { private final UserDetailsService userDetailsService; - private final PasswordEncoder passwordEncoder; - private final FormLoginProperties formLoginProperties; - protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); - - @Override - public int getPriority() { - return Prioritized.MAX_PRIORITY; + public UsernamePasswordUserDetailsService(PasswordEncoder passwordEncoder, FormLoginProperties formLoginProperties, UserDetailsService userDetailsService) { + super(passwordEncoder, formLoginProperties); + this.userDetailsService = userDetailsService; } - /** - * 该类是否支持指定的authType登录方式 - * - * @param authType 指定的authType登录方式 - * @return - */ - @Override - public String support() { - return MultiAuthenticationFilter.DEFAULT_AUTH_TYPE; - } - - /** - * 加载用户前的认证。比如认证短信验证码是否正确等 - * - * @param multiAuthenticationToken - * @return 返回是否认证成功 - */ - @Override - public boolean preVerify(MultiAuthenticationToken multiAuthenticationToken) { - return true; - } /** * 多渠道登录加载用户 @@ -66,15 +35,5 @@ public UserDetails loadUserByMultiToken(String multiToken) { return userDetailsService.loadUserByUsername(multiToken); } - @Override - public void postLoadUser(MultiAuthenticationToken multiAuthenticationToken) { - UserDetails principal = (UserDetails) multiAuthenticationToken.getPrincipal(); - //认证密码是否正确 - if (!passwordEncoder.matches(multiAuthenticationToken.getAuthParameter(formLoginProperties.getPasswordParameter()), principal.getPassword())) { - log.debug("Failed to authenticate since password does not match stored value"); - throw new BadCredentialsException(this.messages - .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); - } - } } From f93885db9a5d0fcdaa4644cc2907954fe70c0df7 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 14 Nov 2023 09:36:46 +0800 Subject: [PATCH 02/30] =?UTF-8?q?docs(admin4j-spring-common):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/admin4j/spring/util/SpelUtil.java | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/admin4j-common-spring/src/main/java/com/admin4j/spring/util/SpelUtil.java b/admin4j-common-spring/src/main/java/com/admin4j/spring/util/SpelUtil.java index c41f974..f1826f8 100644 --- a/admin4j-common-spring/src/main/java/com/admin4j/spring/util/SpelUtil.java +++ b/admin4j-common-spring/src/main/java/com/admin4j/spring/util/SpelUtil.java @@ -20,21 +20,34 @@ */ public class SpelUtil { - private SpelUtil() { - } - + /** + * 是一种基于本地变量表的参数解析器,依赖于编译时的选项,可能无法在所有情况下获取参数名称 + */ private static final LocalVariableTableParameterNameDiscoverer U = new LocalVariableTableParameterNameDiscoverer(); - //使用SPEL进行key的解析 + // 使用SPEL进行key的解析 private static final ExpressionParser EL_PARSER = new SpelExpressionParser(); + private SpelUtil() { + } + /** + * el表达式解析 + * + * @param spel 解析值 + * @param method 方法 + * @param args 参数 + * @return + */ public static String parse(String spel, Method method, Object[] args) { - //获取被拦截方法参数名列表(使用Spring支持类库) + if (StringUtils.isBlank(spel)) { + return null; + } + // 获取被拦截方法参数名列表(使用Spring支持类库) String[] paraNameArr = U.getParameterNames(method); - //SPEL上下文 + // SPEL上下文 StandardEvaluationContext context = new StandardEvaluationContext(); - //把方法参数放入SPEL上下文中 + // 把方法参数放入SPEL上下文中 if (paraNameArr != null) { for (int i = 0; i < paraNameArr.length; i++) { context.setVariable(paraNameArr[i], args[i]); @@ -63,12 +76,12 @@ public static String parse(Object rootObject, String spel, Method method, Object } else if (!spel.contains("#")) { return parse(spel, method, args); } - //获取被拦截方法参数名列表(使用Spring支持类库) + // 获取被拦截方法参数名列表(使用Spring支持类库) String[] paraNameArr = U.getParameterNames(method); - //SPEL上下文 + // SPEL上下文 StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject, method, args, U); - //把方法参数放入SPEL上下文中 + // 把方法参数放入SPEL上下文中 if (paraNameArr != null) { for (int i = 0; i < paraNameArr.length; i++) { context.setVariable(paraNameArr[i], args[i]); From e6f0bc99ab4b655b508dd3c674a4a159c3529bd4 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 14 Nov 2023 14:46:43 +0800 Subject: [PATCH 03/30] =?UTF-8?q?feat(mybatis-plus-boot-starter):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A7=9F=E6=88=B7=E5=BC=80=E5=85=B3=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E9=BB=98=E8=AE=A4=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin4j-dependencies/pom.xml | 2 +- mybatis-plus-boot-starter/pom.xml | 3 ++- .../mp/config/MybatisPlusPluginConfig.java | 18 +++++++++--------- .../framework/mp/properties/MpProperties.java | 6 ++++++ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index acd1fe9..2cf3b84 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -160,7 +160,7 @@ com.admin4j.framework mybatis-plus-boot-starter - ${admin4j.version} + 0.9.3-SNAPSHOT com.admin4j.framework diff --git a/mybatis-plus-boot-starter/pom.xml b/mybatis-plus-boot-starter/pom.xml index 86f63b3..0b86dfd 100644 --- a/mybatis-plus-boot-starter/pom.xml +++ b/mybatis-plus-boot-starter/pom.xml @@ -1,4 +1,4 @@ - 4.0.0 @@ -12,6 +12,7 @@ jar mybatis-plus-boot-starter + 0.9.3-SNAPSHOT diff --git a/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/config/MybatisPlusPluginConfig.java b/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/config/MybatisPlusPluginConfig.java index 2a3f912..0ea3815 100644 --- a/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/config/MybatisPlusPluginConfig.java +++ b/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/config/MybatisPlusPluginConfig.java @@ -36,7 +36,7 @@ public void setLoginTenantInfoService(ILoginTenantInfoService loginTenantInfoSer this.loginTenantInfoService = loginTenantInfoService; } - //多租户 + // 多租户 public TenantLineInnerInterceptor tenantLineInnerInterceptor(ILoginTenantInfoService loginTenantInfoService) { return new TenantLineInnerInterceptor(new TenantLineHandler() { @@ -55,7 +55,7 @@ public Expression getTenantId() { public boolean ignoreTable(String tableName) { Long tenantId = loginTenantInfoService.getTenantId(); - //没有登录,可关闭 租户 + // 没有登录,可关闭 租户 if (ObjectUtils.isEmpty(tenantId) || Objects.equals(0L, tenantId)) { return true; } @@ -77,22 +77,22 @@ public boolean ignoreTable(String tableName) { public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); - if (loginTenantInfoService != null) { + if (loginTenantInfoService != null && mpProperties.isTenantEnable()) { interceptor.addInnerInterceptor(tenantLineInnerInterceptor(loginTenantInfoService)); } - //分页插件 + // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); - //乐观锁 + // 乐观锁 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); - //防止全表更新与删除 + // 防止全表更新与删除 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); - //动态表名 - //interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor()); + // 动态表名 + // interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor()); - //sql 性能规范 + // sql 性能规范 if (mpProperties.isIllegalSql()) { interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor()); } diff --git a/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/properties/MpProperties.java b/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/properties/MpProperties.java index 658b52c..e0b2704 100644 --- a/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/properties/MpProperties.java +++ b/mybatis-plus-boot-starter/src/main/java/com/admin4j/framework/mp/properties/MpProperties.java @@ -11,10 +11,16 @@ @ConfigurationProperties(prefix = "admin4j.mp") public class MpProperties { + /** + * 多租户功能开关 + */ + private boolean tenantEnable = false; + /** * 多租需要忽略的表 */ private String[] ignoreTenantTable; + /** * 是否开启 sql 性能规范插件 */ From a1a65b4ba66fb69f5e2ac973a19cd42f3ddeb0e4 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Thu, 16 Nov 2023 13:35:40 +0800 Subject: [PATCH 04/30] =?UTF-8?q?feat(web-spring-boot-starter):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0404=E5=85=A8=E5=B1=80=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin4j-dependencies/pom.xml | 2 +- web-spring-boot-starter/pom.xml | 4 +- .../JacksonAutoConfiguration.java} | 6 +-- .../UserContextAutoConfiguration.java} | 4 +- .../autoconfigure/WebAutoConfiguration.java | 53 +++++++++++++++++++ .../controller/Admin4jErrorController.java | 24 ++++----- .../main/resources/META-INF/spring.factories | 6 +-- 7 files changed, 74 insertions(+), 25 deletions(-) rename web-spring-boot-starter/src/main/java/com/admin4j/framework/web/{config/JacksonConfig.java => autoconfigure/JacksonAutoConfiguration.java} (94%) rename web-spring-boot-starter/src/main/java/com/admin4j/framework/web/{config/UserContextConfig.java => autoconfigure/UserContextAutoConfiguration.java} (88%) create mode 100644 web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 2cf3b84..778e248 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -220,7 +220,7 @@ com.admin4j.framework web-spring-boot-starter - 0.9.1 + 0.9.2 com.admin4j.framework diff --git a/web-spring-boot-starter/pom.xml b/web-spring-boot-starter/pom.xml index 2cf3baf..5a40c70 100644 --- a/web-spring-boot-starter/pom.xml +++ b/web-spring-boot-starter/pom.xml @@ -12,8 +12,8 @@ com.admin4j.framework web-spring-boot-starter ${project.artifactId} - 0.9.1 - + 0.9.2 + com.alibaba diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/config/JacksonConfig.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/JacksonAutoConfiguration.java similarity index 94% rename from web-spring-boot-starter/src/main/java/com/admin4j/framework/web/config/JacksonConfig.java rename to web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/JacksonAutoConfiguration.java index de3d51b..bc9e76f 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/config/JacksonConfig.java +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/JacksonAutoConfiguration.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.web.config; +package com.admin4j.framework.web.autoconfigure; import com.admin4j.common.time.DateFormatterPattern; @@ -22,7 +22,7 @@ * @author andanyang * @since 2022/8/4 15:24 */ -public class JacksonConfig { +public class JacksonAutoConfiguration { /** * 时区配置 @@ -51,7 +51,7 @@ public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); builder.timeZone(TimeZone.getDefault()); - //防止前端Long型丢失精度 + // 防止前端Long型丢失精度 builder.serializerByType(Long.class, ToStringSerializer.instance); }; } diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/config/UserContextConfig.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java similarity index 88% rename from web-spring-boot-starter/src/main/java/com/admin4j/framework/web/config/UserContextConfig.java rename to web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java index dacf312..e1e449a 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/config/UserContextConfig.java +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.web.config; +package com.admin4j.framework.web.autoconfigure; import com.admin4j.common.service.IUserContextHolder; import com.admin4j.framework.web.SimpleUserContextHolder; @@ -12,7 +12,7 @@ * @since 2023/9/15 9:19 */ @Configuration -public class UserContextConfig { +public class UserContextAutoConfiguration { @Bean @ConditionalOnMissingBean(IUserContextHolder.class) diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java new file mode 100644 index 0000000..1c3e74a --- /dev/null +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java @@ -0,0 +1,53 @@ +package com.admin4j.framework.web.autoconfigure; + +import com.admin4j.framework.web.controller.Admin4jErrorController; +import com.admin4j.framework.web.exception.handler.GlobalExceptionHandler; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver; +import org.springframework.boot.web.servlet.error.ErrorAttributes; +import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.context.annotation.Bean; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author andanyang + * @since 2023/11/16 13:20 + */ +@AutoConfiguration(before = ErrorMvcAutoConfiguration.class) +@RequiredArgsConstructor +public class WebAutoConfiguration { + + private final ServerProperties serverProperties; + + /** + * 404 错误 + * + * @param errorAttributes + * @param errorViewResolvers + * @return + */ + @Bean + @ConditionalOnMissingBean( + value = {ErrorController.class} + ) + public Admin4jErrorController basicErrorController(ErrorAttributes errorAttributes, ObjectProvider errorViewResolvers) { + return new Admin4jErrorController(errorAttributes, this.serverProperties.getError(), (List) errorViewResolvers.orderedStream().collect(Collectors.toList())); + } + + /** + * 全局错误 + * + * @return + */ + @Bean + public GlobalExceptionHandler globalExceptionHandler() { + return new GlobalExceptionHandler(); + } +} diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/controller/Admin4jErrorController.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/controller/Admin4jErrorController.java index 2fc8d34..c5e7a41 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/controller/Admin4jErrorController.java +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/controller/Admin4jErrorController.java @@ -1,15 +1,12 @@ package com.admin4j.framework.web.controller; import com.admin4j.common.pojo.ResponseEnum; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.ErrorProperties; import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; -import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; -import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver; import org.springframework.boot.web.servlet.error.ErrorAttributes; -import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; @@ -22,14 +19,15 @@ * @author andanyang * @since 2023/5/29 17:44 */ -@AutoConfigureBefore(ErrorMvcAutoConfiguration.class) -@ConditionalOnMissingBean(ErrorController.class) +@Controller +@RequestMapping({"${server.error.path:${error.path:/error}}"}) public class Admin4jErrorController extends BasicErrorController { - private final ServerProperties serverProperties; - public Admin4jErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List errorViewResolvers) { - super(errorAttributes, serverProperties.getError(), errorViewResolvers); - this.serverProperties = serverProperties; + private ResponseEntity> error404; + + + public Admin4jErrorController(ErrorAttributes errorAttributes, ErrorProperties error, List collect) { + super(errorAttributes, error, collect); } @Override @@ -44,8 +42,6 @@ public ResponseEntity> error(HttpServletRequest request) { return super.error(request); } - private ResponseEntity> error404; - public ResponseEntity> error404(HttpServletRequest request) { if (error404 == null) { diff --git a/web-spring-boot-starter/src/main/resources/META-INF/spring.factories b/web-spring-boot-starter/src/main/resources/META-INF/spring.factories index 948bc77..b1da646 100644 --- a/web-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/web-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,4 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.admin4j.framework.web.exception.handler.GlobalExceptionHandler,\ - com.admin4j.framework.web.config.JacksonConfig,\ - com.admin4j.framework.web.config.UserContextConfig \ No newline at end of file + com.admin4j.framework.web.autoconfigure.JacksonAutoConfiguration,\ + com.admin4j.framework.web.autoconfigure.WebAutoConfiguration,\ + com.admin4j.framework.web.autoconfigure.UserContextAutoConfiguration \ No newline at end of file From 650c981e99c27e09c8333023e4543ef2196f1884 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 21 Nov 2023 16:25:16 +0800 Subject: [PATCH 05/30] feat(security-spring-boot-starter): remark --- .../src/main/java/com/admin4j/common/pojo/ResponseEnum.java | 6 ------ .../security/handler/DefaultAuthenticationResult.java | 2 ++ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java b/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java index 55786da..e56820b 100644 --- a/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java +++ b/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java @@ -38,12 +38,6 @@ public enum ResponseEnum implements IResponse, Assert { FAIL_AUTH_FORBIDDEN(403, "FAIL_AUTH_FORBIDDEN"), - /** - * token 认证失败 - */ - FAIL_NO_TOKEN(405, "FAIL_NO_TOKEN"), - - /** * 服务未找到 */ diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java index 7531768..50ff672 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java @@ -67,6 +67,8 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo /** * 认证失败回调 + *

+ * ExceptionHandlerExceptionResolver.resolveException 可以模拟这个方法 */ @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, Exception exception) { From a63812ea6fbf0c49c39756ef2212bc6f5eefc803 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Wed, 22 Nov 2023 09:42:39 +0800 Subject: [PATCH 06/30] feat(security-spring-boot-starter): remark --- admin4j-dependencies/pom.xml | 2 +- security-spring-boot-starter/pom.xml | 2 +- .../exception/JwtTokenExpiredException.java | 30 +++++++++++++++++++ .../filter/JwtAuthenticationTokenFilter.java | 5 ++-- 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/exception/JwtTokenExpiredException.java diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 778e248..350cea0 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -155,7 +155,7 @@ com.admin4j.framework security-spring-boot-starter - 0.9.2 + 0.9.3-SNAPSHOT com.admin4j.framework diff --git a/security-spring-boot-starter/pom.xml b/security-spring-boot-starter/pom.xml index eafc511..22fb2d7 100644 --- a/security-spring-boot-starter/pom.xml +++ b/security-spring-boot-starter/pom.xml @@ -10,7 +10,7 @@ com.admin4j.framework security-spring-boot-starter jar - 0.9.2 + 0.9.3-SNAPSHOT security-spring-boot-starter diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/exception/JwtTokenExpiredException.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/exception/JwtTokenExpiredException.java new file mode 100644 index 0000000..774c1ed --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/exception/JwtTokenExpiredException.java @@ -0,0 +1,30 @@ +package com.admin4j.framework.security.exception; + +import org.springframework.security.core.AuthenticationException; + +/** + * @author andanyang + * @since 2023/11/22 9:38 + */ +public class JwtTokenExpiredException extends AuthenticationException { + /** + * Constructs an {@code AuthenticationException} with the specified message and root + * cause. + * + * @param msg the detail message + * @param cause the root cause + */ + public JwtTokenExpiredException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + * Constructs an {@code AuthenticationException} with the specified message and no + * root cause. + * + * @param msg the detail message + */ + public JwtTokenExpiredException(String msg) { + super(msg); + } +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java index 34ad4e0..5f3102b 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -4,6 +4,7 @@ import com.admin4j.common.util.UserContextUtil; import com.admin4j.framework.security.AuthenticationResult; import com.admin4j.framework.security.UserTokenService; +import com.admin4j.framework.security.exception.JwtTokenExpiredException; import com.admin4j.framework.security.factory.AuthenticationUserFactory; import com.admin4j.framework.security.jwt.JwtUserDetails; import lombok.extern.slf4j.Slf4j; @@ -62,8 +63,8 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse UserContextUtil.setUser(authenticationUser); } } catch (Exception e) { - log.error("onAuthenticationFailure {}", e.getMessage(), e); - authenticationResult.onAuthenticationFailure(request, response, e); + log.error("authenticationEntryPoint {}", e.getMessage(), e); + authenticationResult.authenticationEntryPoint(request, response, new JwtTokenExpiredException(e.getMessage(), e)); return; } From 2a13fb974d34c72316ceb40b822c854aef0e3cbb Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Wed, 29 Nov 2023 18:03:29 +0800 Subject: [PATCH 07/30] feat(tenant): tenant --- admin4j-common/pom.xml | 7 ++- .../common/exception/EncryptException.java | 4 ++ .../admin4j/common/crypto/RSAUtilTest.java | 45 +++++++++++++++++++ admin4j-dependencies/pom.xml | 6 +-- tenant-spring-boot-starter/pom.xml | 1 + .../TFeignAutoConfiguration.java | 14 ++---- .../TTenantFilterAutoConfiguration.java | 23 ++++++++++ .../openfeign/UserContextHolderFilter.java | 19 ++++++-- .../main/resources/META-INF/spring.factories | 3 +- web-spring-boot-starter/pom.xml | 3 +- 10 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 admin4j-common/src/test/java/com/admin4j/common/crypto/RSAUtilTest.java create mode 100644 tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java diff --git a/admin4j-common/pom.xml b/admin4j-common/pom.xml index 8e4d528..e734a81 100644 --- a/admin4j-common/pom.xml +++ b/admin4j-common/pom.xml @@ -1,4 +1,4 @@ - 4.0.0 @@ -23,6 +23,11 @@ org.apache.commons commons-lang3 + + + commons-codec + commons-codec + org.slf4j slf4j-api diff --git a/admin4j-common/src/main/java/com/admin4j/common/exception/EncryptException.java b/admin4j-common/src/main/java/com/admin4j/common/exception/EncryptException.java index 57bb7ef..6718e65 100644 --- a/admin4j-common/src/main/java/com/admin4j/common/exception/EncryptException.java +++ b/admin4j-common/src/main/java/com/admin4j/common/exception/EncryptException.java @@ -9,4 +9,8 @@ public class EncryptException extends SystemException { public EncryptException(String message, Throwable throwable) { super(message, throwable); } + + public EncryptException(Throwable throwable) { + super(throwable); + } } diff --git a/admin4j-common/src/test/java/com/admin4j/common/crypto/RSAUtilTest.java b/admin4j-common/src/test/java/com/admin4j/common/crypto/RSAUtilTest.java new file mode 100644 index 0000000..7d4472d --- /dev/null +++ b/admin4j-common/src/test/java/com/admin4j/common/crypto/RSAUtilTest.java @@ -0,0 +1,45 @@ +// package com.admin4j.common.crypto; +// +// import org.junit.Before; +// import org.junit.Test; +// +// import java.security.NoSuchAlgorithmException; +// import java.util.Base64; +// +// /** +// * @author andanyang +// * @since 2023/11/27 16:26 +// */ +// public class RSAUtilTest { +// private static final String PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqLh8XuAeLvjCly5hesbWtjanpgjVOwNGMPX8lx2937pdP48KlaacrW576lconObZahf60CuNohFTyZrRf9y770tXrC1NF2iDAxZpFjsZChlbftwmk94Okvq/9AkJxyprXZniLZ7fU9gI77sVsKx1wASpaAsLFwSu/y4pgsMa+hESr3r0mRZ1hoVSvKr53EbJfqgtoeeqRYzTZJzbpwKyPhrP2fEIG/9NGgiehp0vIzyFtKET9FcLZZzPi0nA6pVIohZUMS1t/qFdwWuebjBDqfH86ZapqUBYqhWBMr8xBI4vYYH3y02eJwYILpKMlJlEXH55C360+g+Zxg8A9mLZ9QIDAQAB"; +// private static final String PRIVATE_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCouHxe4B4u+MKXLmF6xta2NqemCNU7A0Yw9fyXHb3ful0/jwqVppytbnvqVyic5tlqF/rQK42iEVPJmtF/3LvvS1esLU0XaIMDFmkWOxkKGVt+3CaT3g6S+r/0CQnHKmtdmeItnt9T2AjvuxWwrHXABKloCwsXBK7/LimCwxr6ERKvevSZFnWGhVK8qvncRsl+qC2h56pFjNNknNunArI+Gs/Z8Qgb/00aCJ6GnS8jPIW0oRP0VwtlnM+LScDqlUiiFlQxLW3+oV3Ba55uMEOp8fzplqmpQFiqFYEyvzEEji9hgffLTZ4nBggukoyUmURcfnkLfrT6D5nGDwD2Ytn1AgMBAAECggEAX/zdXQi6g1SpOGN9t+EJ5I6BoJdj4HgDZfV8p+iWzoLzbCaQCgXJG25A91hw1ZsMVCyiV+5/XJXpCkiXKdxF22UM0vrO4iNmbcpBLRGgNDuq7yiGlhd+SSZ5MHg35OSAQrc6k2PQgJ3mr6TLOUFUmFLOok+uFoFmoez8VtVzMpK5dSWJwDETOzjYFIR85dI3V+ZJr4EodtcXlLPQnQnAts0J829Wu+N/LH6PGayRLm3HOe1AVCfIy4uW2EwhMVg3BLE7u2omITWgXwNIe/fHii7uokZkB2uKAHSaBKeyfdynvrZplKpWDsDH9Cz4u2MXQGSeuD+JeMvpKiMKgTFk3QKBgQDVT/V9hDjtmTCPhAEhejJ2Cldo8FSF/rpQB0+WHsai5pUaqKTk6tH62vCXG0QnW1pQVBp8Z8U8ZKfCyKBRdmGShNZ6vjcC0LGfR51cTh3/Rl/ywsfMkK3WcPSn1mDpzse9/zJ1HhGAdIXOe1OXMO9AAuGll4IPY/2nR2mHvF2lLwKBgQDKfBWFpzBFRsY+BlMeF4GzZIMufKVGbaVaNDn9V/3JZpMHkygvjmcF4exz+8GmLF5HsplZJdbXDGg5j+GAATFzMRHmJZOLSFTJlme0Lpye/kzU0JkWOQDnm2XO6LD6f7iomuD1Xcnd/Jto91uD1+E6rzufb9oOw8EJxu/6tGTyGwKBgQDJZKOfLJ3e3Xn2lafHpqpLvfnG7tiuZdAbzLs8PbRGirMNp1l/c6BqWhk6YRjYm6xKGQ2klQinu1SUV3zdTIpUniwtWLdxZf29Jw0P4AT8RcJC3dlrbtFhm+WxLHr1ZDA7VtyZrJjTka/fQZqrLR1FbzMBd2jpBPuv2oFtEM/NKwKBgArQTaXxo9ZPTU8Kr22v+7FE8OyOo5T7ThVfLKmnBVq4K6n/5emERWQ/CI25KEJjpDVYCHCGYM7jTr2kPXrElYt9V2NfJl4N4tlROwCYbKzhD+Fdso9JRA8acXl3W9xE7euzOchg1eMRFouoii6kXNbxfNGq+45GTgzjnvVYpPt5AoGAZ1fE7Dj+y5bhyIl0mQq7DV3Zeoi97E2h7a8Z2SAvQjgk9EPnU4oQLbOdSwsxVSC/jivfFY82tckMcirBxQjz8VAzFy/DNgnlWyN76WeCZgzgyTuOPnRjOCPvvPMWDApSzUY9UwxAY4eqvCaziRMFEMRI+M1Nr0+ICYGC0YuSvec="; +// static RSAUtil.RSAKeyPair rsaKeyPair; +// +// @Before +// public void init() throws NoSuchAlgorithmException { +// rsaKeyPair = RSAUtil.generateKeyPair(2048); +// System.out.println("getPublicKey = " + rsaKeyPair.getPublicKey()); +// System.out.println("getPrivateKey = " + rsaKeyPair.getPrivateKey()); +// } +// +// @Test +// public void testEncrypt() { +// +// // RSAUtil. +// String data = "bec36c76-d315-40bd-af6d-cf32feee49ae-221"; +// byte[] keyBytes = PRIVATE_KEY.getBytes(); +// +// byte[] encrypt = RSAUtil.encrypt(data.getBytes(), RSAUtil.getPrivateKey(keyBytes)); +// System.out.println("encrypt = " + Base64.getEncoder().encodeToString(encrypt)); +// +// // // // TODO TEST +// // String decrypt = RSAUtil.decrypt(encrypt, PUBLIC_KEY); +// // System.out.println("decrypt = " + decrypt); +// // assert encrypt.equals(decrypt); +// } +// +// public void testDecrypt() { +// } +// +// +// } \ No newline at end of file diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 350cea0..b23b0c6 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -125,7 +125,7 @@ com.admin4j.framework tenant-spring-boot-starter - ${admin4j.version} + 0.9.3-SNAPSHOT com.admin4j.framework @@ -220,7 +220,7 @@ com.admin4j.framework web-spring-boot-starter - 0.9.2 + 0.9.3-SNAPSHOT com.admin4j.framework @@ -311,7 +311,7 @@ io.github.admin4j common-http-starter - 0.8.1 + 0.9.1 com.xuxueli diff --git a/tenant-spring-boot-starter/pom.xml b/tenant-spring-boot-starter/pom.xml index 274c5c1..114a7d0 100644 --- a/tenant-spring-boot-starter/pom.xml +++ b/tenant-spring-boot-starter/pom.xml @@ -9,6 +9,7 @@ com.admin4j.framework tenant-spring-boot-starter + 0.9.3-SNAPSHOT tenant-spring-boot-starter 多各大中间件的,多租户能力的适配 diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java index 22e47dd..f212031 100644 --- a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java @@ -2,17 +2,16 @@ import com.admin4j.common.service.IUserContextHolder; import com.admin4j.framework.tenant.openfeign.FeignRequestInterceptor; -import com.admin4j.framework.tenant.openfeign.UserContextHolderFilter; -import feign.RequestInterceptor; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; -import org.springframework.core.annotation.Order; /** * @author andanyang * @since 2023/9/20 9:42 */ -@ConditionalOnBean(RequestInterceptor.class) +@AutoConfigureOrder(Integer.MAX_VALUE) +@ConditionalOnBean(value = IUserContextHolder.class, name = "feignAutoConfiguration") public class TFeignAutoConfiguration { @Bean @@ -20,11 +19,4 @@ public class TFeignAutoConfiguration { public FeignRequestInterceptor feignRequestInterceptor(IUserContextHolder userContextHolder) { return new FeignRequestInterceptor(userContextHolder); } - - @Bean - @ConditionalOnBean(IUserContextHolder.class) - @Order - public UserContextHolderFilter userContextHolderFilter(IUserContextHolder userContextHolder) { - return new UserContextHolderFilter(userContextHolder); - } } diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java new file mode 100644 index 0000000..590d2fb --- /dev/null +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java @@ -0,0 +1,23 @@ +package com.admin4j.framework.tenant.autoconfigure; + +import com.admin4j.common.service.IUserContextHolder; +import com.admin4j.framework.tenant.openfeign.UserContextHolderFilter; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.Order; + +/** + * @author andanyang + * @since 2023/9/20 9:42 + */ +@AutoConfigureOrder(Integer.MAX_VALUE) +public class TTenantFilterAutoConfiguration { + + @Bean + @ConditionalOnBean(IUserContextHolder.class) + @Order + public UserContextHolderFilter userContextHolderFilter(IUserContextHolder userContextHolder) { + return new UserContextHolderFilter(userContextHolder); + } +} diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/openfeign/UserContextHolderFilter.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/openfeign/UserContextHolderFilter.java index 3c470d5..15898ef 100644 --- a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/openfeign/UserContextHolderFilter.java +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/openfeign/UserContextHolderFilter.java @@ -5,12 +5,14 @@ import com.admin4j.common.util.UserContextUtil; import com.admin4j.framework.tenant.TenantConstant; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; import org.springframework.web.filter.GenericFilterBean; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -21,6 +23,9 @@ * @since 2023/9/20 9:29 */ @RequiredArgsConstructor +@WebFilter(filterName = "UserContextHolderFilter", + urlPatterns = "/*" +) public class UserContextHolderFilter extends GenericFilterBean { private final IUserContextHolder userContextHolder; @@ -28,15 +33,21 @@ public class UserContextHolderFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + // TODO 限制IP HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; try { - String userInfo = URLDecoder.decode(request.getHeader(TenantConstant.USER_INFO), "UTF-8"); - AuthenticationUser authenticationUser = userContextHolder.decode(userInfo); - if (authenticationUser != null) { - UserContextUtil.setUser(authenticationUser); + String header = request.getHeader(TenantConstant.USER_INFO); + if (StringUtils.isNotBlank(header)) { + + String userInfo = URLDecoder.decode(header, "UTF-8"); + AuthenticationUser authenticationUser = userContextHolder.decode(userInfo); + if (authenticationUser != null) { + UserContextUtil.setUser(authenticationUser); + } } + filterChain.doFilter(request, response); } finally { UserContextUtil.clear(); diff --git a/tenant-spring-boot-starter/src/main/resources/META-INF/spring.factories b/tenant-spring-boot-starter/src/main/resources/META-INF/spring.factories index 294c72e..60c5dd1 100644 --- a/tenant-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/tenant-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,7 +1,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.admin4j.framework.tenant.autoconfigure.TFeignAutoConfiguration,\ com.admin4j.framework.tenant.autoconfigure.TXxlJobAutoConfiguration,\ - com.admin4j.framework.tenant.autoconfigure.TRocketMQAutoConfiguration + com.admin4j.framework.tenant.autoconfigure.TRocketMQAutoConfiguration,\ + com.admin4j.framework.tenant.autoconfigure.TTenantFilterAutoConfiguration diff --git a/web-spring-boot-starter/pom.xml b/web-spring-boot-starter/pom.xml index 5a40c70..8cb145f 100644 --- a/web-spring-boot-starter/pom.xml +++ b/web-spring-boot-starter/pom.xml @@ -12,7 +12,7 @@ com.admin4j.framework web-spring-boot-starter ${project.artifactId} - 0.9.2 + 0.9.3-SNAPSHOT @@ -32,6 +32,7 @@ com.admin4j.framework mybatis-plus-boot-starter + provided com.admin4j.common From 21027e52dc312b9632486c091cd0123aef5cb180 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Wed, 6 Dec 2023 08:49:15 +0800 Subject: [PATCH 08/30] feat(security-spring-boot-starter): security --- .../admin4j/common/constant/WebConstant.java | 13 +++++ admin4j-dependencies/pom.xml | 4 +- elasticsearch-spring-boot-starter/pom.xml | 12 ++--- .../elasticsearch/ElasticsearchConfig.java | 11 +++-- .../src/test/java/com/admin4j/AppTest.java | 38 --------------- .../UserTokenServiceConfiguration.java | 3 ++ .../framework/tenant/TenantConstant.java | 6 ++- .../framework/tenant/TenantUserFactory.java | 8 ++-- .../TFeignAutoConfiguration.java | 3 +- .../TRocketMQAutoConfiguration.java | 11 ++++- .../TTenantFilterAutoConfiguration.java | 5 ++ .../tenant/prop/TenantProperties.java | 19 ++++++++ .../tenant/rocketmq/MqSendMessageHook.java | 47 +++++++++++++++++++ .../UserContextAutoConfiguration.java | 3 ++ 14 files changed, 123 insertions(+), 60 deletions(-) create mode 100644 admin4j-common-spring-web/src/main/java/com/admin4j/common/constant/WebConstant.java delete mode 100644 elasticsearch-spring-boot-starter/src/test/java/com/admin4j/AppTest.java create mode 100644 tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/prop/TenantProperties.java create mode 100644 tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/rocketmq/MqSendMessageHook.java diff --git a/admin4j-common-spring-web/src/main/java/com/admin4j/common/constant/WebConstant.java b/admin4j-common-spring-web/src/main/java/com/admin4j/common/constant/WebConstant.java new file mode 100644 index 0000000..f48700b --- /dev/null +++ b/admin4j-common-spring-web/src/main/java/com/admin4j/common/constant/WebConstant.java @@ -0,0 +1,13 @@ +package com.admin4j.common.constant; + +/** + * @author andanyang + * @since 2023/12/1 11:18 + */ +public final class WebConstant { + + /** + * IUserContextHolder 接口再spring中初始化顺序 + */ + public static final int IUserContextHolderOrder = -1; +} diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index b23b0c6..143ae35 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -98,7 +98,7 @@ org.apache.rocketmq rocketmq-spring-boot-starter - 2.2.1 + 2.2.3 jakarta.json @@ -165,7 +165,7 @@ com.admin4j.framework elasticsearch-spring-boot-starter - 7.x.1 + 7.x.2 com.admin4j.redis diff --git a/elasticsearch-spring-boot-starter/pom.xml b/elasticsearch-spring-boot-starter/pom.xml index b452128..b4cab0d 100644 --- a/elasticsearch-spring-boot-starter/pom.xml +++ b/elasticsearch-spring-boot-starter/pom.xml @@ -10,13 +10,13 @@ com.admin4j.framework elasticsearch-spring-boot-starter jar - 7.x.1 + 7.x.2 elasticsearch Spring boot 自动装配器 elasticsearch-spring-boot-starter - 8.7.1 + 8.11.1 UTF-8 @@ -24,7 +24,7 @@ io.github.hakky54 sslcontext-kickstart - 7.4.6 + 8.2.0 co.elastic.clients @@ -47,14 +47,10 @@ jakarta.json-api 2.1.2 - - junit - junit - test - org.springframework.boot spring-boot-autoconfigure + provided diff --git a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/ElasticsearchConfig.java b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/ElasticsearchConfig.java index 0b3cc4f..6478978 100644 --- a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/ElasticsearchConfig.java +++ b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/ElasticsearchConfig.java @@ -4,6 +4,7 @@ import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.rest_client.RestClientTransport; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import nl.altindag.ssl.SSLFactory; import org.apache.http.HttpHost; @@ -30,6 +31,8 @@ @Data @EnableConfigurationProperties(ElasticsearchProperties.class) public class ElasticsearchConfig { + @Autowired + private ObjectMapper objectMapper; /** * elasticsearch url. * 集群使用英文逗号隔开 @@ -39,8 +42,8 @@ public class ElasticsearchConfig { @Bean public ElasticsearchClient client() { - //解析hostlist配置信息 - //创建HttpHost数组,其中存放es主机和端口的配置信息 + // 解析hostlist配置信息 + // 创建HttpHost数组,其中存放es主机和端口的配置信息 HttpHost[] httpHostArray = new HttpHost[elasticsearchProperties.getUris().size()]; @@ -67,7 +70,7 @@ public ElasticsearchClient client() { .build(); // Create the low-level client - //添加认证 + // 添加认证 if (elasticsearchProperties.getUsername() != null) { final CredentialsProvider provider = new BasicCredentialsProvider(); provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(elasticsearchProperties.getUsername(), elasticsearchProperties.getPassword())); @@ -87,7 +90,7 @@ public ElasticsearchClient client() { RestClient restClient = builder.build(); // Create the transport with a Jackson mapper - ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); + ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper)); // And create the API client return new ElasticsearchClient(transport); diff --git a/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/AppTest.java b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/AppTest.java deleted file mode 100644 index 02cc84c..0000000 --- a/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.admin4j; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java index 7ef4647..58345e7 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java @@ -1,5 +1,6 @@ package com.admin4j.framework.security.configuration; +import com.admin4j.common.constant.WebConstant; import com.admin4j.common.service.IUserContextHolder; import com.admin4j.framework.security.UserTokenService; import com.admin4j.framework.security.context.SecurityUserContextHolder; @@ -18,6 +19,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.Order; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -69,6 +71,7 @@ public UsernamePasswordUserDetailsService usernamePasswordUserDetailsService( @Bean @ConditionalOnMissingBean(IUserContextHolder.class) @ConditionalOnClass(name = "com.admin4j.common.service.IUserContextHolder") + @Order(WebConstant.IUserContextHolderOrder) public SecurityUserContextHolder securityUserContextHolder() { return new SecurityUserContextHolder(); } diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantConstant.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantConstant.java index f94cbd4..5f8d3f3 100644 --- a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantConstant.java +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantConstant.java @@ -5,8 +5,12 @@ * @since 2023/9/20 9:19 */ public class TenantConstant { - + public static final String USER_INFO = "USER_INFO"; + public static final Long JOB_USER_ID = 1000L; + public static final String JOB_USER_NAME = "job"; + public static final Long MQ_USER_ID = 1001L; + public static final String MQ_USER_NAME = "mq"; private TenantConstant() { } diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantUserFactory.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantUserFactory.java index c3e1475..70e45b6 100644 --- a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantUserFactory.java +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/TenantUserFactory.java @@ -11,8 +11,8 @@ public class TenantUserFactory { public static AuthenticationUser jobUser(Long tenantId) { AuthenticationUser authenticationUser = new AuthenticationUser(); - authenticationUser.setUserId(1000L); - authenticationUser.setUsername("job"); + authenticationUser.setUserId(TenantConstant.JOB_USER_ID); + authenticationUser.setUsername(TenantConstant.JOB_USER_NAME); authenticationUser.setTenantId(tenantId); authenticationUser.setAdmin(true); return authenticationUser; @@ -21,8 +21,8 @@ public static AuthenticationUser jobUser(Long tenantId) { public static AuthenticationUser mqUser(Long tenantId) { AuthenticationUser authenticationUser = new AuthenticationUser(); - authenticationUser.setUserId(1001L); - authenticationUser.setUsername("mq"); + authenticationUser.setUserId(TenantConstant.MQ_USER_ID); + authenticationUser.setUsername(TenantConstant.MQ_USER_NAME); authenticationUser.setTenantId(tenantId); authenticationUser.setAdmin(true); return authenticationUser; diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java index f212031..66a993c 100644 --- a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TFeignAutoConfiguration.java @@ -4,6 +4,7 @@ import com.admin4j.framework.tenant.openfeign.FeignRequestInterceptor; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.context.annotation.Bean; /** @@ -11,7 +12,7 @@ * @since 2023/9/20 9:42 */ @AutoConfigureOrder(Integer.MAX_VALUE) -@ConditionalOnBean(value = IUserContextHolder.class, name = "feignAutoConfiguration") +@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration") public class TFeignAutoConfiguration { @Bean diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TRocketMQAutoConfiguration.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TRocketMQAutoConfiguration.java index 22bb3dd..accc0c2 100644 --- a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TRocketMQAutoConfiguration.java +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TRocketMQAutoConfiguration.java @@ -1,7 +1,8 @@ package com.admin4j.framework.tenant.autoconfigure; +import com.admin4j.common.service.IUserContextHolder; +import com.admin4j.framework.tenant.rocketmq.MqSendMessageHook; import com.admin4j.framework.tenant.rocketmq.RocketmqBeanPostProcessor; -import org.apache.rocketmq.client.hook.ConsumeMessageHook; import org.apache.rocketmq.client.hook.SendMessageHook; import org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -18,8 +19,14 @@ public class TRocketMQAutoConfiguration { @Bean - @ConditionalOnBean(value = {SendMessageHook.class, ConsumeMessageHook.class}) + // @ConditionalOnBean(value = {SendMessageHook.class, ConsumeMessageHook.class}) public RocketmqBeanPostProcessor rocketmqBeanPostProcessor() { return new RocketmqBeanPostProcessor(); } + + @Bean + @ConditionalOnBean(IUserContextHolder.class) + public SendMessageHook mqSendMessageHook(IUserContextHolder userContextHolder) { + return new MqSendMessageHook(userContextHolder); + } } diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java index 590d2fb..1f15d60 100644 --- a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/autoconfigure/TTenantFilterAutoConfiguration.java @@ -2,8 +2,11 @@ import com.admin4j.common.service.IUserContextHolder; import com.admin4j.framework.tenant.openfeign.UserContextHolderFilter; +import com.admin4j.framework.tenant.prop.TenantProperties; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; @@ -12,11 +15,13 @@ * @since 2023/9/20 9:42 */ @AutoConfigureOrder(Integer.MAX_VALUE) +@EnableConfigurationProperties(TenantProperties.class) public class TTenantFilterAutoConfiguration { @Bean @ConditionalOnBean(IUserContextHolder.class) @Order + @ConditionalOnProperty(value = "admin4j.tenant.servletFilter", matchIfMissing = true) public UserContextHolderFilter userContextHolderFilter(IUserContextHolder userContextHolder) { return new UserContextHolderFilter(userContextHolder); } diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/prop/TenantProperties.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/prop/TenantProperties.java new file mode 100644 index 0000000..bf366fb --- /dev/null +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/prop/TenantProperties.java @@ -0,0 +1,19 @@ +package com.admin4j.framework.tenant.prop; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author andanyang + * @since 2023/12/1 11:13 + */ +@Data +@ConfigurationProperties(prefix = "admin4j.tenant") +public class TenantProperties { + + + /** + * 是否开启 servletFilter 的租户过滤 + */ + private boolean servletFilter = true; +} diff --git a/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/rocketmq/MqSendMessageHook.java b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/rocketmq/MqSendMessageHook.java new file mode 100644 index 0000000..3e856a4 --- /dev/null +++ b/tenant-spring-boot-starter/src/main/java/com/admin4j/framework/tenant/rocketmq/MqSendMessageHook.java @@ -0,0 +1,47 @@ +package com.admin4j.framework.tenant.rocketmq; + +import com.admin4j.common.pojo.AuthenticationUser; +import com.admin4j.common.service.IUserContextHolder; +import com.admin4j.framework.tenant.TenantConstant; +import com.alibaba.fastjson2.JSONObject; +import lombok.RequiredArgsConstructor; +import org.apache.rocketmq.client.hook.SendMessageContext; +import org.apache.rocketmq.client.hook.SendMessageHook; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author andanyang + * @since 2023/12/1 11:04 + */ +@RequiredArgsConstructor +public class MqSendMessageHook implements SendMessageHook { + + private final IUserContextHolder userContextHolder; + + @Override + public String hookName() { + return "tenant"; + } + + @Override + public void sendMessageBefore(SendMessageContext context) { + + AuthenticationUser user = userContextHolder.getAuthenticationUser(); + if (user != null) { + + Map props = context.getProps(); + if (props == null) { + props = new HashMap<>(); + } + props.put(TenantConstant.USER_INFO, JSONObject.toJSONString(user)); + context.setProps(props); + } + } + + @Override + public void sendMessageAfter(SendMessageContext context) { + + } +} diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java index e1e449a..a845c8a 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java @@ -1,7 +1,9 @@ package com.admin4j.framework.web.autoconfigure; +import com.admin4j.common.constant.WebConstant; import com.admin4j.common.service.IUserContextHolder; import com.admin4j.framework.web.SimpleUserContextHolder; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -12,6 +14,7 @@ * @since 2023/9/15 9:19 */ @Configuration +@AutoConfigureOrder(WebConstant.IUserContextHolderOrder + 2) public class UserContextAutoConfiguration { @Bean From 58af7f50d15ac97d311f255d4a96b162c301d842 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Wed, 6 Dec 2023 14:38:28 +0800 Subject: [PATCH 09/30] feat(security-spring-boot-starter): security --- .../security/configuration/SecurityConfiguration.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java index 7d43d3d..d6615da 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java @@ -13,7 +13,9 @@ import com.admin4j.framework.security.properties.JwtProperties; import com.admin4j.framework.security.properties.MultiAuthenticationProperties; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; @@ -44,6 +46,7 @@ */ @EnableGlobalMethodSecurity(prePostEnabled = true) @EnableConfigurationProperties({IgnoringUrlProperties.class, JwtProperties.class, FormLoginProperties.class, MultiAuthenticationProperties.class}) +@AutoConfigureBefore(UserDetailsServiceAutoConfiguration.class) public class SecurityConfiguration { From c9d87dd2b122bea370c8293338713a87622e1e20 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Thu, 7 Dec 2023 16:29:08 +0800 Subject: [PATCH 10/30] =?UTF-8?q?feat(elasticsearch):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20JsonpMapper=20=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- elasticsearch-spring-boot-starter/pom.xml | 4 +++ .../ElasticsearchAutoconfigure.java} | 22 +++++++++---- .../JsonpMapperAutoconfigure.java | 32 +++++++++++++++++++ .../properties/ElasticsearchProperties.java | 7 ++-- .../main/resources/META-INF/spring.factories | 3 +- .../configuration/SecurityConfiguration.java | 4 +++ 6 files changed, 61 insertions(+), 11 deletions(-) rename elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/{ElasticsearchConfig.java => autoconfigure/ElasticsearchAutoconfigure.java} (82%) create mode 100644 elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java diff --git a/elasticsearch-spring-boot-starter/pom.xml b/elasticsearch-spring-boot-starter/pom.xml index b4cab0d..f634d1f 100644 --- a/elasticsearch-spring-boot-starter/pom.xml +++ b/elasticsearch-spring-boot-starter/pom.xml @@ -47,6 +47,10 @@ jakarta.json-api 2.1.2 + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + org.springframework.boot spring-boot-autoconfigure diff --git a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/ElasticsearchConfig.java b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/ElasticsearchAutoconfigure.java similarity index 82% rename from elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/ElasticsearchConfig.java rename to elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/ElasticsearchAutoconfigure.java index 6478978..c9af929 100644 --- a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/ElasticsearchConfig.java +++ b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/ElasticsearchAutoconfigure.java @@ -1,6 +1,7 @@ -package com.admin4j.elasticsearch; +package com.admin4j.elasticsearch.autoconfigure; import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.transport.ElasticsearchTransport; import co.elastic.clients.transport.rest_client.RestClientTransport; @@ -19,6 +20,7 @@ import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Lazy; import java.net.URI; @@ -30,9 +32,8 @@ */ @Data @EnableConfigurationProperties(ElasticsearchProperties.class) -public class ElasticsearchConfig { - @Autowired - private ObjectMapper objectMapper; +public class ElasticsearchAutoconfigure { + /** * elasticsearch url. * 集群使用英文逗号隔开 @@ -41,7 +42,8 @@ public class ElasticsearchConfig { private ElasticsearchProperties elasticsearchProperties; @Bean - public ElasticsearchClient client() { + @Lazy + public ElasticsearchClient client(@Autowired(required = false) JsonpMapper jsonpMapper, @Autowired ObjectMapper objectMapper) { // 解析hostlist配置信息 // 创建HttpHost数组,其中存放es主机和端口的配置信息 HttpHost[] httpHostArray = new HttpHost[elasticsearchProperties.getUris().size()]; @@ -89,8 +91,16 @@ public ElasticsearchClient client() { RestClient restClient = builder.build(); + ElasticsearchTransport transport = null; + if (jsonpMapper != null) { + transport = new RestClientTransport(restClient, jsonpMapper); + } else if (objectMapper != null) { + transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper)); + } else { + transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); + } // Create the transport with a Jackson mapper - ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper)); + // And create the API client return new ElasticsearchClient(transport); diff --git a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java new file mode 100644 index 0000000..bde4ff9 --- /dev/null +++ b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java @@ -0,0 +1,32 @@ +package com.admin4j.elasticsearch.autoconfigure; + +import co.elastic.clients.json.JsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +/** + * @author andanyang + * @since 2023/12/7 16:18 + */ +@ConditionalOnClass(name = "com.fasterxml.jackson.databind.ObjectMapper") +public class JsonpMapperAutoconfigure { + + @Bean + @ConditionalOnMissingBean(JsonpMapper.class) + public JsonpMapper esJsonpMapper() { + + ObjectMapper objectMapper = new ObjectMapper() + .configure(SerializationFeature.INDENT_OUTPUT, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL); + + objectMapper.registerModule(new JavaTimeModule()); + + return new JacksonJsonpMapper(objectMapper); + } +} diff --git a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java index 27a5a38..bc504ae 100644 --- a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java +++ b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java @@ -1,15 +1,14 @@ package com.admin4j.elasticsearch.properties; import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author andanyang * @since 2023/4/18 11:31 */ @Data -@ConfigurationProperties(prefix = "admin4j.elasticsearc") - +@Deprecated +// @ConfigurationProperties(prefix = "admin4j.elasticsearc") public class ElasticsearchProperties { /** @@ -26,5 +25,5 @@ public class ElasticsearchProperties { */ private String password; - + } diff --git a/elasticsearch-spring-boot-starter/src/main/resources/META-INF/spring.factories b/elasticsearch-spring-boot-starter/src/main/resources/META-INF/spring.factories index 69334fc..0fe97da 100644 --- a/elasticsearch-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/elasticsearch-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,2 +1,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.admin4j.elasticsearch.ElasticsearchConfig \ No newline at end of file + com.admin4j.elasticsearch.autoconfigure.ElasticsearchAutoconfigure,\ + com.admin4j.elasticsearch.autoconfigure.JsonpMapperAutoconfigure diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java index d6615da..61a82ad 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java @@ -37,6 +37,10 @@ import java.util.Map; /** + * TODO 需要注入,取消 UserDetailsServiceAutoConfiguration 开启 + * value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, + * AuthenticationManagerResolver.class }, + * * @author andanyang * @since 2023/3/24 16:34 */ From ff49fa59e8078130830a8a738afba12fea23580a Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Thu, 7 Dec 2023 17:04:44 +0800 Subject: [PATCH 11/30] =?UTF-8?q?feat(elasticsearch):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20JsonpMapper=20=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- elasticsearch-spring-boot-starter/pom.xml | 4 +++ .../JsonpMapperAutoconfigure.java | 31 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/elasticsearch-spring-boot-starter/pom.xml b/elasticsearch-spring-boot-starter/pom.xml index f634d1f..91dbef1 100644 --- a/elasticsearch-spring-boot-starter/pom.xml +++ b/elasticsearch-spring-boot-starter/pom.xml @@ -56,5 +56,9 @@ spring-boot-autoconfigure provided + + com.admin4j.common + admin4j-common + diff --git a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java index bde4ff9..d612340 100644 --- a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java +++ b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/JsonpMapperAutoconfigure.java @@ -2,14 +2,26 @@ import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import com.admin4j.common.time.DateFormatterPattern; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.TimeZone; + /** * @author andanyang * @since 2023/12/7 16:18 @@ -20,12 +32,25 @@ public class JsonpMapperAutoconfigure { @Bean @ConditionalOnMissingBean(JsonpMapper.class) public JsonpMapper esJsonpMapper() { - + ObjectMapper objectMapper = new ObjectMapper() .configure(SerializationFeature.INDENT_OUTPUT, false) - .setSerializationInclusion(JsonInclude.Include.NON_NULL); + .configure(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, false) + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL).setTimeZone(TimeZone.getDefault()); + + JavaTimeModule module = new JavaTimeModule(); + + module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateFormatterPattern.NORM_DATETIME_FORMATTER)); + module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateFormatterPattern.NORM_DATETIME_FORMATTER)); + + module.addSerializer(LocalDate.class, new LocalDateSerializer(DateFormatterPattern.NORM_DATE_FORMATTER)); + module.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateFormatterPattern.NORM_DATE_FORMATTER)); + + module.addSerializer(LocalTime.class, new LocalTimeSerializer(DateFormatterPattern.NORM_TIME_FORMATTER)); + module.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateFormatterPattern.NORM_TIME_FORMATTER)); - objectMapper.registerModule(new JavaTimeModule()); + objectMapper.registerModule(module); return new JacksonJsonpMapper(objectMapper); } From f27b1a13bce586f0e558e26af14bd288fd1c7322 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Thu, 7 Dec 2023 17:38:10 +0800 Subject: [PATCH 12/30] =?UTF-8?q?feat(elasticsearch):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20JsonpMapper=20=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin4j-dependencies/pom.xml | 2 +- elasticsearch-spring-boot-starter/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 143ae35..43f77bd 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -165,7 +165,7 @@ com.admin4j.framework elasticsearch-spring-boot-starter - 7.x.2 + 8.x.2 com.admin4j.redis diff --git a/elasticsearch-spring-boot-starter/pom.xml b/elasticsearch-spring-boot-starter/pom.xml index 91dbef1..fb32a86 100644 --- a/elasticsearch-spring-boot-starter/pom.xml +++ b/elasticsearch-spring-boot-starter/pom.xml @@ -10,7 +10,7 @@ com.admin4j.framework elasticsearch-spring-boot-starter jar - 7.x.2 + 8.x.2 elasticsearch Spring boot 自动装配器 elasticsearch-spring-boot-starter From 3f74c96fee169bc51ec66fa72d2816590a09a3ba Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Thu, 7 Dec 2023 18:24:43 +0800 Subject: [PATCH 13/30] =?UTF-8?q?fix(CommonPageFactory):=20=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E5=BD=93=E5=89=8D=E9=A1=B5=E6=80=BB=E6=95=B0=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/admin4j/framework/web/factory/CommonPageFactory.java | 4 ++-- .../admin4j/framework/web/factory/CommonPageMpFactory.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageFactory.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageFactory.java index 6505ee3..f1e7c39 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageFactory.java +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageFactory.java @@ -23,7 +23,7 @@ public static CommonPage ok(Page pageInfo) { pageResultVO.setTotal(pageInfo.getTotal()); pageResultVO.setCurrent(pageInfo.getPageNum()); - pageResultVO.setSize(pageInfo.getPageSize()); + pageResultVO.setSize(pageInfo.size()); pageResultVO.setRecords(pageInfo); return CommonPage.ok(pageResultVO); @@ -56,7 +56,7 @@ public static CommonPage ok(Page pageInfo, List list) { pageResultVO.setTotal(pageInfo.getTotal()); pageResultVO.setCurrent(pageInfo.getPageNum()); - pageResultVO.setSize(pageInfo.getPageSize()); + pageResultVO.setSize(pageInfo.size()); pageResultVO.setRecords(list); return CommonPage.ok(pageResultVO); diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageMpFactory.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageMpFactory.java index 9228ea1..927312e 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageMpFactory.java +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/factory/CommonPageMpFactory.java @@ -19,7 +19,7 @@ public static CommonPage ok(IPage pageInfo) { pageResultVO.setTotal(pageInfo.getTotal()); pageResultVO.setCurrent(pageInfo.getCurrent()); - pageResultVO.setSize(pageInfo.getSize()); + pageResultVO.setSize(pageInfo.getRecords() == null ? 0 : pageInfo.getRecords().size()); pageResultVO.setRecords(pageInfo.getRecords()); return CommonPage.ok(pageResultVO); @@ -51,7 +51,7 @@ public static CommonPage ok(IPage pageInfo, List list) { pageResultVO.setTotal(pageInfo.getTotal()); pageResultVO.setCurrent(pageInfo.getCurrent()); - pageResultVO.setSize(pageInfo.getSize()); + pageResultVO.setSize(pageInfo.getRecords() == null ? 0 : pageInfo.getRecords().size()); pageResultVO.setRecords(list); return CommonPage.ok(pageResultVO); From 4f40b0c3219bbbe386b2009c1dde8187d4d5d599 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Mon, 11 Dec 2023 17:25:46 +0800 Subject: [PATCH 14/30] feat(dict): version --- admin4j-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 43f77bd..d71ba41 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -110,7 +110,7 @@ com.admin4j.dict dict-spring-boot-starter - 0.9.0 + 0.9.3 com.admin4j.framework From ece6c16d1dbb311e6c9363b7e7bb6a257467bf30 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Mon, 11 Dec 2023 17:54:20 +0800 Subject: [PATCH 15/30] feat(dict): version --- admin4j-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index d71ba41..454a66d 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -110,7 +110,7 @@ com.admin4j.dict dict-spring-boot-starter - 0.9.3 + 0.9.4 com.admin4j.framework From d356853946a0d544ac2985bab180326152678414 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Wed, 13 Dec 2023 17:22:32 +0800 Subject: [PATCH 16/30] =?UTF-8?q?feat(security):=20CorsFilter=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20order=20=E6=9C=80=E6=96=B0=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E5=85=B6=E4=BB=96filter=E6=8F=90?= =?UTF-8?q?=E5=89=8D=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/security/configuration/CorsConfiguration.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/CorsConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/CorsConfiguration.java index 94d6832..4a14a90 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/CorsConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/CorsConfiguration.java @@ -4,6 +4,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @@ -16,6 +18,7 @@ public class CorsConfiguration { @Bean @ConditionalOnProperty(prefix = "admin4j.security.cors", name = "enabled", matchIfMissing = true) + @Order(Ordered.HIGHEST_PRECEDENCE) public CorsFilter corsFilter(CorsProperties properties) { org.springframework.web.cors.CorsConfiguration config = new org.springframework.web.cors.CorsConfiguration(); From 5a79d0849e0bb0876b18c0c73f12a96b2482e2ad Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Fri, 15 Dec 2023 13:41:39 +0800 Subject: [PATCH 17/30] feat(elasticsearch): elasticsearch version --- admin4j-dependencies/pom.xml | 2 +- elasticsearch-spring-boot-starter/README.md | 1453 ++++++++++++++++- elasticsearch-spring-boot-starter/pom.xml | 8 +- .../ElasticsearchAutoconfigure.java | 2 +- .../properties/ElasticsearchProperties.java | 29 - .../com/admin4j/elasticsearch/ESDocTest.java | 129 ++ .../admin4j/elasticsearch/ESIndexTest.java | 106 ++ .../admin4j/elasticsearch/ESSearchTest.java | 98 ++ .../ElasticsearchApplication.java | 15 + .../admin4j/elasticsearch/entity/Product.java | 19 + .../src/test/resources/application.yml | 9 + 11 files changed, 1796 insertions(+), 74 deletions(-) delete mode 100644 elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java create mode 100644 elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESDocTest.java create mode 100644 elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESIndexTest.java create mode 100644 elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESSearchTest.java create mode 100644 elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ElasticsearchApplication.java create mode 100644 elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/entity/Product.java create mode 100644 elasticsearch-spring-boot-starter/src/test/resources/application.yml diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 454a66d..e1f304d 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -165,7 +165,7 @@ com.admin4j.framework elasticsearch-spring-boot-starter - 8.x.2 + 8.x.3 com.admin4j.redis diff --git a/elasticsearch-spring-boot-starter/README.md b/elasticsearch-spring-boot-starter/README.md index d13f3fc..6f3e999 100644 --- a/elasticsearch-spring-boot-starter/README.md +++ b/elasticsearch-spring-boot-starter/README.md @@ -1,58 +1,1427 @@ -# Elasticsearch +一、ES Client 简介 +============== -## Usage +1. ES 是一个服务,采用 C/S 结构 -```xml +![](https://img-blog.csdnimg.cn/img_convert/0de678a3c03729336e140e3ec38e318b.png) - - com.admin4j.framework - elasticsearch-spring-boot-starter - 0.2.0 - +## 2. 回顾 ES 的架构 + +![](https://img-blog.csdnimg.cn/img_convert/8d83b03a364c488e20f703aadc8eb503.png) + +## 3. ES 支持的客户端连接方式 + +### **3.1 REST API ,端口 9200** + +这种连接方式对应于架构图中的 RESTful style API 这一层,这种客户端的连接方式是 RESTful 风格的,使用 http 的方式进行连接 + +![](https://img-blog.csdnimg.cn/img_convert/ea451f7b50cbb284b27f04922451663d.png) + +### **3.2 Transport 连接 端口 9300** + +这种连接方式对应于架构图中的 Transport 这一层,这种客户端连接方式是直接连接 ES 的节点,使用 TCP 的方式进行连接 + +![](https://img-blog.csdnimg.cn/img_convert/e2e0bbe76ded9658d838e4282444a916.png) + +## 4. ES 提供了多种编程语言客户端 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/72374263d91d4f25a8e09c5a3c105144.png) + +官网可以了解详情: + +https://www.elastic.co/guide/en/elasticsearch/client/index.html + +我们在学习ES客户端时,一直使用的都是Java High Level Rest Client,在浏览官网时,发现官方给出的警告是:Java REST +客户端已被弃用,取而代之的是 Java API client 客户端,ES 8.x 新版本中,Type 概念被弃用,所以新版 JavaAPI 也相应做出了改变,使用更加简便。ES +官方从 7.15 起开始建议使用新的 JavaAPI + +如何使用最新的 Elasticsearch Java client 8.0 来创建索引并进行搜索。最新的 Elasticsearch Java client API +和之前的不同。在es7的一些教程中,经常使用 High Level API 来进行操作。但在官方文档中,已经显示为 deprecated。 + + + +二、Java REST Client 介绍 +===================== + +## 1. ES 提供了两个 JAVA REST client 版本 + +**Java Low Level REST Client:** 低级别的 REST 客户端,通过 http 与集群交互,用户需自己编组请求 JSON 串,及解析响应 JSON 串。 +**兼容所有 ES 版本**。 +**Java High Level REST Client:** 高级别的 REST 客户端,基于低级别的 REST 客户端,增加了编组请求 JSON 串、解析响应 JSON 串等相关 +api。**使用的版本需要保持和 ES 服务端的版本一致,否则会有版本问题。** + +## 2. Java Low Level REST Client 说明 + +特点,maven 引入、使用介绍: https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low.html + +API doc :https://artifacts.elastic.co/javadoc/org/elasticsearch/client/elasticsearch-rest-client/6.2.4/index.html. + +过期的客户端就讲到这里 + +# 三、**Java API Client** + +官网为啥又推出一个新的客户端接口呢,这是为了统一管理,官网给出的回应是:将来只对这个客户端进行维护改进,这也接口会更加的清晰明了,可读性更高,更易于上手,更简单!代码看着更加简洁了! + +无论是ElasticsearchTemplate类还是ElasticsearchRepository接口,都是对ES常用的简单功能进行封装,在实际使用时,复杂的查询语法还是依赖ElasticsearchClient和原生的API封装; + +## 开始 + +本文将指导您完成 Java 客户端的安装过程,向您展示如何实例化客户端,以及如何使用它执行基本的 `Elasticsearch` 操作。 + +## 安装 + +在项目的 pom.xml 中,添加以下存储库定义和依赖项: + +``` + + + + + co.elastic.clients + elasticsearch-java + 8.10.4 + + + + com.fasterxml.jackson.core + jackson-databind + 2.12.3 + + + + ``` -### 使用自己的es版本(向下兼容) +你可以使用API密钥和Elasticsearch端点来连接到Elastic 。 + +- RestClient这个类主要是用作于与服务端IP以及端口的配置,在其的builder()方法可以设置登陆权限的账号密码、连接时长等等。总而言之就是服务端配置。 + +- RestClientTransport + 这是Jackson映射器创建传输。建立客户端与服务端之间的连接传输数据。这是在创建ElasticsearchClient需要的参数,而创建RestClientTransport就需要上面创建的RestClient。 + +- ElasticsearchClient 这个就是Elasticsearch的客户端。调用Elasticsearch语法所用到的类,其就需要传入上面介绍的RestClientTransport。 + +``` +public class ElasticsearchAutoconfigure { + + /** + * elasticsearch url. + * 集群使用英文逗号隔开 + */ + @Autowired + private ElasticsearchProperties elasticsearchProperties; + + @Bean + @Lazy + public ElasticsearchClient client(@Autowired(required = false) JsonpMapper jsonpMapper, @Autowired(required = false) ObjectMapper objectMapper) { + // 解析hostlist配置信息 + // 创建HttpHost数组,其中存放es主机和端口的配置信息 + HttpHost[] httpHostArray = new HttpHost[elasticsearchProperties.getUris().size()]; + + + for (int i = 0; i < elasticsearchProperties.getUris().size(); i++) { + String item = elasticsearchProperties.getUris().get(i); -```xml + URI uri = URI.create(item); + httpHostArray[i] = new HttpHost(uri.getHost(), + uri.getPort(), uri.getScheme()); + } + + RequestConfig.Builder requesrBuilder = RequestConfig.custom() + .setConnectTimeout(Long.valueOf(elasticsearchProperties.getConnectionTimeout().toMillis()).intValue()) + .setSocketTimeout(Long.valueOf(elasticsearchProperties.getSocketTimeout().toMillis()).intValue()); + RequestConfig requestConfig = requesrBuilder.build(); + + RestClientBuilder builder = RestClient.builder(httpHostArray); + + // + SSLFactory sslFactory = SSLFactory.builder() + .withUnsafeTrustMaterial() + .withUnsafeHostnameVerifier() + .build(); + + // Create the low-level client + // 添加认证 + if (elasticsearchProperties.getUsername() != null) { + final CredentialsProvider provider = new BasicCredentialsProvider(); + provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(elasticsearchProperties.getUsername(), elasticsearchProperties.getPassword())); + + builder.setHttpClientConfigCallback(f -> + f.setDefaultCredentialsProvider(provider) + .setSSLContext(sslFactory.getSslContext()) + .setDefaultRequestConfig(requestConfig) + ); + } + + if (elasticsearchProperties.getPathPrefix() != null) { + builder.setPathPrefix(elasticsearchProperties.getPathPrefix()); + } + + + RestClient restClient = builder.build(); + + ElasticsearchTransport transport = null; + if (jsonpMapper != null) { + transport = new RestClientTransport(restClient, jsonpMapper); + } else if (objectMapper != null) { + transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper)); + } else { + transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); + } + // Create the transport with a Jackson mapper + + + // And create the API client + return new ElasticsearchClient(transport); + } +} +``` + +或者使用直接封装好的pom + +``` - com.admin4j.framework + com.admin4j.framework elasticsearch-spring-boot-starter - 0.2.0 - - - elasticsearch-java - co.elastic.clients - - - - -co.elastic.clients -elasticsearch-java -${elasticsearch.version} - - - jakarta.json-api - jakarta.json - - - + 8.x.3 + +``` + +- appliction.yml 配置 + + ``` + spring: + elasticsearch: + uris: + - http://192.168.1.13:9200 + ``` + +得到API客户端对象`client` ,你就可以进行`Elasticsearch`的基本操作,以下是一些最基本的操作。 + +## 简单操作 + +### 创建索引 + +下面的代码片段显示了如何使用 `indices` **命名空间客户端**创建索引(`lambda` 语法在下面进行了说明): + +``` + elasticsearchClient.indices().create(q -> q.index("products")); +``` + +###### 判断索引是否存在并创建索引(构建器写法与lambda写法) + +**lambda写法**: + +``` + String iName = "products"; + // 获取【索引客户端对象】 + ElasticsearchIndicesClient indexClient = elasticsearchClient.indices(); + + boolean flag = indexClient.exists(req -> req.index(iName)).value(); + // CreateIndexResponse createIndexResponse = null; + boolean result = false; + if (flag) { + // 目标索引已存在 + log.info("索引【" + iName + "】已存在!"); + } else { + // 不存在 + result = indexClient.create(req -> req.index(iName)).acknowledged(); + if (result) { + log.info("索引【" + iName + "】创建成功!"); + } else { + log.info("索引【" + iName + "】创建失败!"); + } + } +``` + +**构建器写法** + +``` + String iName = "products"; + // 获取【索引客户端对象】 + ElasticsearchIndicesClient indexClient = elasticsearchClient.indices(); + // 1、构建【存在请求对象】 + ExistsRequest existsRequest = new ExistsRequest.Builder().index(iName).build(); + // 2、判断目标索引是否存在 + boolean flag = indexClient.exists(existsRequest).value(); + + if (flag) { + // 目标索引已存在 + log.info("索引【" + iName + "】已存在!"); + } else { + // 1. 获取【创建索引请求对象】 + CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(iName).build(); + // 2. 创建索引,得到【创建索引响应对象】 + CreateIndexResponse createIndexResponse = indexClient.create(createIndexRequest); + createIndexResponse = indexClient.create(req -> req.index(iName)); + // System.out.println("创建索引响应对象:" + createIndexResponse); + boolean result = createIndexResponse.acknowledged(); + if (result) { + log.info("索引【" + iName + "】创建成功!"); + } else { + log.info("索引【" + iName + "】创建失败!"); + } + } +``` + +可以看到构建器写法在简洁度上完全不如lambda表达式,接下来所有例子均只采用lambda写法 + +### 查询索引 + +``` + String indexName = "products"; + Map result = elasticsearchClient.indices().get(req -> req.index(indexName)).result(); + System.out.println("result = " + result); + + // 查询全部索引 + Set all = elasticsearchClient.indices().get(req -> req.index("*")).result().keySet(); + System.out.println("all = " + all); + + // 删除索引 + Boolean isDelete = elasticsearchClient.indices().delete(req -> req.index(indexName)).acknowledged(); + if (isDelete) { + log.info("删除索引成功"); + } else { + log.info("删除索引失败"); + } +``` + +### 插入文档 + +生成请求的最直接方法是使用流畅的 DSL。在下面的示例中,我们使用产品的 SKU 作为索引中的文档标识符,在产品索引中为products描述编制索引。product对象将使用 +Elasticsearch 客户端上配置的对象映射器映射到 JSON。 + +``` + Product product = new Product("bk-1", "City bike", 123.0, null); + + IndexResponse response = elasticsearchClient.index(i -> i + .index("products") + .id(product.getSku()) + .document(product) + ); + + log.info("Indexed with version " + response.version()); +``` + +还可以将使用 DSL 创建的对象分配给变量。Java API 客户端类有一个静态of() 方法,它使用 DSL 语法创建一个对象。 + +``` + Product product = new Product("bk-1", "City bike", 123.0, null); + + IndexRequest request = IndexRequest.of(i -> i + .index("products") + .id(product.getSku()) + .document(product) + ); + + IndexResponse response = elasticsearchClient.index(request); + + log.info("Indexed with version " + response.version()); +``` + +### 使用原始 JSON 数据 + +当您要索引的数据来自外部源时,对于半结构化数据,必须创建域对象可能会很麻烦或完全不可能。 + +您可以使用 withJson() 为任意源的数据编制索引。使用此方法将读取源并将其用于索引请求的document属性。 + +``` + Reader input = new StringReader( + "{'@timestamp': '2022-04-08T13:55:32Z', 'level': 'warn', 'message': 'Some log message'}" + .replace('\'', '"')); + + IndexRequest request = IndexRequest.of(i -> i + .index("logs") + .withJson(input) + ); + + IndexResponse response = elasticsearchClient.index(request); + + log.info("Indexed with version " + response.version()); +``` + +### 批量请求:多个文档 + +批量请求:多个文档 +批量请求允许在一个请求中发送多个与文档相关的操作到 Elasticsearch。当你有多个文档需要导入时,这比分别发送每个文档的请求更有效率。 + +一个批量请求可以包含多种类型的操作: + +- 创建一个文档,在确保它不存在后进行索引 + +- 索引一个文档,如果需要则创建它,如果已经存在则替换它 + +- 更新一个已经存在的文档,可以使用脚本或部分文档 + +- 删除一个文档 + +一个批量请求包含一系列操作,每个操作都是一种类型,有几个变种。为了创建这个请求,最方便的方法是使用主请求的构建器对象以及每个操作的流畅式 +DSL。 + +下面的示例展示了如何索引一个应用程序对象列表。 + +``` + List products = Arrays.asList( + new Product("bk-1", "City bike", 123.0, null), + new Product("bk-2", "City bike", 124.0, null), + new Product("bk-3", "City bike", 125.0, null), + new Product("bk-4", "City bike", 126.0, null) + ); + + BulkRequest.Builder br = new BulkRequest.Builder(); + + for (Product product : products) { + br.operations(op -> op + .index(idx -> idx + .index("products") + .id(product.getSku()) + .document(product) + ) + ); + } + + BulkRequest bulkRequest = br.build(); + log.error("bulkRequest {}", bulkRequest.toString()); + BulkResponse result = elasticsearchClient.bulk(bulkRequest); + + // Log errors, if any + if (result.errors()) { + log.error("Bulk had errors"); + for (BulkResponseItem item : result.items()) { + if (item.error() != null) { + log.error(item.error().reason()); + } + } + } +``` + +批量的脚本操作: + +``` +for (ProductDTO productDTO : Optional.ofNullable(productResult.getData()).orElse(Collections.emptyList())) { + + Map params = new HashMap<>(16); + params.put("buys", JsonData.of(productDTO.getBuys())); + params.put("views", JsonData.of(productDTO.getViews())); + params.put("comments", JsonData.of(productDTO.getComments())); + + + br.operations(op -> op + .update(u -> u + .id(String.valueOf(productDTO.getId())) + .index(searchProperties.getProductIndexName()) + .action(a -> a + .script(s -> s + .inline(i -> i + .source("ctx._source.buys = params.buys;" + + "ctx._source.views = params.views;" + + "ctx._source.comments = params.comments;") + .params(params)))) + + ) + ); + } +``` + +### 查询文档 + +**Elasticsearch主要用于搜索,但你也可以直接访问文档,通过id 。** + +下面的示例从"`products`"索引中读取id "bk-1"的文档。 + +get请求有两个参数: + +- 第一个参数是实际请求,使用DSL构建 +- 第二个参数是我们希望将文档的JSON映射到的类。 + +``` + GetResponse response = elasticsearchClient.get(g -> g + .index("products") + .id("bk-1"), + Product.class + ); + + if (response.found()) { + Product product = response.source(); + log.info("产品名称 " + product.getName()); + } else { + log.info("未找到产品"); + } +``` + +> 1. 这个get请求包括索引名称和标识符 +> 2. 目标类,在这里是`Product` + +如果你的索引包含半结构化数据,或者如果你没有对象的定义,你也可以将文档作为原始JSON数据来读取。 + +原始JSON数据只是另一个类,你可以将其用作get请求的结果类型。在下面的示例中,我们使用了Jackson的ObjectNode。我们也可以使用任何可以由与ElasticsearchClient关联的JSON映射器反序列化的JSON表示。 + +``` + GetResponse response = elasticsearchClient.get(g -> g + .index("products") + .id("bk-1"), + ObjectNode.class + ); + + if (response.found()) { + ObjectNode json = response.source(); + String name = json.get("name").asText(); + log.info("产品名称 " + name); + } else { + log.info("未找到产品"); + } +``` + +> 目标类是一个原始的JSON对象。 + +### 修改文档 + +``` + UpdateResponse updateResponse = elasticsearchClient.update(u -> u + .doc("products") + .id("bk-1"), + Product.class); +``` + +### 删除文档 + +``` + DeleteResponse deleteResponse = elasticsearchClient.delete(d -> d + .index("products") + .id("bk-1") + ); +``` + +### 命名空间 + +在REST API文档中,数量众多API是按照特性(feature)来分组的,如下图: + +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/799a393bacdf408c8b16ff0f5bec177b.png) + +在ES的Java库Java API Client中,上图中的各种feature被称为namespace + +在ES的Java库Java API Client中,与REST +API对应的的类和接口都在统一的包名co.elastic.clients.elasticsearch之下,然后再通过下一级package进行分类,这个分类与上图的feature相对应。例如索引相关的,在REST +API中的feature是Index APIs,那么在Java API +Client中,完整的package就是co.elastic.clients.elasticsearch.indices,这里面有索引操作所需的请求、响应、服务等各种类. + +每一个namespace(也就是REST +API中的feature),都有自己的client,例如索引相关的操作都有索引专用的client类负责,client.indices() +返回的是ElasticsearchIndicesClient对象,这是索引操作专用的实例 + +``` +ElasticsearchClient client = ...... + +client.indices().create(c -> c.index("products")); +``` + +展开上述代码的indices() +方法,看看其内部实现,如下所示,每次调用indices方法,都会创建一个ElasticsearchIndicesClient对象,对于其他namespace,例如ingest、license亦是如此,都会创建新的实例 + +每个namespace都有自己的client,但也有例外,就是search和document,它们的代码不在search或者document这样的package下面,而是在core下面,而且可以直接通过ElasticsearchClient来操作,如下: +插入一条文档: + +``` + Product product = new Product("bk-1", "City bike", 123.0); + + IndexResponse response = client.index(i -> i + .index("products") + .id(product.getSku()) + .document(product) + ); + + logger.info("Indexed with version " + response.version()); +``` + +## 构建 API 对象 + +### 1.构建器模式 + +``` +CreateIndexResponse createResponse = client.indices().create( + new CreateIndexRequest.Builder() + .index("my-index") + .aliases("foo", + new Alias.Builder().isWriteIndex(true).build() + ) + .build() +); +``` + +### 2.lambda 表达式 + +虽然这效果很好,但必须实例化构建器类并调用 build() 方法有点冗长。因此,Java API 客户端中的每个属性设置器也接受一个 lambda +表达式,该表达式将新创建的构建器作为参数,并返回填充的构建器。上面的片段也可以写成: + +``` +CreateIndexResponse createResponse = client.indices() + .create(createIndexBuilder -> createIndexBuilder + .index("my-index") + .aliases("foo", aliasBuilder -> aliasBuilder + .isWriteIndex(true) + ) + ); ``` -## 配置 +这种方法允许更简洁的代码,并且还避免了导入类(甚至记住它们的名称),因为类型是从方法参数签名推断出来的。建议大家这样去写,非常简洁快速,后面的各种操作我也会用这种方式来书写。 + +生成器 lambda 对于复杂的嵌套查询(如下所示)特别有用 + +``` +{ + "query": { + "intervals": { + "field": "my_text", + "all_of": [ + { + "ordered": true, + "intervals": [ + { + "match": { + "query": "my favorite food", + "max_gaps": 0, + "ordered": true + } + } + ] + }, + { + "any_of": { + "intervals": [ + { + "match": { + "query": "hot water" + } + }, + { + "match": { + "query": "cold porridge" + } + } + ] + } + } + ] + } + } +} +``` + +对应的代码如下: + +``` +SearchResponse results = client + .search(b0 -> b0 + .query(b1 -> b1 + .intervals(b2 -> b2 + .field("my_text") + .allOf(b3 -> b3 + .ordered(true) + .intervals(b4 -> b4 + .match(b5 -> b5 + .query("my favorite food") + .maxGaps(0) + .ordered(true) + ) + ) + .intervals(b4 -> b4 + .anyOf(b5 -> b5 + .intervals(b6 -> b6 + .match(b7 -> b7 + .query("hot water") + ) + ) + .intervals(b6 -> b6 + .match(b7 -> b7 + .query("cold porridge") + ) + ) + ) + ) + ) + ) + ), + SomeApplicationData.class +); +``` + +## 复杂查询 + +### 搜索查询 + +有许多类型的搜索查询可以组合使用。我们将从简单的文本匹配查询开始,在products索引中搜索自行车。我们在这里选择匹配查询(全文搜索) + +搜索结果具有hits属性,其中包含与查询匹配的文档以及有关索引中存在的匹配项总数的信息。 +总值带有一个关系,该关系指示总值是精确的(eq — 相等)还是近似的(gte — 大于或等于)。 +每个返回的文档都附带其相关性分数以及有关其在索引中的位置的其他信息。 + + ``` + String searchText = "bk"; + + SearchResponse response = elasticsearchClient.search(s -> s + .index("products") + .query(q -> q + .match(t -> t + .field("name") + .query(searchText) + ) + ), + Product.class + ); -```yaml -spring: - elasticsearch: - uris: - - http://192.168.1.13:9200 + TotalHits total = response.hits().total();// total可以获取结果的总数 + boolean isExactResult = total.relation() == TotalHitsRelation.Eq; + + if (isExactResult) { + log.info("找到 " + total.value() + " 个结果"); + } else { + log.info("找到超过 " + total.value() + " 个结果"); + } + + + List> hits = response.hits().hits(); + for (Hit hit : hits) { + Product product = hit.source(); + log.info("找到产品 " + product.getSku() + ",得分 " + hit.score()); + } + ``` + +与获取操作类似,您可以使用相应的目标类而不是 **Product**(如 **JSON-P** 的 **JsonValue** 或 **Jackson** 的 **ObjectNode** +)将匹配查询的文档作为原始 JSON 获取. + +### bool查询 + +``` +@SpringBootTest +@Slf4j +public class ESTest { + + @Resource + ElasticsearchClient client; + + String index = "textbook"; + + @Test + public void grepTextBook() throws IOException { + SearchResponse boolSearch = client.search(s -> s + .index(index) + .query(q -> q + .bool(b -> b + .must(m -> m + .term(t -> t + .field("author") + .value("老坛") + ) + ) + .should(sh -> sh + .match(t -> t + .field("bookName") + .query("老坛") + ) + ) + + ) + ), + TextBook.class); + for (Hit hit: boolSearch.hits().hits()) { + TextBook pd = hit.source(); + System.out.println(pd); + } + } +} +``` + +对应了ES的bool查询,它等价的ES语法就是: + +``` +GET textbook/_search +{ + "query":{ + "bool":{ + "should":{ + "match":{ + "bookName":"老坛" + } + }, + "must":{ + "term":{ + "author":"老坛" + } + } + } + } +} +``` + +### 嵌套搜索查询 + +在下面的示例中,我们将搜索最高价格为 200 的自行车。 + +Java API 客户端Query类有一个静态of() 方法,它使用 DSL 语法创建一个对象。 + +``` +String searchText = "自行车"; +double maxPrice = 200.0; + +// 根据产品名称搜索 +Query byName = MatchQuery.of(m -> m + .field("name") + .query(searchText) +)._toQuery(); + +// 根据最高价格搜索 +Query byMaxPrice = RangeQuery.of(r -> r + .field("price") + .gte(JsonData.of(maxPrice)) +)._toQuery(); + +// 组合产品名称和价格查询来搜索产品索引 +SearchResponse response = client.search(s -> s + .index("products") + .query(q -> q + .bool(b -> b + .must(byName) + .must(byMaxPrice) + ) + ), + Product.class +); + +List> hits = response.hits().hits(); +for (Hit hit : hits) { + Product product = hit.source(); + logger.info("找到产品 " + product.getSku() + ",得分 " + hit.score()); +} +``` -## 开启访问日志 -logging: - level: - org.springframework.data.elasticsearch.client.WIRE: TRACE +在大量并发频繁执行各种namespace操作时,会创建大量client对象,这样会影响系统性能吗? + +官方说这是轻量级对象(very lightweight),所以,理论上可以放心创建,不必担心其对系统造成的压力 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/f2cd5f5f049d4a5484c57f959b8a7cef.png) + +同时,这段代码的目的是为了实现逻辑功能,代码的可读性和维护性通常比微小的内存浪费更重要。如果通过将这段逻辑放在条件块内,来避免不使用的 `boolQueryBuilder` +,可能会使代码更复杂和难以阅读。 + +### 拼接查询条件 + +可以选择性的拼接条件,我们先创建一个SearchRequest.Builder请求对象构建器,然后拼接条件。 + +``` + // 1. 创建查询构建器 + co.elastic.clients.elasticsearch.core.SearchRequest.Builder searchBuilder = + new co.elastic.clients.elasticsearch.core.SearchRequest.Builder(); + //设置索引名称 + searchBuilder + .index(searchProperties.getProductIndexName()); + + if (StrUtil.isBlank(request.getKey())) { + + Query query = Query.of(q -> q + .bool(b -> b + .must(m -> m.matchAll(m1 -> m1)))); + searchBuilder.query(query); + } else { + + Query query = Query.of(q -> q + .bool(b1 -> b1 + .should(s -> s + .matchPhrase(m1 -> m1 + .field("productName").query(request.getKey()).boost(3f))) + .should(s1 -> s1 + .matchPhrase(m2 -> m2 + .field("shopName").query(request.getKey()))) + .should(s2 -> s2 + .matchPhrase(m3 -> m3 + .field("brandName").query(request.getKey()))) + )); + } + + //根据条件拼接不同query + searchBuilder.query(query); + //查询 + co.elastic.clients.elasticsearch.core.SearchRequest searchRequest = searchBuilder.build(); + SearchResponse response = client.search(searchRequest, ProductDocument.class); +``` + +### term查询 + +``` +@SpringBootTest +@Slf4j +public class ESTest { + + @Resource + ElasticsearchClient client; + + String index = "textbook"; + + @Test + public void grepTextBook() throws IOException { + SearchResponse termSearch = client.search(s -> s + .index(index) + .query(q -> q + .term(t -> t + .field("bookName") + .value("老坛") + ) + ), + TextBook.class); + for (Hit hit: termSearch.hits().hits()) { + TextBook pd = hit.source(); + System.out.println(pd); + } + } +} +``` + +对应了ES的term查询,它等价的ES语法就是: + +``` +GET textbook/_search +{ + "query": { + "term": { + "bookName":"老坛" + } + } +} +``` + +### terms查询 + +``` + List skuIds = new ArrayList<>(); + skuIds.add(1L); + skuIds.add(2L); + skuIds.add(3L); + + // 创建 "skuIds" 条件tems查询 + TermsQuery bySkuIds = TermsQuery.of(t -> t + .field("skuIds") + .terms(t2 -> t2 + .value(skuIds.stream().map(FieldValue::of).collect(Collectors.toList()))) + ); + //查询命令 + SearchResponse search = client.search(s -> s + .index("activity") + .query(q -> q + .terms(bySkuIds) + ) + , ActivityDocument.class); +``` + +对应了ES的terms查询,它等价的ES语法就是: + +``` +{ + "query": { + "terms": { + "skuIds ": [1,2,3] + } + } +} +``` + +### match_phrase查询 + +``` +@SpringBootTest +@Slf4j +public class ESTest { + + @Resource + ElasticsearchClient client; + + String index = "textbook"; + + @Test + public void grepTextBook() throws IOException { + SearchResponse matchPhraseSearch = client.search(s -> s + .index(index) + .query(q -> q + .matchPhrase(m -> m + .field("bookName") + .query("老坛") + ) + ), + TextBook.class); + for (Hit hit: matchPhraseSearch.hits().hits()) { + TextBook pd = hit.source(); + System.out.println(pd); + } + } +} +``` + +对应了ES的match_phrase查询,它等价的ES语法就是: + +``` +GET textbook/_search +{ + "query": { + "match_phrase": { + "bookName":"老坛" + } + } +} +``` + +### multi_match查询 + +``` +@SpringBootTest +@Slf4j +public class ESTest { + + @Resource + ElasticsearchClient client; + + String index = "textbook"; + + @Test + public void grepTextBook() throws IOException { + SearchResponse multiMatchSearch = client.search(s -> s + .index(index) + .query(q -> q + .multiMatch(m -> m + .query("老坛") + .fields("author", "bookName") + ) + ), + TextBook.class); + for (Hit hit: multiMatchSearch.hits().hits()) { + TextBook pd = hit.source(); + System.out.println(pd); + } + } +} +``` + +对应了ES的multi_match查询,它等价的ES语法就是: + +``` +GET textbook/_search +{ + "query": { + "multi_match": { + "query": "老坛", + "fields": ["author","bookName"] + } + } +} +``` + +### fuzzy查询 + +``` +@SpringBootTest +@Slf4j +public class ESTest { + + @Resource + ElasticsearchClient client; + + String index = "textbook"; + + @Test + public void grepTextBook() throws IOException { + SearchResponse fuzzySearch = client.search(s -> s + .index(index) + .query(q -> q + .fuzzy(f -> f + .field("bookName") + .fuzziness("2") + .value("老坛") + ) + ), + TextBook.class); + for (Hit hit: fuzzySearch.hits().hits()) { + TextBook pd = hit.source(); + System.out.println(pd); + } + } +} ``` -## elasticsearch-java 使用指南 +对应了ES的fuzzy查询,它等价的ES语法就是: + +``` +GET textbook/_search +{ + "query": { + "fuzzy": { + "bookName":{ + "value":"老坛", + "fuzziness":2 + } + } + } +} +``` + +### range查询 + +``` +@SpringBootTest +@Slf4j +public class ESTest { + + @Resource + ElasticsearchClient client; + + String index = "textbook"; + + @Test + public void grepTextBook() throws IOException { + SearchResponse rangeSearch = client.search(s -> s + .index(index) + .query(q -> q + .range(r -> r + .field("bookName") + .gt(JsonData.of(20)) + .lt(JsonData.of(20)) + ) + ), + TextBook.class); + for (Hit hit: rangeSearch.hits().hits()) { + TextBook pd = hit.source(); + System.out.println(pd); + } + } +} +``` + +对应了ES的range查询,它等价的ES语法就是: + +``` +GET textbook/_search +{ + "query": { + "range": { + "bookName": { + "gt":20, + "lt":30 + } + } + } +} +``` + +### 高亮查询 + +实现很简单,请注意,我们定义 HighlightField 即hf,即我们要突出显示的字段。 + +在这个 HighlightField 中,我们还定义了参数,包括numberOfFragments和fragmentSize。 + +参数可以设置在highlight的下一级,此时为全局设置(如下面的fragmentSize(50)和numberOfFragments(5) +),也可以设置在字段的下一级,此时为字段设置。单个字段的设置优先级高于全局设置。 + +``` +var response = client.search(s -> s + .index("product") + .query(q -> q.multiMatch(m -> m.fields(List.of("title", "description")).query("Aliens and predator"))) + .highlight(h -> h + .type(HighlighterType.Unified) + .fields("title",hf -> hf + .numberOfFragments(0)) + .fields("description",hf -> hf + .numberOfFragments(4).fragmentSize(50)) + .fragmentSize(50) + .numberOfFragments(5) + ) + , Movie.class); +``` + +上面的写法等同于: + +``` +Map map = new HashMap<>(); +map.put("title", HighlightField.of(hf -> hf.numberOfFragments(0))); +map.put("description", HighlightField.of(hf -> hf.numberOfFragments(4).fragmentSize(50))); + +Highlight highlight = Highlight.of( + h -> h.type(HighlighterType.Unified) + .fields(map) + .fragmentSize(50) + .numberOfFragments(5) +); + +var response = client.search(s -> s + .index("idx_movies") + .query(q -> q.multiMatch(m -> m.fields(List.of("title", "description")).query("Aliens and predator"))) + .highlight(highlight) + , Movie.class); +``` + +### 排序和分页 + +排序和分页直接像ES的语法一样,体现在和query的平级即可。这里已match为例进行介绍。 + +``` +@SpringBootTest +@Slf4j +public class ESTest { + + @Resource + ElasticsearchClient client; + + String index = "textbook"; + + @Test + public void grepTextBook() throws IOException { + SearchResponse matchSearch = client.search(s -> s + .index(index) + .query(q -> q + .match(t -> t + .field("bookName") + .query("老坛") + ) + ) + .from(1) + .size(100) + .sort(so -> so // 排序操作项 + .field(f -> f // 排序字段规则 + .field("num") + .order(SortOrder.Desc) + ) + ), + TextBook.class); + for (Hit hit: matchSearch.hits().hits()) { + TextBook pd = hit.source(); + System.out.println(pd); + } + } +} +``` + +这是一个根据num字段进行降序排序的查询,按页容量为100对数据进行分页,取第二页数据。 + +它等价的ES语法就是: + +``` +GET textbook/_search +{ + "query":{ + "match":{ + "bookName":"老坛" + } + }, + "from":1, + "size":100, + "sort":{ + "num":{ + "order":"desc" + } + } +} +``` + +### 聚合 + +这个示例是一种用于分析的聚合操作,我们不需要使用匹配的文档。用于分析的搜索请求通常的一般模式是将结果大小设置为0,将搜索结果的目标类设置为 +Void。 + +如果同样的聚合用于显示产品和价格直方图作为钻取细分,我们会将大小设置为非零值,并使用 Product 作为目标类来处理结果。 + +``` +String searchText = "自行车"; + +Query query = MatchQuery.of(m -> m + .field("name") + .query(searchText) +)._toQuery(); + +SearchResponse response = client.search(b -> b + .index("products") + .size(0) // 将匹配文档数量设置为零,因为我们只关心价格直方图 + .query(query) // 设置用于过滤要执行聚合的产品的查询 + .aggregations("price-histogram", a -> a + .histogram(h -> h + .field("price") + .interval(50.0) + ) + ), + Void.class +); +``` + +在上面的代码中,我们首先创建了一个用于产品名称匹配的查询,然后执行了一个搜索请求,其中包含了一个名为 “price-histogram” +的聚合操作,用于创建价格直方图。我们将结果大小设置为零,因为我们只关心聚合结果,不需要匹配的文档。 + +响应包含了每个请求中的聚合结果。 + +``` +List buckets = response.aggregations() + .get("price-histogram") + .histogram() + .buckets().array(); + +for (HistogramBucket bucket : buckets) { + logger.info("有 " + bucket.docCount() + + "辆自行车的价格低于 " + bucket.key()); +} +``` + +获取 “price-histogram” 聚合的结果。 + +将其转换为直方图变体的结果。这必须与聚合定义保持一致。 + +桶可以表示为数组或映射。这里将其转换为数组变体(默认选项)。 + +#### 另一个例子 + +``` + // Creating aggregations + SearchResponse search3 = client.search( b-> b + .index("products") + .size(0) + .aggregations("price-histo", a -> a + .histogram(h -> h + .field("price") + .interval(20.0) + ) + ), + Void.class + ); + + long firstBucketCount = search3.aggregations() + .get("price-histo") + .histogram() + .buckets().array() + .get(0) + .docCount(); + + System.out.println("doc count: " + firstBucketCount); + } + +``` + +上面的 aggregation 相当于如下的请求: + +``` +GET products/_search +{ + "size": 0, + "aggs": { + "price-histo": { + "histogram": { + "field": "price", + "interval": 20 + } + } + } +} +``` + +我们的 Java 代码的输出结果为: + +``` +doc count: 2 +``` + +上面的聚合,我们可以甚至直接使用 JSON 结构的字符串来进行操作: + +``` + String aggstr = "\n" + + " { \n" + + " \"size\": 0, \n" + + " \"aggs\": { \n" + + " \"price-histo\": { \n" + + " \"histogram\": { \n" + + " \"field\": \"price\", \n" + + " \"interval\": 20 \n" + + " } \n" + + " } \n" + + " } \n" + + " } "; + + System.out.println("agg is: " + aggstr ); + + InputStream agg = new ByteArrayInputStream(aggstr.getBytes()); + SearchResponse searchAgg = client + .search(b -> b + .index("products") + .withJson(agg), + Void.class + ); + + firstBucketCount = searchAgg.aggregations() + .get("price-histo") + .histogram() + .buckets().array() + .get(0) + .docCount(); + + System.out.println("doc count: " + firstBucketCount); +``` + +上面代码显示的结果和之上的结果是一样的 + +### 分组查询 + +Elasticsearch Java API +Client客户端中的分组查询,也是属于聚合查询的一部分,所以同样使用aggregations方法,并使用terms方法来代表分组查询,field传入需要分组的字段,最后通过响应中的aggregations参数来获取,这里需要根据数据的类型来获取最后的分组结果,我这里因为统计的是数字类型,所以调用lterms() +使用LongTermsAggregate来获取结果,同理:如果是String类型则调用sterms()使用StringTermsAggregate,最后打印出docCount属性即可。 + +``` +SearchResponse response11 = client.search(s -> s + .index("newapi") + .size(100) + .aggregations("ageGroup", a -> a + .terms(t -> t + .field("age") + ) + ) + , Test.class); + +System.out.println(response11.took()); +System.out.println(response11.hits().total().value()); + +response11.hits().hits().forEach(e -> { + System.out.println(e.source().toString()); +}); + +Aggregate aggregate = response11.aggregations().get("ageGroup"); +LongTermsAggregate lterms = aggregate.lterms(); +Buckets buckets = lterms.buckets(); + +for (LongTermsBucket b : buckets.array()) { + System.out.println(b.key() + " : " + b.docCount()); +} +``` + +### 过滤器 + +SourceConfig 提供对包含和排除字段的访问权限。 + +``` + SearchResponse search = client.search(s -> s + .query(query) + .source(s1 -> s1 + .filter(v -> v + .includes("type", "allProdsFlag", "price", "discount", "marketingType", "marketingCalType", "number", "name", "shopId", "pic", "startTime", "endTime", "skuIds", "activityId") + .excludes(null) + ) + ) + , ActivityDocument.class); + +``` + +或者使用of来构建 + +``` + SourceConfig sourceConfig = SourceConfig.of(s -> s + .filter(v -> v + .includes("type", "allProdsFlag", "price", "discount", "marketingType", "marketingCalType", "number", "name", "shopId", "pic", "startTime", "endTime", "skuIds", "activityId") + .excludes(null) + ) + ); + + SearchResponse search = client.search(s -> s + .query(query) + .source(sourceConfig) + , ActivityDocument.class); +``` -[https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/introduction.html](https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/introduction.html) \ No newline at end of file diff --git a/elasticsearch-spring-boot-starter/pom.xml b/elasticsearch-spring-boot-starter/pom.xml index fb32a86..c43329c 100644 --- a/elasticsearch-spring-boot-starter/pom.xml +++ b/elasticsearch-spring-boot-starter/pom.xml @@ -10,7 +10,7 @@ com.admin4j.framework elasticsearch-spring-boot-starter jar - 8.x.2 + 8.x.3 elasticsearch Spring boot 自动装配器 elasticsearch-spring-boot-starter @@ -60,5 +60,11 @@ com.admin4j.common admin4j-common + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/ElasticsearchAutoconfigure.java b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/ElasticsearchAutoconfigure.java index c9af929..f8a6dbd 100644 --- a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/ElasticsearchAutoconfigure.java +++ b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/autoconfigure/ElasticsearchAutoconfigure.java @@ -43,7 +43,7 @@ public class ElasticsearchAutoconfigure { @Bean @Lazy - public ElasticsearchClient client(@Autowired(required = false) JsonpMapper jsonpMapper, @Autowired ObjectMapper objectMapper) { + public ElasticsearchClient client(@Autowired(required = false) JsonpMapper jsonpMapper, @Autowired(required = false) ObjectMapper objectMapper) { // 解析hostlist配置信息 // 创建HttpHost数组,其中存放es主机和端口的配置信息 HttpHost[] httpHostArray = new HttpHost[elasticsearchProperties.getUris().size()]; diff --git a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java b/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java deleted file mode 100644 index bc504ae..0000000 --- a/elasticsearch-spring-boot-starter/src/main/java/com/admin4j/elasticsearch/properties/ElasticsearchProperties.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.admin4j.elasticsearch.properties; - -import lombok.Data; - -/** - * @author andanyang - * @since 2023/4/18 11:31 - */ -@Data -@Deprecated -// @ConfigurationProperties(prefix = "admin4j.elasticsearc") -public class ElasticsearchProperties { - - /** - * es的server地址,多个server之间使用英⽂逗号分隔开 - */ - private String urls; - - /** - * 账号 - */ - private String username; - /** - * 密码 - */ - private String password; - - -} diff --git a/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESDocTest.java b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESDocTest.java new file mode 100644 index 0000000..9c77b10 --- /dev/null +++ b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESDocTest.java @@ -0,0 +1,129 @@ +package com.admin4j.elasticsearch; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.*; +import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem; +import co.elastic.clients.json.JsonData; +import com.admin4j.elasticsearch.entity.Product; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Arrays; +import java.util.List; + +/** + * @author andanyang + * @since 2023/12/15 10:22 + */ +@SpringBootTest +@Slf4j +public class ESDocTest { + + @Autowired + ElasticsearchClient elasticsearchClient; + String indexName = "products"; + + + // 插入文档 + @Test + public void addDocument() throws IOException { + Product product = new Product("bk-1", "City bike", 123.0, null); + + + // IndexResponse response = elasticsearchClient.index(i -> i + // .index("products") + // .id(product.getSku()) + // .document(product) + // ); + // + // log.info("Indexed with version " + response.version()); + + IndexRequest request = IndexRequest.of(i -> i + .index("products") + .id(product.getSku()) + .document(product) + ); + + IndexResponse response = elasticsearchClient.index(request); + + log.info("Indexed with version " + response.version()); + } + + // 插入文档 + @Test + public void addDocumentJson() throws IOException { + + Reader input = new StringReader( + "{'@timestamp': '2022-04-08T13:55:32Z', 'level': 'warn', 'message': 'Some log message'}" + .replace('\'', '"')); + + IndexRequest request = IndexRequest.of(i -> i + .index("logs") + .withJson(input) + ); + + IndexResponse response = elasticsearchClient.index(request); + + log.info("Indexed with version " + response.version()); + } + + // 批量插入文档 + @Test + public void addDocumentBulk() throws IOException { + + List products = Arrays.asList( + new Product("bk-1", "City bike", 123.0, null), + new Product("bk-2", "City bike", 124.0, null), + new Product("bk-3", "City bike", 125.0, null), + new Product("bk-4", "City bike", 126.0, null) + ); + + BulkRequest.Builder br = new BulkRequest.Builder(); + + for (Product product : products) { + br.operations(op -> op + .index(idx -> idx + .index("products") + .id(product.getSku()) + .document(product) + ) + ); + } + + BulkRequest bulkRequest = br.build(); + log.error("bulkRequest {}", bulkRequest.toString()); + BulkResponse result = elasticsearchClient.bulk(bulkRequest); + + // Log errors, if any + if (result.errors()) { + log.error("Bulk had errors"); + for (BulkResponseItem item : result.items()) { + if (item.error() != null) { + log.error(item.error().reason()); + } + } + } + } + + @Test + public void update() throws IOException { + + UpdateResponse updateResponse = elasticsearchClient.update(u -> u + .doc("products") + .id("bk-1"), + Product.class); + + + DeleteResponse deleteResponse = elasticsearchClient.delete(d -> d + .index("products") + .id("bk-1") + ); + + } + +} diff --git a/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESIndexTest.java b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESIndexTest.java new file mode 100644 index 0000000..0228c2b --- /dev/null +++ b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESIndexTest.java @@ -0,0 +1,106 @@ +package com.admin4j.elasticsearch; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.indices.*; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +/** + * @author andanyang + * @since 2023/12/15 10:10 + */ +@SpringBootTest +@Slf4j +public class ESIndexTest { + + @Autowired + ElasticsearchClient elasticsearchClient; + + // 创建索引 + @Test + public void createIndex() throws IOException { + elasticsearchClient.indices().create(q -> q.index("products")); + } + + // 判断索引是否存在并创建索引 + @Test + public void indexExists() throws IOException { + + String iName = "products"; + // 获取【索引客户端对象】 + ElasticsearchIndicesClient indexClient = elasticsearchClient.indices(); + + boolean flag = indexClient.exists(req -> req.index(iName)).value(); + // CreateIndexResponse createIndexResponse = null; + boolean result = false; + if (flag) { + // 目标索引已存在 + log.info("索引【" + iName + "】已存在!"); + } else { + // 不存在 + result = indexClient.create(req -> req.index(iName)).acknowledged(); + if (result) { + log.info("索引【" + iName + "】创建成功!"); + } else { + log.info("索引【" + iName + "】创建失败!"); + } + } + } + + // 判断索引是否存在并创建索引 构建器写法 + @Test + public void indexExistsBuilder() throws IOException { + + String iName = "products"; + // 获取【索引客户端对象】 + ElasticsearchIndicesClient indexClient = elasticsearchClient.indices(); + // 1、构建【存在请求对象】 + ExistsRequest existsRequest = new ExistsRequest.Builder().index(iName).build(); + // 2、判断目标索引是否存在 + boolean flag = indexClient.exists(existsRequest).value(); + + if (flag) { + // 目标索引已存在 + log.info("索引【" + iName + "】已存在!"); + } else { + // 1. 获取【创建索引请求对象】 + CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(iName).build(); + // 2. 创建索引,得到【创建索引响应对象】 + CreateIndexResponse createIndexResponse = indexClient.create(createIndexRequest); + createIndexResponse = indexClient.create(req -> req.index(iName)); + // System.out.println("创建索引响应对象:" + createIndexResponse); + boolean result = createIndexResponse.acknowledged(); + if (result) { + log.info("索引【" + iName + "】创建成功!"); + } else { + log.info("索引【" + iName + "】创建失败!"); + } + } + } + + // 查询索引 + @Test + public void testSearchIndex() throws IOException { + String indexName = "products"; + Map result = elasticsearchClient.indices().get(req -> req.index(indexName)).result(); + System.out.println("result = " + result); + + // 查询全部索引 + Set all = elasticsearchClient.indices().get(req -> req.index("*")).result().keySet(); + System.out.println("all = " + all); + + // 删除索引 + Boolean isDelete = elasticsearchClient.indices().delete(req -> req.index(indexName)).acknowledged(); + if (isDelete) { + log.info("删除索引成功"); + } else { + log.info("删除索引失败"); + } + } +} diff --git a/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESSearchTest.java b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESSearchTest.java new file mode 100644 index 0000000..228357c --- /dev/null +++ b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ESSearchTest.java @@ -0,0 +1,98 @@ +package com.admin4j.elasticsearch; + +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.core.GetResponse; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.core.search.TotalHits; +import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation; +import com.admin4j.elasticsearch.entity.Product; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.List; + +/** + * @author andanyang + * @since 2023/12/15 10:22 + */ +@SpringBootTest +@Slf4j +public class ESSearchTest { + + @Autowired + ElasticsearchClient elasticsearchClient; + String indexName = "products"; + + @Test + public void testSearch() throws Exception { + + GetResponse response = elasticsearchClient.get(g -> g + .index("products") + .id("bk-1"), + Product.class + ); + + if (response.found()) { + Product product = response.source(); + log.info("产品名称 " + product.getName()); + } else { + log.info("未找到产品"); + } + } + + @Test + public void testSearchJSON() throws Exception { + + GetResponse response = elasticsearchClient.get(g -> g + .index("products") + .id("bk-1"), + ObjectNode.class + ); + + if (response.found()) { + ObjectNode json = response.source(); + String name = json.get("name").asText(); + log.info("产品名称 " + name); + } else { + log.info("未找到产品"); + } + } + + @Test + public void testSearch1() throws Exception { + String searchText = "bk"; + + SearchResponse response = elasticsearchClient.search(s -> s + .index("products") + .query(q -> q + .match(t -> t + .field("name") + .query(searchText) + ) + ), + Product.class + ); + + TotalHits total = response.hits().total();// total可以获取结果的总数 + boolean isExactResult = total.relation() == TotalHitsRelation.Eq; + + if (isExactResult) { + log.info("找到 " + total.value() + " 个结果"); + } else { + log.info("找到超过 " + total.value() + " 个结果"); + } + + + List> hits = response.hits().hits(); + for (Hit hit : hits) { + Product product = hit.source(); + log.info("找到产品 " + product.getSku() + ",得分 " + hit.score()); + } + + + } +} diff --git a/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ElasticsearchApplication.java b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ElasticsearchApplication.java new file mode 100644 index 0000000..cdaed6c --- /dev/null +++ b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/ElasticsearchApplication.java @@ -0,0 +1,15 @@ +package com.admin4j.elasticsearch; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author andanyang + * @since 2023/12/15 10:08 + */ +@SpringBootApplication +public class ElasticsearchApplication { + public static void main(String[] args) { + SpringApplication.run(ElasticsearchApplication.class, args); + } +} diff --git a/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/entity/Product.java b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/entity/Product.java new file mode 100644 index 0000000..a219945 --- /dev/null +++ b/elasticsearch-spring-boot-starter/src/test/java/com/admin4j/elasticsearch/entity/Product.java @@ -0,0 +1,19 @@ +package com.admin4j.elasticsearch.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author andanyang + * @since 2023/12/15 10:28 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Product { + private String sku; + private String name; + private double price; + private String description; +} diff --git a/elasticsearch-spring-boot-starter/src/test/resources/application.yml b/elasticsearch-spring-boot-starter/src/test/resources/application.yml new file mode 100644 index 0000000..dbf2be0 --- /dev/null +++ b/elasticsearch-spring-boot-starter/src/test/resources/application.yml @@ -0,0 +1,9 @@ +spring: + elasticsearch: + uris: + - http://192.168.0.252:9200 + + +logging: + level: + org.springframework.data.elasticsearch.client.WIRE: TRACE \ No newline at end of file From 815926b525f38bd6e5b5c0b753b7cf334de1b7d9 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Mon, 18 Dec 2023 17:04:33 +0800 Subject: [PATCH 18/30] =?UTF-8?q?feat(security):=20security=20Authenticati?= =?UTF-8?q?onManager=20=E4=BB=A3=E7=A0=81=E9=87=8D=E6=96=B0=E6=A2=B3?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- security-spring-boot-starter/pom.xml | 2 +- .../MultiAuthenticationAutoConfiguration.java | 60 ++++++++++++++ ...uthenticationManagerAutoConfiguration.java | 73 +++++++++++++++++ .../configuration/SecurityConfiguration.java | 80 ++++++------------- .../UserTokenServiceConfiguration.java | 26 ------ .../MultiAuthenticationFilter.java | 21 ++--- .../MultiAuthenticationProvider.java | 11 ++- .../MultiAuthenticationToken.java | 2 +- .../MultiCheckUsernamePasswordService.java | 2 +- .../MultiSecurityConfigurerAdapter.java | 16 ++-- .../MultiUserDetailsService.java | 2 +- .../UsernamePasswordUserDetailsService.java | 8 +- .../security/properties/CorsProperties.java | 2 +- .../properties/FormLoginProperties.java | 8 ++ .../properties/IgnoringUrlProperties.java | 4 +- .../main/resources/META-INF/spring.factories | 4 +- 16 files changed, 208 insertions(+), 113 deletions(-) create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationAutoConfiguration.java create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{mult => multi}/MultiAuthenticationFilter.java (86%) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{mult => multi}/MultiAuthenticationProvider.java (95%) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{mult => multi}/MultiAuthenticationToken.java (98%) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{mult => multi}/MultiCheckUsernamePasswordService.java (98%) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{mult => multi}/MultiSecurityConfigurerAdapter.java (66%) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{mult => multi}/MultiUserDetailsService.java (96%) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{mult => multi}/UsernamePasswordUserDetailsService.java (91%) diff --git a/security-spring-boot-starter/pom.xml b/security-spring-boot-starter/pom.xml index 22fb2d7..a491f78 100644 --- a/security-spring-boot-starter/pom.xml +++ b/security-spring-boot-starter/pom.xml @@ -10,7 +10,7 @@ com.admin4j.framework security-spring-boot-starter jar - 0.9.3-SNAPSHOT + 0.9.4-SNAPSHOT security-spring-boot-starter diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationAutoConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationAutoConfiguration.java new file mode 100644 index 0000000..9c21fab --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationAutoConfiguration.java @@ -0,0 +1,60 @@ +package com.admin4j.framework.security.configuration; + +import com.admin4j.framework.security.filter.JwtAuthenticationTokenFilter; +import com.admin4j.framework.security.multi.MultiAuthenticationFilter; +import com.admin4j.framework.security.multi.MultiSecurityConfigurerAdapter; +import com.admin4j.framework.security.properties.FormLoginProperties; +import com.admin4j.framework.security.properties.MultiAuthenticationProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; + +/** + * 多渠道配置自动登录 + * + * @author andanyang + * @since 2023/12/15 16:15 + */ +@ConditionalOnProperty(prefix = "admin4j.security.multi", value = "enable", matchIfMissing = true) +public class MultiAuthenticationAutoConfiguration { + + @Autowired + MultiAuthenticationProperties multiAuthenticationProperties; + @Autowired + AuthenticationSuccessHandler authenticationSuccessHandler; + @Autowired + AuthenticationFailureHandler authenticationFailureHandler; + @Autowired + FormLoginProperties formLoginProperties; + + + @Bean + @ConditionalOnMissingBean(MultiAuthenticationFilter.class) + public MultiAuthenticationFilter multiAuthenticationFilter(AuthenticationManager authenticationManager) { + + MultiAuthenticationFilter authenticationFilter = new MultiAuthenticationFilter(multiAuthenticationProperties, formLoginProperties); + authenticationFilter.setAuthenticationManager(authenticationManager); + authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler); + authenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); + return authenticationFilter; + } + + /** + * 多渠道认证功能配置 + * + * @return + */ + @Bean + @ConditionalOnMissingBean(MultiSecurityConfigurerAdapter.class) + public MultiSecurityConfigurerAdapter multiSecurityConfigurerAdapter(MultiAuthenticationFilter multiAuthenticationFilter, + JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter, + AuthenticationManager authenticationManager) throws Exception { + + + return new MultiSecurityConfigurerAdapter(multiAuthenticationFilter, authenticationManager, jwtAuthenticationTokenFilter); + } +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java new file mode 100644 index 0000000..641ce57 --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java @@ -0,0 +1,73 @@ +package com.admin4j.framework.security.configuration; + +import com.admin4j.framework.security.multi.MultiAuthenticationProvider; +import com.admin4j.framework.security.multi.MultiUserDetailsService; +import com.admin4j.framework.security.multi.UsernamePasswordUserDetailsService; +import com.admin4j.framework.security.properties.FormLoginProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; + +import java.util.List; + +/** + * 多渠道配置自动登录 + * + * @author andanyang + * @since 2023/12/15 16:15 + */ +@ConditionalOnProperty(prefix = "admin4j.security.multi", value = "enable", matchIfMissing = true) +public class MultiAuthenticationManagerAutoConfiguration { + + /** + * 默认的多渠道表单登录 + * + * @return + */ + @Bean + @ConditionalOnMissingBean(UsernamePasswordUserDetailsService.class) + @ConditionalOnBean(UserDetailsService.class) + // @ConditionalOnProperty(prefix = "admin4j.security.multi", name = "enable", matchIfMissing = true) + @ConditionalOnProperty(prefix = "admin4j.security.form-login", name = "enable", matchIfMissing = true) + public MultiUserDetailsService usernamePasswordUserDetailsService( + UserDetailsService userDetailsService, + PasswordEncoder passwordEncoder, + FormLoginProperties formLoginProperties + ) { + + return new UsernamePasswordUserDetailsService( + passwordEncoder, + formLoginProperties, + userDetailsService + ); + } + + + /** + * 获取 AuthenticationManager + *

+ * 或者: + * + * AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); + * authenticationManagerBuilder.userDetailsService(userDetailsService); + * authenticationManager = authenticationManagerBuilder.build(); + * + * + * @return + */ + + @Bean + @ConditionalOnMissingBean(AuthenticationManager.class) + public AuthenticationManager authenticationManager(@Autowired(required = false) + List userDetailServices) { + + return new ProviderManager(new MultiAuthenticationProvider(userDetailServices)); + } + +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java index 61a82ad..20d9542 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java @@ -2,12 +2,8 @@ import com.admin4j.framework.security.ISecurityIgnoringUrl; import com.admin4j.framework.security.filter.ActuatorFilter; -import com.admin4j.framework.security.filter.JwtAuthenticationTokenFilter; import com.admin4j.framework.security.ignoringUrl.AnonymousAccessUrl; -import com.admin4j.framework.security.mult.MultiAuthenticationFilter; -import com.admin4j.framework.security.mult.MultiAuthenticationProvider; -import com.admin4j.framework.security.mult.MultiSecurityConfigurerAdapter; -import com.admin4j.framework.security.mult.MultiUserDetailsService; +import com.admin4j.framework.security.multi.MultiSecurityConfigurerAdapter; import com.admin4j.framework.security.properties.FormLoginProperties; import com.admin4j.framework.security.properties.IgnoringUrlProperties; import com.admin4j.framework.security.properties.JwtProperties; @@ -19,7 +15,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; @@ -47,6 +42,8 @@ /** * 开启方法级别的注解支持 + * + * @author andanyang */ @EnableGlobalMethodSecurity(prePostEnabled = true) @EnableConfigurationProperties({IgnoringUrlProperties.class, JwtProperties.class, FormLoginProperties.class, MultiAuthenticationProperties.class}) @@ -72,21 +69,16 @@ public class SecurityConfiguration { AnonymousAccessUrl anonymousAccessUrl; @Autowired LogoutSuccessHandler logoutSuccessHandler; - @Autowired - JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; - @Autowired(required = false) - List usernamePasswordAuthenticationFilters; - @Autowired - MultiAuthenticationProperties multiAuthenticationProperties; - @Autowired(required = false) - List userDetailServices; - @Autowired - AuthenticationConfiguration authenticationConfiguration; + // @Autowired + // JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; + // @Autowired(required = false) + // List usernamePasswordAuthenticationFilters; @Autowired(required = false) ActuatorFilter actuatorFilter; @Autowired(required = false) CorsFilter corsFilter; - + @Autowired(required = false) + MultiSecurityConfigurerAdapter multiSecurityConfigurerAdapter; /** * 取消ROLE_前缀 @@ -137,35 +129,27 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti ; // 添加Logout filter - httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); - // 添加JWT filter - httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); + httpSecurity.logout().logoutUrl(formLoginProperties.getLogOutProcessingUrl()).permitAll().logoutSuccessHandler(logoutSuccessHandler); + + // 授权请求配置 + // 忽略URl配置 + ignoringRequestMatcherRegistry(httpSecurity.authorizeRequests()); + // 除上面外的所有请求全部需要鉴权认证;其他路径必须验证 + httpSecurity.authorizeRequests().anyRequest().authenticated(); + // 添加CORS filter if (corsFilter != null) { - httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); + httpSecurity.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class); } - - if (usernamePasswordAuthenticationFilters != null && usernamePasswordAuthenticationFilters.size() > 0) { - usernamePasswordAuthenticationFilters.forEach(usernameFilter -> httpSecurity.addFilterBefore(usernameFilter, UsernamePasswordAuthenticationFilter.class)); + if (actuatorFilter != null) { + httpSecurity.addFilterBefore(actuatorFilter, UsernamePasswordAuthenticationFilter.class); } - - - authorizeRequestsConfigurer(httpSecurity); - + // 多渠道登录 - if (multiAuthenticationProperties.isEnable()) { - - MultiAuthenticationFilter authenticationFilter = new MultiAuthenticationFilter(multiAuthenticationProperties, formLoginProperties); - - authenticationFilter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager()); - authenticationFilter.setAuthenticationFailureHandler(authenticationFailureHandler); - authenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); - - MultiSecurityConfigurerAdapter multiSecurityConfigurerAdapter = new MultiSecurityConfigurerAdapter(authenticationFilter, new MultiAuthenticationProvider(userDetailServices)); + if (multiSecurityConfigurerAdapter != null) { httpSecurity.apply(multiSecurityConfigurerAdapter); - - } else { + } else if (formLoginProperties.isEnable()) { // 开启form表单认证 httpSecurity.formLogin() .loginProcessingUrl(formLoginProperties.getLoginProcessingUrl()) @@ -174,29 +158,11 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti .failureHandler(authenticationFailureHandler) .successHandler(authenticationSuccessHandler) .permitAll(); - - } - - if (actuatorFilter != null) { - httpSecurity.addFilterBefore(actuatorFilter, UsernamePasswordAuthenticationFilter.class); } - // GlobalAuthenticationConfigurerAdapter - // WebSecurityConfigurerAdapter return httpSecurity.build(); } - /** - * 授权请求配置 - */ - private void authorizeRequestsConfigurer(HttpSecurity httpSecurity) throws Exception { - ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry = - httpSecurity.authorizeRequests(); - // 忽略URl配置 - ignoringRequestMatcherRegistry(expressionInterceptUrlRegistry); - // 除上面外的所有请求全部需要鉴权认证;其他路径必须验证 - expressionInterceptUrlRegistry.anyRequest().authenticated(); - } /** * 忽略URl配置 diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java index 58345e7..95cafac 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/UserTokenServiceConfiguration.java @@ -7,20 +7,15 @@ import com.admin4j.framework.security.filter.ActuatorFilter; import com.admin4j.framework.security.jwt.JwtUserDetailsService; import com.admin4j.framework.security.jwt.JwtUserTokenService; -import com.admin4j.framework.security.mult.MultiCheckUsernamePasswordService; -import com.admin4j.framework.security.mult.UsernamePasswordUserDetailsService; import com.admin4j.framework.security.properties.ActuatorProperties; -import com.admin4j.framework.security.properties.FormLoginProperties; import com.admin4j.framework.security.properties.JwtProperties; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -46,27 +41,6 @@ public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } - /** - * 默认的表达登录 - * - * @return - */ - @Bean - @ConditionalOnMissingBean(MultiCheckUsernamePasswordService.class) - @ConditionalOnBean(UserDetailsService.class) - @ConditionalOnProperty(prefix = "admin4j.security.multi", name = "enable", matchIfMissing = true) - public UsernamePasswordUserDetailsService usernamePasswordUserDetailsService( - UserDetailsService userDetailsService, - PasswordEncoder passwordEncoder, - FormLoginProperties formLoginProperties) { - - return new UsernamePasswordUserDetailsService( - passwordEncoder, - formLoginProperties, - userDetailsService - ); - } - @Bean @ConditionalOnMissingBean(IUserContextHolder.class) diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationFilter.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationFilter.java similarity index 86% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationFilter.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationFilter.java index 27554ef..df7cb00 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationFilter.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.security.mult; +package com.admin4j.framework.security.multi; import com.admin4j.framework.security.properties.FormLoginProperties; import com.admin4j.framework.security.properties.MultiAuthenticationProperties; @@ -23,8 +23,8 @@ public class MultiAuthenticationFilter extends AbstractAuthenticationProcessingFilter { static final String DEFAULT_AUTH_TYPE = ""; - private MultiAuthenticationProperties multiAuthenticationProperties; - private FormLoginProperties formLoginProperties; + private final MultiAuthenticationProperties multiAuthenticationProperties; + private final FormLoginProperties formLoginProperties; public MultiAuthenticationFilter(MultiAuthenticationProperties multiAuthenticationProperties, FormLoginProperties formLoginProperties) { @@ -51,7 +51,13 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ return this.getAuthenticationManager().authenticate(token); } - private MultiAuthenticationToken obtainToken(HttpServletRequest request) { + /** + * 获取未认证的令牌 + * + * @param request + * @return + */ + protected MultiAuthenticationToken obtainToken(HttpServletRequest request) { /** * 获取授权方式 @@ -60,17 +66,13 @@ private MultiAuthenticationToken obtainToken(HttpServletRequest request) { if (StringUtils.isBlank(authType)) { - // 尝试去uri路径里面获取 + // 尝试去uri路径里面获取 /login/phone String requestURI = request.getRequestURI(); authType = StringUtils.substringAfter(requestURI, multiAuthenticationProperties.getLoginProcessingUrlPrefix()); } String principal; if (StringUtils.isBlank(authType)) { - // if (!formLoginProperties.isEnable()) { - // throw new AuthenticationServiceException( - // "Authentication authType not find: " + request.getRequestURI()); - //} // 默认开启了formLogin 获取默认的 username字段 authType = DEFAULT_AUTH_TYPE; principal = request.getParameter(formLoginProperties.getUsernameParameter()); @@ -79,7 +81,6 @@ private MultiAuthenticationToken obtainToken(HttpServletRequest request) { principal = request.getParameter(field); } - return MultiAuthenticationToken.unauthenticated(authType, principal, request.getParameterMap()); } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationProvider.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationProvider.java similarity index 95% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationProvider.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationProvider.java index e8201ce..c68ad85 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationProvider.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationProvider.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.security.mult; +package com.admin4j.framework.security.multi; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -23,7 +23,6 @@ public class MultiAuthenticationProvider implements AuthenticationProvider { private final List userDetailServices; protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); - private UserDetailsChecker preAuthenticationChecks = new DefaultPreAuthenticationChecks(); private UserDetailsChecker postAuthenticationChecks = new DefaultPostAuthenticationChecks(); @@ -43,6 +42,7 @@ public boolean supports(Class authentication) { public Authentication authenticate(Authentication authentication) throws AuthenticationException { MultiAuthenticationToken authenticationToken = (MultiAuthenticationToken) authentication; + // 查找支持当前登录方式的 MultiUserDetailsService MultiUserDetailsService userDetailService = null; if (userDetailServices != null) { for (MultiUserDetailsService item : userDetailServices) { @@ -59,13 +59,16 @@ public Authentication authenticate(Authentication authentication) throws Authent boolean b = userDetailService.preVerify(authenticationToken); if (!b) { - throw new InternalAuthenticationServiceException("Authentication failure"); + throw new InternalAuthenticationServiceException("Authentication preVerify failure"); } + // 加载用户信息 UserDetails userDetails = userDetailService.loadUserByMultiToken((String) authenticationToken.getPrincipal()); + + // 检查用户信息 this.preAuthenticationChecks.check(userDetails); - //生成一个认证成功 Authentication + // 生成一个认证成功 Authentication MultiAuthenticationToken multiAuthenticationToken = new MultiAuthenticationToken(authenticationToken.getAuthType(), userDetails, userDetails.getAuthorities()); multiAuthenticationToken.setDetails(authenticationToken.getDetails()); multiAuthenticationToken.setAuthParameters(authenticationToken.getAuthParameters()); diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationToken.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationToken.java similarity index 98% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationToken.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationToken.java index 47820c6..487f7a4 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiAuthenticationToken.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationToken.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.security.mult; +package com.admin4j.framework.security.multi; import lombok.Getter; import lombok.Setter; diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiCheckUsernamePasswordService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiCheckUsernamePasswordService.java similarity index 98% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiCheckUsernamePasswordService.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiCheckUsernamePasswordService.java index 17f5c57..6768059 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiCheckUsernamePasswordService.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiCheckUsernamePasswordService.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.security.mult; +package com.admin4j.framework.security.multi; import com.admin4j.common.Prioritized; import com.admin4j.framework.security.properties.FormLoginProperties; diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiSecurityConfigurerAdapter.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiSecurityConfigurerAdapter.java similarity index 66% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiSecurityConfigurerAdapter.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiSecurityConfigurerAdapter.java index 7c9f066..2844880 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiSecurityConfigurerAdapter.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiSecurityConfigurerAdapter.java @@ -1,8 +1,8 @@ -package com.admin4j.framework.security.mult; +package com.admin4j.framework.security.multi; +import com.admin4j.framework.security.filter.JwtAuthenticationTokenFilter; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -15,14 +15,18 @@ public class MultiSecurityConfigurerAdapter extends AbstractHttpConfigurer { private final MultiAuthenticationFilter multiAuthenticationFilter; - private final AuthenticationProvider authenticationProvider; + + private final AuthenticationManager authenticationManager; + + private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Override public void configure(HttpSecurity http) throws Exception { - - AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class); + multiAuthenticationFilter.setAuthenticationManager(authenticationManager); - http.authenticationProvider(authenticationProvider) + http.authenticationManager(authenticationManager) + // 添加JWT filter + .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(multiAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiUserDetailsService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiUserDetailsService.java similarity index 96% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiUserDetailsService.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiUserDetailsService.java index 31798d2..9a79cb0 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/MultiUserDetailsService.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiUserDetailsService.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.security.mult; +package com.admin4j.framework.security.multi; import com.admin4j.common.Prioritized; import org.springframework.security.core.userdetails.UserDetails; diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/UsernamePasswordUserDetailsService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/UsernamePasswordUserDetailsService.java similarity index 91% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/UsernamePasswordUserDetailsService.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/UsernamePasswordUserDetailsService.java index a97d135..71ddd5a 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/mult/UsernamePasswordUserDetailsService.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/UsernamePasswordUserDetailsService.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.security.mult; +package com.admin4j.framework.security.multi; import com.admin4j.framework.security.properties.FormLoginProperties; import lombok.extern.slf4j.Slf4j; @@ -15,7 +15,9 @@ @Slf4j public class UsernamePasswordUserDetailsService extends MultiCheckUsernamePasswordService { - + /** + * 兼容系统 原来的 UserDetailsService + */ private final UserDetailsService userDetailsService; public UsernamePasswordUserDetailsService(PasswordEncoder passwordEncoder, FormLoginProperties formLoginProperties, UserDetailsService userDetailsService) { @@ -32,6 +34,8 @@ public UsernamePasswordUserDetailsService(PasswordEncoder passwordEncoder, FormL */ @Override public UserDetails loadUserByMultiToken(String multiToken) { + + return userDetailsService.loadUserByUsername(multiToken); } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/CorsProperties.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/CorsProperties.java index 148a2f0..a60510c 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/CorsProperties.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/CorsProperties.java @@ -31,5 +31,5 @@ public class CorsProperties { /** * 是否允许客户端带cookie */ - private boolean allowCredentials = true; + private boolean allowCredentials = false; } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/FormLoginProperties.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/FormLoginProperties.java index 711c8e7..83adf0c 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/FormLoginProperties.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/FormLoginProperties.java @@ -16,6 +16,10 @@ public class FormLoginProperties { * 开启form 处理 url */ private String loginProcessingUrl = "/login"; + /** + * 退出处理url + */ + private String logOutProcessingUrl = "/logout"; /** * 密码字段名 */ @@ -24,4 +28,8 @@ public class FormLoginProperties { * 账号字段名 */ private String usernameParameter = "username"; + /** + * 开启默认的表单登录 + */ + private boolean enable = true; } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java index a66da49..c172df5 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java @@ -4,7 +4,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; /** - * 忽略url 配置文件 + * 根据配置文件 忽略url * * @author andanyang * @since 2023/3/24 17:00 @@ -14,7 +14,7 @@ public class IgnoringUrlProperties { /** - * 包含所有请求类型的路径 + * 包含所有请求类型的路径,不考虑请求方法 */ private String[] uris; /** diff --git a/security-spring-boot-starter/src/main/resources/META-INF/spring.factories b/security-spring-boot-starter/src/main/resources/META-INF/spring.factories index 280eadb..78953a9 100644 --- a/security-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/security-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -6,7 +6,9 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.admin4j.framework.security.configuration.SecurityConfiguration, \ com.admin4j.framework.security.configuration.SecurityHandlerConfiguration, \ com.admin4j.framework.security.configuration.CorsConfiguration, \ - com.admin4j.framework.security.configuration.UserTokenServiceConfiguration + com.admin4j.framework.security.configuration.UserTokenServiceConfiguration, \ + com.admin4j.framework.security.configuration.MultiAuthenticationManagerAutoConfiguration, \ + com.admin4j.framework.security.configuration.MultiAuthenticationAutoConfiguration From e27f1105c9f331ab04889bd8cb548810086a788c Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Mon, 18 Dec 2023 17:46:38 +0800 Subject: [PATCH 19/30] =?UTF-8?q?feat(security):=20security=20Authenticati?= =?UTF-8?q?onManager=20=E4=BB=A3=E7=A0=81=E9=87=8D=E6=96=B0=E6=A2=B3?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- security-spring-boot-starter/README.md | 5 +++++ .../MultiAuthenticationManagerAutoConfiguration.java | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/security-spring-boot-starter/README.md b/security-spring-boot-starter/README.md index 9f917b2..5cde5c0 100644 --- a/security-spring-boot-starter/README.md +++ b/security-spring-boot-starter/README.md @@ -1,5 +1,10 @@ # admin security +## Features + +- 多渠道登录 +- 一个注解/一个配置,解决匿名url访问(忽略认证) + ## USAGES 1. 引入 pom diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java index 641ce57..6e334c9 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java @@ -69,5 +69,4 @@ public AuthenticationManager authenticationManager(@Autowired(required = false) return new ProviderManager(new MultiAuthenticationProvider(userDetailServices)); } - } From 44dfa856496d2f543e7d47f9d89c18991a59b5f3 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Mon, 18 Dec 2023 17:48:37 +0800 Subject: [PATCH 20/30] =?UTF-8?q?feat(security):=20security=20Authenticati?= =?UTF-8?q?onManager=20=E4=BB=A3=E7=A0=81=E9=87=8D=E6=96=B0=E6=A2=B3?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin4j-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index e1f304d..ed76042 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -155,7 +155,7 @@ com.admin4j.framework security-spring-boot-starter - 0.9.3-SNAPSHOT + 0.9.4-SNAPSHOT com.admin4j.framework From ddaeb9322339dcdaf85ecd488836580140faefd6 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 19 Dec 2023 09:52:50 +0800 Subject: [PATCH 21/30] feat(AuthenticationHandler): AuthenticationHandler rename --- ...Result.java => AuthenticationHandler.java} | 4 ++- .../configuration/SecurityConfiguration.java | 2 +- .../SecurityHandlerConfiguration.java | 28 +++++++++---------- .../filter/JwtAuthenticationTokenFilter.java | 6 ++-- ...java => DefaultAuthenticationHandler.java} | 4 +-- .../handler/RestAccessDeniedHandler.java | 6 ++-- .../handler/RestAuthenticationEntryPoint.java | 8 +++--- .../RestAuthenticationFailureHandler.java | 6 ++-- .../RestAuthenticationSuccessHandler.java | 9 ++---- .../handler/RestLogoutSuccessHandler.java | 6 ++-- 10 files changed, 39 insertions(+), 40 deletions(-) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/{AuthenticationResult.java => AuthenticationHandler.java} (94%) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/{DefaultAuthenticationResult.java => DefaultAuthenticationHandler.java} (96%) diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/AuthenticationResult.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/AuthenticationHandler.java similarity index 94% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/AuthenticationResult.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/AuthenticationHandler.java index 2b1f9f5..5542dd9 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/AuthenticationResult.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/AuthenticationHandler.java @@ -8,10 +8,12 @@ import javax.servlet.http.HttpServletResponse; /** + * 认证成功结果回调处理 + * * @author andanyang * @since 2023/5/31 17:57 */ -public interface AuthenticationResult { +public interface AuthenticationHandler { /** * 认证成功回调 diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java index 20d9542..1f530c3 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java @@ -145,7 +145,7 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti if (actuatorFilter != null) { httpSecurity.addFilterBefore(actuatorFilter, UsernamePasswordAuthenticationFilter.class); } - + // 多渠道登录 if (multiSecurityConfigurerAdapter != null) { httpSecurity.apply(multiSecurityConfigurerAdapter); diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java index 5aa4f5e..8734f9c 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java @@ -1,6 +1,6 @@ package com.admin4j.framework.security.configuration; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import com.admin4j.framework.security.UserTokenService; import com.admin4j.framework.security.handler.*; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -15,39 +15,39 @@ public class SecurityHandlerConfiguration { @Bean - @ConditionalOnMissingBean({AuthenticationResult.class}) - public AuthenticationResult authenticationResult(UserTokenService userTokenService) { - return new DefaultAuthenticationResult(userTokenService); + @ConditionalOnMissingBean({AuthenticationHandler.class}) + public AuthenticationHandler authenticationResult(UserTokenService userTokenService) { + return new DefaultAuthenticationHandler(userTokenService); } @Bean @ConditionalOnMissingBean(AuthenticationEntryPoint.class) - public AuthenticationEntryPoint authenticationEntryPoint(AuthenticationResult authenticationResult) { - return new RestAuthenticationEntryPoint(authenticationResult); + public AuthenticationEntryPoint authenticationEntryPoint(AuthenticationHandler authenticationHandler) { + return new RestAuthenticationEntryPoint(authenticationHandler); } @Bean @ConditionalOnMissingBean(AuthenticationFailureHandler.class) - public AuthenticationFailureHandler authenticationFailureHandler(AuthenticationResult authenticationResult) { - return new RestAuthenticationFailureHandler(authenticationResult); + public AuthenticationFailureHandler authenticationFailureHandler(AuthenticationHandler authenticationHandler) { + return new RestAuthenticationFailureHandler(authenticationHandler); } @Bean @ConditionalOnMissingBean(AuthenticationSuccessHandler.class) - public AuthenticationSuccessHandler authenticationSuccessHandler(AuthenticationResult authenticationResult) { - return new RestAuthenticationSuccessHandler(authenticationResult); + public AuthenticationSuccessHandler authenticationSuccessHandler(AuthenticationHandler authenticationHandler) { + return new RestAuthenticationSuccessHandler(authenticationHandler); } @Bean @ConditionalOnMissingBean(AccessDeniedHandler.class) - public AccessDeniedHandler accessDeniedHandler(AuthenticationResult authenticationResult) { - return new RestAccessDeniedHandler(authenticationResult); + public AccessDeniedHandler accessDeniedHandler(AuthenticationHandler authenticationHandler) { + return new RestAccessDeniedHandler(authenticationHandler); } @Bean @ConditionalOnMissingBean(LogoutSuccessHandler.class) - public LogoutSuccessHandler logoutSuccessHandler(AuthenticationResult authenticationResult) { - return new RestLogoutSuccessHandler(authenticationResult); + public LogoutSuccessHandler logoutSuccessHandler(AuthenticationHandler authenticationHandler) { + return new RestLogoutSuccessHandler(authenticationHandler); } @Bean diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java index 5f3102b..061b0c6 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -2,7 +2,7 @@ import com.admin4j.common.pojo.AuthenticationUser; import com.admin4j.common.util.UserContextUtil; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import com.admin4j.framework.security.UserTokenService; import com.admin4j.framework.security.exception.JwtTokenExpiredException; import com.admin4j.framework.security.factory.AuthenticationUserFactory; @@ -35,7 +35,7 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { //@Autowired // UserDetailsService userDetailsService; @Autowired - AuthenticationResult authenticationResult; + AuthenticationHandler authenticationHandler; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { @@ -64,7 +64,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } } catch (Exception e) { log.error("authenticationEntryPoint {}", e.getMessage(), e); - authenticationResult.authenticationEntryPoint(request, response, new JwtTokenExpiredException(e.getMessage(), e)); + authenticationHandler.authenticationEntryPoint(request, response, new JwtTokenExpiredException(e.getMessage(), e)); return; } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationHandler.java similarity index 96% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationHandler.java index 50ff672..92b8ae6 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationResult.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/DefaultAuthenticationHandler.java @@ -6,7 +6,7 @@ import com.admin4j.common.pojo.SimpleResponse; import com.admin4j.common.util.ServletUtils; import com.admin4j.common.util.UserContextUtil; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import com.admin4j.framework.security.UserTokenService; import com.admin4j.framework.security.event.AuthenticationSuccessEvent; import com.admin4j.framework.security.factory.AuthenticationUserFactory; @@ -30,7 +30,7 @@ */ @Slf4j @RequiredArgsConstructor -public class DefaultAuthenticationResult implements AuthenticationResult { +public class DefaultAuthenticationHandler implements AuthenticationHandler { protected static final IResponse FAIL_AUTH_FORBIDDEN = new SimpleResponse(ResponseEnum.FAIL_AUTH_FORBIDDEN); diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAccessDeniedHandler.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAccessDeniedHandler.java index 9509d1e..2ba62a7 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAccessDeniedHandler.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAccessDeniedHandler.java @@ -1,6 +1,6 @@ package com.admin4j.framework.security.handler; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import lombok.RequiredArgsConstructor; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; @@ -18,7 +18,7 @@ @RequiredArgsConstructor public class RestAccessDeniedHandler implements AccessDeniedHandler { - final AuthenticationResult authenticationResult; + final AuthenticationHandler authenticationHandler; @Override public void handle( @@ -26,6 +26,6 @@ public void handle( throws IOException, ServletException { - authenticationResult.accessDeniedHandler(httpServletRequest, response, e); + authenticationHandler.accessDeniedHandler(httpServletRequest, response, e); } } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationEntryPoint.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationEntryPoint.java index e532365..3344249 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationEntryPoint.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationEntryPoint.java @@ -1,6 +1,6 @@ package com.admin4j.framework.security.handler; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.AuthenticationException; @@ -19,8 +19,8 @@ @Slf4j @RequiredArgsConstructor public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { - - private final AuthenticationResult authenticationResult; + + private final AuthenticationHandler authenticationHandler; @Override public void commence( @@ -29,6 +29,6 @@ public void commence( AuthenticationException authException) throws IOException, ServletException { - authenticationResult.authenticationEntryPoint(request, response, authException); + authenticationHandler.authenticationEntryPoint(request, response, authException); } } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationFailureHandler.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationFailureHandler.java index 6f62f67..2bb13f3 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationFailureHandler.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationFailureHandler.java @@ -1,6 +1,6 @@ package com.admin4j.framework.security.handler; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import lombok.RequiredArgsConstructor; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; @@ -17,10 +17,10 @@ @RequiredArgsConstructor public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler { - final AuthenticationResult authenticationResult; + final AuthenticationHandler authenticationHandler; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - authenticationResult.onAuthenticationFailure(request, response, exception); + authenticationHandler.onAuthenticationFailure(request, response, exception); } } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationSuccessHandler.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationSuccessHandler.java index 99bcfb7..d57c340 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationSuccessHandler.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestAuthenticationSuccessHandler.java @@ -1,6 +1,6 @@ package com.admin4j.framework.security.handler; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -19,6 +19,7 @@ */ @RequiredArgsConstructor public class RestAuthenticationSuccessHandler implements AuthenticationSuccessHandler, ApplicationContextAware { + final AuthenticationHandler authenticationHandler; private ApplicationContext applicationContext; @Override @@ -26,14 +27,10 @@ public void setApplicationContext(ApplicationContext applicationContext) throws this.applicationContext = applicationContext; } - - final AuthenticationResult authenticationResult; - - @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - authenticationResult.onAuthenticationSuccess(request, response, authentication); + authenticationHandler.onAuthenticationSuccess(request, response, authentication); } } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestLogoutSuccessHandler.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestLogoutSuccessHandler.java index bf250b9..d406a57 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestLogoutSuccessHandler.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/RestLogoutSuccessHandler.java @@ -1,6 +1,6 @@ package com.admin4j.framework.security.handler; -import com.admin4j.framework.security.AuthenticationResult; +import com.admin4j.framework.security.AuthenticationHandler; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; @@ -17,11 +17,11 @@ @RequiredArgsConstructor public class RestLogoutSuccessHandler implements LogoutSuccessHandler { - final AuthenticationResult authenticationResult; + final AuthenticationHandler authenticationHandler; @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - authenticationResult.onLogoutSuccess(request, response, authentication); + authenticationHandler.onLogoutSuccess(request, response, authentication); } } From 86639a442b184988bc04f9040dc68725c570c35e Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 19 Dec 2023 14:43:10 +0800 Subject: [PATCH 22/30] =?UTF-8?q?feat(security):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8A=A8=E6=80=81=E6=9D=83=E9=99=90?= =?UTF-8?q?=20PermissionAuthorizationManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- security-spring-boot-starter/README.md | 1 + security-spring-boot-starter/pom.xml | 2 +- .../authorization/IPermissionUriService.java | 26 ++++ .../PermissionAuthorizationManager.java | 114 ++++++++++++++++++ ...uthenticationManagerAutoConfiguration.java | 2 +- .../PermissionAutoConfiguration.java | 22 ++++ .../configuration/SecurityConfiguration.java | 51 +++++--- .../multi/MultiAuthenticationFilter.java | 2 +- 8 files changed, 199 insertions(+), 21 deletions(-) create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java diff --git a/security-spring-boot-starter/README.md b/security-spring-boot-starter/README.md index 5cde5c0..90a0a4a 100644 --- a/security-spring-boot-starter/README.md +++ b/security-spring-boot-starter/README.md @@ -4,6 +4,7 @@ - 多渠道登录 - 一个注解/一个配置,解决匿名url访问(忽略认证) +- 基于数据库的动态权限 ## USAGES diff --git a/security-spring-boot-starter/pom.xml b/security-spring-boot-starter/pom.xml index a491f78..142144f 100644 --- a/security-spring-boot-starter/pom.xml +++ b/security-spring-boot-starter/pom.xml @@ -10,7 +10,7 @@ com.admin4j.framework security-spring-boot-starter jar - 0.9.4-SNAPSHOT + 0.9.5-SNAPSHOT security-spring-boot-starter diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java new file mode 100644 index 0000000..8ce5e16 --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java @@ -0,0 +1,26 @@ +package com.admin4j.framework.security.authorization; + +import java.util.List; + +/** + * 权限uri 服务 + * + * @author andanyang + * @since 2023/12/19 14:34 + */ +public interface IPermissionUriService { + + /** + * 获取 系统 所有的 PermissionUri + * + * @return + */ + List allPermissionUri(); + + /** + * 当前用户拥有的权限 + * + * @return + */ + List getMyPermissionUrls(); +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java new file mode 100644 index 0000000..cff3395 --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java @@ -0,0 +1,114 @@ +package com.admin4j.framework.security.authorization; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.util.AntPathMatcher; + +import java.util.Collection; +import java.util.function.Supplier; + +/** + * 自定义数据权限(授权)处理 + * 被授 AuthorizationFilter 调用,负责做出最终的访问控制决定 + * + * @author andanyang + * @since 2023/12/19 9:53 + */ +@RequiredArgsConstructor +public class PermissionAuthorizationManager implements AuthorizationManager { + + /** + * 有权限 + */ + protected static final AuthorizationDecision GRANTED = new AuthorizationDecision(true); + /** + * 没有权限 + */ + protected static final AuthorizationDecision UN_AUTHORIZED = new AuthorizationDecision(false); + + protected final IPermissionUriService permissionUriService; + + protected AntPathMatcher antPathMatcher = new AntPathMatcher(); + + /** + * Determines if access is granted for a specific authentication and object. + * + * @param authentication the {@link Supplier} of the {@link Authentication} to check + * @param object the {@link T} object to check + * @return an {@link AuthorizationDecision} or null if no decision could be made + */ + @Override + public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext object) { + + // 获取当前请求的 URL 地址 + String requestURI = object.getRequest().getRequestURI(); + boolean matchPermission = matchPermission(requestURI); + if (matchPermission) { + return GRANTED; + } + + // 沒有匹配到, 查看当前 requestURI 是否需要权限控制 + return urlNeedPermission(requestURI) ? UN_AUTHORIZED : GRANTED; + } + + /** + * url 是否需要授权 + * TODO 放在 service 立马 + * + * @return + */ + public boolean urlNeedPermission(String requestURI) { + + Collection allPermissionUrls = getAllPermissionUrls(); + for (String url : allPermissionUrls) { + if (antPathMatcher.match(url, requestURI)) { + return true; + } + } + return false; + } + + /** + * 当前用户是否可以匹配到访问该url权限 + * + * @param requestURI + * @return + */ + public boolean matchPermission(String requestURI) { + Collection permissionUrls = getPermissionUrls(); + + if (permissionUrls == null || permissionUrls.isEmpty()) { + return false; + } + + for (String url : permissionUrls) { + if (antPathMatcher.match(url, requestURI)) { + return true; + } + } + return false; + } + + /** + * 当前用户拥有的权限 + * + * @return + */ + public Collection getPermissionUrls() { + + return permissionUriService.getMyPermissionUrls(); + } + + + /** + * 获取全部权限 + * + * @return + */ + protected Collection getAllPermissionUrls() { + return permissionUriService.allPermissionUri(); + } +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java index 6e334c9..7554de7 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/MultiAuthenticationManagerAutoConfiguration.java @@ -50,7 +50,7 @@ public MultiUserDetailsService usernamePasswordUserDetailsService( /** - * 获取 AuthenticationManager + * 获取 PermissionAuthorizationManager *

* 或者: * diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java new file mode 100644 index 0000000..b0f49f5 --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java @@ -0,0 +1,22 @@ +package com.admin4j.framework.security.configuration; + +import com.admin4j.framework.security.authorization.IPermissionUriService; +import com.admin4j.framework.security.authorization.PermissionAuthorizationManager; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +/** + * @author andanyang + * @since 2023/12/19 14:40 + */ +@ConditionalOnBean(IPermissionUriService.class) +@ConditionalOnMissingBean(PermissionAuthorizationManager.class) +public class PermissionAutoConfiguration { + + @Bean + public PermissionAuthorizationManager permissionAuthorizationManager(IPermissionUriService permissionUriService) { + + return new PermissionAuthorizationManager(permissionUriService); + } +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java index 1f530c3..d9bb4b5 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java @@ -1,6 +1,7 @@ package com.admin4j.framework.security.configuration; import com.admin4j.framework.security.ISecurityIgnoringUrl; +import com.admin4j.framework.security.authorization.PermissionAuthorizationManager; import com.admin4j.framework.security.filter.ActuatorFilter; import com.admin4j.framework.security.ignoringUrl.AnonymousAccessUrl; import com.admin4j.framework.security.multi.MultiSecurityConfigurerAdapter; @@ -16,8 +17,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.SecurityFilterChain; @@ -33,7 +35,7 @@ /** * TODO 需要注入,取消 UserDetailsServiceAutoConfiguration 开启 - * value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class, + * value = { PermissionAuthorizationManager.class, AuthenticationProvider.class, UserDetailsService.class, * AuthenticationManagerResolver.class }, * * @author andanyang @@ -79,7 +81,8 @@ public class SecurityConfiguration { CorsFilter corsFilter; @Autowired(required = false) MultiSecurityConfigurerAdapter multiSecurityConfigurerAdapter; - + @Autowired(required = false) + PermissionAuthorizationManager permissionAuthorizationManager; /** * 取消ROLE_前缀 */ @@ -131,11 +134,6 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti // 添加Logout filter httpSecurity.logout().logoutUrl(formLoginProperties.getLogOutProcessingUrl()).permitAll().logoutSuccessHandler(logoutSuccessHandler); - // 授权请求配置 - // 忽略URl配置 - ignoringRequestMatcherRegistry(httpSecurity.authorizeRequests()); - // 除上面外的所有请求全部需要鉴权认证;其他路径必须验证 - httpSecurity.authorizeRequests().anyRequest().authenticated(); // 添加CORS filter if (corsFilter != null) { @@ -160,6 +158,22 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti .permitAll(); } + // 授权请求配置 authorizeHttpRequests(6.0 新版) authorizeRequests(旧版) 区别 + // httpSecurity.authorizeRequests().anyRequest().authenticated(); + httpSecurity.authorizeHttpRequests(register -> { + + // 忽略URl配置 + ignoringRequestMatcherRegistry(register); + if (permissionAuthorizationManager != null) { + // 自定义授权 + register.anyRequest().access(permissionAuthorizationManager); + } else { + // 除上面外的所有请求全部需要鉴权认证;其他路径必须验证 + register.anyRequest().authenticated(); + } + + }); + return httpSecurity.build(); } @@ -167,7 +181,8 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti /** * 忽略URl配置 */ - private void ignoringRequestMatcherRegistry(ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry) { + private void ignoringRequestMatcherRegistry(AbstractRequestMatcherRegistry.AuthorizedUrl> matcherRegistry) { + if (securityIgnoringUrls != null && !securityIgnoringUrls.isEmpty()) { securityIgnoringUrls.forEach(url -> { @@ -177,9 +192,9 @@ private void ignoringRequestMatcherRegistry(ExpressionUrlAuthorizationConfigurer } if (url.support() == null) { - expressionInterceptUrlRegistry.antMatchers(url.ignoringUrls()).permitAll(); + matcherRegistry.mvcMatchers(url.ignoringUrls()).permitAll(); } else { - expressionInterceptUrlRegistry.antMatchers(url.support(), url.ignoringUrls()).permitAll(); + matcherRegistry.antMatchers(url.support(), url.ignoringUrls()).permitAll(); } }); } @@ -187,23 +202,23 @@ private void ignoringRequestMatcherRegistry(ExpressionUrlAuthorizationConfigurer if (ignoringUrlProperties != null) { if (ignoringUrlProperties.getUris() != null && ignoringUrlProperties.getUris().length > 0) { - expressionInterceptUrlRegistry.antMatchers(ignoringUrlProperties.getUris()).permitAll(); + matcherRegistry.antMatchers(ignoringUrlProperties.getUris()).permitAll(); } if (ignoringUrlProperties.getGet() != null && ignoringUrlProperties.getGet().length > 0) { - expressionInterceptUrlRegistry.antMatchers(HttpMethod.GET, ignoringUrlProperties.getGet()).permitAll(); + matcherRegistry.antMatchers(HttpMethod.GET, ignoringUrlProperties.getGet()).permitAll(); } if (ignoringUrlProperties.getPost() != null && ignoringUrlProperties.getPost().length > 0) { - expressionInterceptUrlRegistry.antMatchers(HttpMethod.POST, ignoringUrlProperties.getPost()).permitAll(); + matcherRegistry.antMatchers(HttpMethod.POST, ignoringUrlProperties.getPost()).permitAll(); } if (ignoringUrlProperties.getPut() != null && ignoringUrlProperties.getPut().length > 0) { - expressionInterceptUrlRegistry.antMatchers(HttpMethod.PUT, ignoringUrlProperties.getPut()).permitAll(); + matcherRegistry.antMatchers(HttpMethod.PUT, ignoringUrlProperties.getPut()).permitAll(); } if (ignoringUrlProperties.getPatch() != null && ignoringUrlProperties.getPatch().length > 0) { - expressionInterceptUrlRegistry.antMatchers(HttpMethod.PATCH, ignoringUrlProperties.getPatch()).permitAll(); + matcherRegistry.antMatchers(HttpMethod.PATCH, ignoringUrlProperties.getPatch()).permitAll(); } if (ignoringUrlProperties.getDelete() != null && ignoringUrlProperties.getDelete().length > 0) { - expressionInterceptUrlRegistry.antMatchers(HttpMethod.DELETE, ignoringUrlProperties.getDelete()).permitAll(); + matcherRegistry.antMatchers(HttpMethod.DELETE, ignoringUrlProperties.getDelete()).permitAll(); } } @@ -213,7 +228,7 @@ private void ignoringRequestMatcherRegistry(ExpressionUrlAuthorizationConfigurer Map anonymousUrl = anonymousAccessUrl.getAnonymousUrl(); anonymousUrl.keySet().forEach(i -> { - expressionInterceptUrlRegistry.antMatchers(i, anonymousUrl.get(i)).permitAll(); + matcherRegistry.antMatchers(i, anonymousUrl.get(i)).permitAll(); }); } } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationFilter.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationFilter.java index df7cb00..3fe0cfe 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationFilter.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/multi/MultiAuthenticationFilter.java @@ -47,7 +47,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ MultiAuthenticationToken token = obtainToken(request); setDetails(request, token); - // 匹配成功交给 AuthenticationManager 去认证 + // 匹配成功交给 PermissionAuthorizationManager 去认证 return this.getAuthenticationManager().authenticate(token); } From 7cc902e42b8a78c638b0636ecb1410e12a23ccf3 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 19 Dec 2023 14:45:34 +0800 Subject: [PATCH 23/30] =?UTF-8?q?feat(security):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8A=A8=E6=80=81=E6=9D=83=E9=99=90?= =?UTF-8?q?=20PermissionAuthorizationManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/authorization/PermissionAuthorizationManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java index cff3395..944a94a 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java @@ -36,7 +36,7 @@ public class PermissionAuthorizationManager implements AuthorizationManager Date: Tue, 19 Dec 2023 14:49:54 +0800 Subject: [PATCH 24/30] =?UTF-8?q?feat(security):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8A=A8=E6=80=81=E6=9D=83=E9=99=90?= =?UTF-8?q?=20PermissionAuthorizationManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/META-INF/spring.factories | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security-spring-boot-starter/src/main/resources/META-INF/spring.factories b/security-spring-boot-starter/src/main/resources/META-INF/spring.factories index 78953a9..3a93ddc 100644 --- a/security-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/security-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -8,7 +8,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.admin4j.framework.security.configuration.CorsConfiguration, \ com.admin4j.framework.security.configuration.UserTokenServiceConfiguration, \ com.admin4j.framework.security.configuration.MultiAuthenticationManagerAutoConfiguration, \ - com.admin4j.framework.security.configuration.MultiAuthenticationAutoConfiguration + com.admin4j.framework.security.configuration.MultiAuthenticationAutoConfiguration, \ + com.admin4j.framework.security.configuration.PermissionAutoConfiguration From eccd4579d80db680821dd1c16f09ae9fc1a5796f Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 19 Dec 2023 15:48:21 +0800 Subject: [PATCH 25/30] =?UTF-8?q?feat(security):=20SecurityUserContextHold?= =?UTF-8?q?er=20=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- admin4j-common-spring-web/pom.xml | 22 +++- .../config/UserContextAutoConfiguration.java | 43 +++++++ .../common/config/UserContextConfig.java | 24 ---- .../impl}/SimpleUserContextHolder.java | 42 ++++--- .../main/resources/META-INF/spring.factories | 2 +- admin4j-dependencies/pom.xml | 2 +- pom.xml | 4 +- .../context/SecurityUserContextHolder.java | 119 +----------------- web-spring-boot-starter/pom.xml | 5 - .../UserContextAutoConfiguration.java | 26 ---- .../main/resources/META-INF/spring.factories | 3 +- 11 files changed, 93 insertions(+), 199 deletions(-) create mode 100644 admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextAutoConfiguration.java delete mode 100644 admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextConfig.java rename {web-spring-boot-starter/src/main/java/com/admin4j/framework/web => admin4j-common-spring-web/src/main/java/com/admin4j/common/service/impl}/SimpleUserContextHolder.java (92%) delete mode 100644 web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java diff --git a/admin4j-common-spring-web/pom.xml b/admin4j-common-spring-web/pom.xml index b072a6a..bbf0a85 100644 --- a/admin4j-common-spring-web/pom.xml +++ b/admin4j-common-spring-web/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 @@ -11,6 +11,7 @@ com.admin4j.common admin4j-common-spring-web + 0.9.3-SNAPSHOT 与业务无关的工具类库 @@ -20,9 +21,16 @@ + + + com.alibaba + transmittable-thread-local + provided + org.springframework.boot spring-boot + provided io.swagger @@ -71,5 +79,15 @@ hibernate-validator provided + + org.springframework.boot + spring-boot-autoconfigure + provided + + + org.springframework.boot + spring-boot-autoconfigure-processor + provided + \ No newline at end of file diff --git a/admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextAutoConfiguration.java b/admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextAutoConfiguration.java new file mode 100644 index 0000000..1a650af --- /dev/null +++ b/admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextAutoConfiguration.java @@ -0,0 +1,43 @@ +package com.admin4j.common.config; + +import com.admin4j.common.constant.WebConstant; +import com.admin4j.common.service.IUserContextHolder; +import com.admin4j.common.service.impl.SimpleUserContextHolder; +import com.admin4j.common.util.UserContextUtil; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author andanyang + * @since 2023/9/15 9:19 + */ +@Configuration +@AutoConfigureOrder(WebConstant.IUserContextHolderOrder + 6) +public class UserContextAutoConfiguration implements InitializingBean, ApplicationContextAware { + + private ApplicationContext applicationContext; + + @Bean + @ConditionalOnMissingBean(IUserContextHolder.class) + @ConditionalOnClass(name = "com.alibaba.ttl.TransmittableThreadLocal") + public IUserContextHolder userContextHolder() { + return new SimpleUserContextHolder(); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void afterPropertiesSet() throws Exception { + UserContextUtil.userContextHolder = applicationContext.getBean(IUserContextHolder.class); + } +} diff --git a/admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextConfig.java b/admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextConfig.java deleted file mode 100644 index d22127a..0000000 --- a/admin4j-common-spring-web/src/main/java/com/admin4j/common/config/UserContextConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.admin4j.common.config; - -import com.admin4j.common.service.IUserContextHolder; -import com.admin4j.common.util.UserContextUtil; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * @author andanyang - * @since 2023/6/7 8:54 - */ -public class UserContextConfig implements InitializingBean { - - - @Autowired(required = false) - IUserContextHolder userContextHolder; - - - @Override - public void afterPropertiesSet() throws Exception { - - UserContextUtil.userContextHolder = userContextHolder; - } -} diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/SimpleUserContextHolder.java b/admin4j-common-spring-web/src/main/java/com/admin4j/common/service/impl/SimpleUserContextHolder.java similarity index 92% rename from web-spring-boot-starter/src/main/java/com/admin4j/framework/web/SimpleUserContextHolder.java rename to admin4j-common-spring-web/src/main/java/com/admin4j/common/service/impl/SimpleUserContextHolder.java index 86e0b52..0d2aa3e 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/SimpleUserContextHolder.java +++ b/admin4j-common-spring-web/src/main/java/com/admin4j/common/service/impl/SimpleUserContextHolder.java @@ -1,4 +1,4 @@ -package com.admin4j.framework.web; +package com.admin4j.common.service.impl; import com.admin4j.common.pojo.AuthenticationUser; import com.admin4j.common.pojo.ResponseEnum; @@ -13,7 +13,6 @@ * @author andanyang * @since 2021/7/27 10:56 */ - public class SimpleUserContextHolder implements IUserContextHolder { /** @@ -29,6 +28,11 @@ public void loginOut() { clear(); } + @Override + public AuthenticationUser getAuthenticationUser() { + return THREAD_LOCAL_USER.get(); + } + /** * 设置登录者信息 * @@ -39,11 +43,6 @@ public void setAuthenticationUser(AuthenticationUser authenticationUser) { THREAD_LOCAL_USER.set(authenticationUser); } - @Override - public AuthenticationUser getAuthenticationUser() { - return THREAD_LOCAL_USER.get(); - } - /** * 获取用户 * @@ -90,6 +89,20 @@ public void offTenant() { setTenantId(0L); } + /** + * get租户 + */ + @Override + public Long getTenantId() { + AuthenticationUser loginUserNoCheck = getLoginUserNoCheck(); + // 小心三目表达式,NPE + if (loginUserNoCheck == null) { + return null; + } else { + return loginUserNoCheck.getTenantId(); + } + } + /** * 设置租户 * @@ -101,12 +114,11 @@ public void setTenantId(Long tenant) { } /** - * get租户 + * get用户ID */ @Override - public Long getTenantId() { - AuthenticationUser loginUserNoCheck = getLoginUserNoCheck(); - return loginUserNoCheck == null ? 0L : loginUserNoCheck.getTenantId(); + public Long getUserId() { + return getLoginUser().getUserId(); } /** @@ -118,12 +130,4 @@ public Long getTenantId() { public void setUserId(Long userId) { getLoginUser().setUserId(userId); } - - /** - * get用户ID - */ - @Override - public Long getUserId() { - return getLoginUser().getUserId(); - } } diff --git a/admin4j-common-spring-web/src/main/resources/META-INF/spring.factories b/admin4j-common-spring-web/src/main/resources/META-INF/spring.factories index 15319b2..ad5cecb 100644 --- a/admin4j-common-spring-web/src/main/resources/META-INF/spring.factories +++ b/admin4j-common-spring-web/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.admin4j.common.config.UserContextConfig + com.admin4j.common.config.UserContextAutoConfiguration diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index ed76042..3c2811c 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -215,7 +215,7 @@ com.admin4j.common admin4j-common-spring-web - ${admin4j.version} + 0.9.3-SNAPSHOT com.admin4j.framework diff --git a/pom.xml b/pom.xml index ccd763d..304e251 100644 --- a/pom.xml +++ b/pom.xml @@ -43,8 +43,8 @@ enum-spring-boot-starter - 0.9.0 - 0.9.2 + 0.9.3-SNAPSHOT + 0.9.3-SNAPSHOT 8 8 UTF-8 diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/context/SecurityUserContextHolder.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/context/SecurityUserContextHolder.java index 54d313f..ec48f8f 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/context/SecurityUserContextHolder.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/context/SecurityUserContextHolder.java @@ -1,10 +1,6 @@ package com.admin4j.framework.security.context; -import com.admin4j.common.pojo.AuthenticationUser; -import com.admin4j.common.pojo.ResponseEnum; -import com.admin4j.common.service.IUserContextHolder; -import com.alibaba.ttl.TransmittableThreadLocal; -import org.apache.commons.lang3.ObjectUtils; +import com.admin4j.common.service.impl.SimpleUserContextHolder; /** * 当前登录用户上下文信息,可实现切换用户,切换租户。 @@ -12,117 +8,6 @@ * @author andanyang * @since 2021/7/27 10:56 */ +public class SecurityUserContextHolder extends SimpleUserContextHolder { -public class SecurityUserContextHolder implements IUserContextHolder { - - /** - * 支持父子线程之间的数据传递 THREAD_LOCAL_TENANT - */ - private final ThreadLocal THREAD_LOCAL_USER = new TransmittableThreadLocal<>(); - - /** - * 当前会话注销登录 - */ - @Override - public void loginOut() { - clear(); - } - - /** - * 设置登录者信息 - * - * @param authenticationUser 认证用户 - */ - @Override - public void setAuthenticationUser(AuthenticationUser authenticationUser) { - THREAD_LOCAL_USER.set(authenticationUser); - } - - @Override - public AuthenticationUser getAuthenticationUser() { - return THREAD_LOCAL_USER.get(); - } - - /** - * 获取用户 - * - * @return String - */ - public AuthenticationUser getLoginUser() { - AuthenticationUser authenticationUser = THREAD_LOCAL_USER.get(); - - ResponseEnum.FAIL_AUTH_FORBIDDEN.notNull(authenticationUser); - - return authenticationUser; - } - - /** - * 获取用户 - * - * @return String - */ - @Override - public boolean isLogin() { - AuthenticationUser authenticationUser = THREAD_LOCAL_USER.get(); - return ObjectUtils.isNotEmpty(authenticationUser); - } - - /** - * 获取用户 - * - * @return String - */ - public AuthenticationUser getLoginUserNoCheck() { - return THREAD_LOCAL_USER.get(); - } - - /** - * 清除LOCAL - */ - @Override - public void clear() { - THREAD_LOCAL_USER.remove(); - } - - @Override - public void offTenant() { - setTenantId(0L); - } - - /** - * 设置租户 - * - * @param tenant - */ - @Override - public void setTenantId(Long tenant) { - getLoginUser().setTenantId(tenant); - } - - /** - * get租户 - */ - @Override - public Long getTenantId() { - AuthenticationUser loginUserNoCheck = getLoginUserNoCheck(); - return loginUserNoCheck == null ? 0L : loginUserNoCheck.getTenantId(); - } - - /** - * 设置用户ID - * - * @param userId - */ - @Override - public void setUserId(Long userId) { - getLoginUser().setUserId(userId); - } - - /** - * get用户ID - */ - @Override - public Long getUserId() { - return getLoginUser().getUserId(); - } } diff --git a/web-spring-boot-starter/pom.xml b/web-spring-boot-starter/pom.xml index 8cb145f..33f343e 100644 --- a/web-spring-boot-starter/pom.xml +++ b/web-spring-boot-starter/pom.xml @@ -15,11 +15,6 @@ 0.9.3-SNAPSHOT - - com.alibaba - transmittable-thread-local - provided - com.admin4j.common admin4j-common diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java deleted file mode 100644 index a845c8a..0000000 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/UserContextAutoConfiguration.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.admin4j.framework.web.autoconfigure; - -import com.admin4j.common.constant.WebConstant; -import com.admin4j.common.service.IUserContextHolder; -import com.admin4j.framework.web.SimpleUserContextHolder; -import org.springframework.boot.autoconfigure.AutoConfigureOrder; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @author andanyang - * @since 2023/9/15 9:19 - */ -@Configuration -@AutoConfigureOrder(WebConstant.IUserContextHolderOrder + 2) -public class UserContextAutoConfiguration { - - @Bean - @ConditionalOnMissingBean(IUserContextHolder.class) - @ConditionalOnClass(name = "com.alibaba.ttl.TransmittableThreadLocal") - public IUserContextHolder userContextHolder() { - return new SimpleUserContextHolder(); - } -} diff --git a/web-spring-boot-starter/src/main/resources/META-INF/spring.factories b/web-spring-boot-starter/src/main/resources/META-INF/spring.factories index b1da646..0619fd8 100644 --- a/web-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/web-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,4 +1,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.admin4j.framework.web.autoconfigure.JacksonAutoConfiguration,\ - com.admin4j.framework.web.autoconfigure.WebAutoConfiguration,\ - com.admin4j.framework.web.autoconfigure.UserContextAutoConfiguration \ No newline at end of file + com.admin4j.framework.web.autoconfigure.WebAutoConfiguration \ No newline at end of file From d15e6b98e74dc6966f627aff7313694f22af88ef Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 19 Dec 2023 16:50:30 +0800 Subject: [PATCH 26/30] =?UTF-8?q?feat(security):=20PermissionAuthorization?= =?UTF-8?q?Manager=20=E6=A0=A1=E9=AA=8C=20http=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authorization/HttpUrlPermission.java | 29 ++++++++++++++ .../authorization/IPermissionUriService.java | 4 +- .../PermissionAuthorizationManager.java | 38 ++++++++++++------- 3 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/HttpUrlPermission.java diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/HttpUrlPermission.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/HttpUrlPermission.java new file mode 100644 index 0000000..9e6bfd3 --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/HttpUrlPermission.java @@ -0,0 +1,29 @@ +package com.admin4j.framework.security.authorization; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.http.HttpMethod; + +/** + * http 请求权限数据 + * + * @author andanyang + * @since 2023/12/19 16:08 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class HttpUrlPermission { + + /** + * http 请求方法; + * 为null 表示不限制请求方法 + */ + private HttpMethod httpMethod; + /** + * http 请求地址 + * 如: /user/1 + */ + private String requestURI; +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java index 8ce5e16..3cfdc96 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java @@ -15,12 +15,12 @@ public interface IPermissionUriService { * * @return */ - List allPermissionUri(); + List allPermissionUrl(); /** * 当前用户拥有的权限 * * @return */ - List getMyPermissionUrls(); + List getMyPermissionUrls(); } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java index 944a94a..99a8ee3 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java @@ -45,13 +45,14 @@ public AuthorizationDecision check(Supplier authentication, Requ // 获取当前请求的 URL 地址 String requestURI = object.getRequest().getRequestURI(); - boolean matchPermission = matchPermission(requestURI); + String method = object.getRequest().getMethod(); + boolean matchPermission = matchPermission(requestURI, method); if (matchPermission) { return GRANTED; } // 沒有匹配到, 查看当前 requestURI 是否需要权限控制 - return urlNeedPermission(requestURI) ? UN_AUTHORIZED : GRANTED; + return urlNeedPermission(requestURI, method) ? UN_AUTHORIZED : GRANTED; } /** @@ -60,11 +61,15 @@ public AuthorizationDecision check(Supplier authentication, Requ * * @return */ - public boolean urlNeedPermission(String requestURI) { - - Collection allPermissionUrls = getAllPermissionUrls(); - for (String url : allPermissionUrls) { - if (antPathMatcher.match(url, requestURI)) { + public boolean urlNeedPermission(String requestURI, String method) { + + Collection allPermissionUrls = getAllPermissionUrls(); + for (HttpUrlPermission urlPermission : allPermissionUrls) { + // method 相同 && 请求路径可以匹配 + if ( + (urlPermission.getHttpMethod() == null || urlPermission.getHttpMethod().name().equalsIgnoreCase(method)) + && + antPathMatcher.match(urlPermission.getRequestURI(), requestURI)) { return true; } } @@ -77,15 +82,20 @@ public boolean urlNeedPermission(String requestURI) { * @param requestURI * @return */ - public boolean matchPermission(String requestURI) { - Collection permissionUrls = getPermissionUrls(); + public boolean matchPermission(String requestURI, String method) { + Collection permissionUrls = getPermissionUrls(); if (permissionUrls == null || permissionUrls.isEmpty()) { return false; } - for (String url : permissionUrls) { - if (antPathMatcher.match(url, requestURI)) { + for (HttpUrlPermission urlPermission : permissionUrls) { + + // method 相同 && 请求路径可以匹配 + if ( + (urlPermission.getHttpMethod() == null || urlPermission.getHttpMethod().name().equalsIgnoreCase(method)) + && + antPathMatcher.match(urlPermission.getRequestURI(), requestURI)) { return true; } } @@ -97,7 +107,7 @@ public boolean matchPermission(String requestURI) { * * @return */ - public Collection getPermissionUrls() { + public Collection getPermissionUrls() { return permissionUriService.getMyPermissionUrls(); } @@ -108,7 +118,7 @@ public Collection getPermissionUrls() { * * @return */ - protected Collection getAllPermissionUrls() { - return permissionUriService.allPermissionUri(); + protected Collection getAllPermissionUrls() { + return permissionUriService.allPermissionUrl(); } } From ce28c3db43080623b90275fec911765d9e98046f Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 19 Dec 2023 17:15:17 +0800 Subject: [PATCH 27/30] =?UTF-8?q?feat(security):=20IPermissionUrlService?= =?UTF-8?q?=20=E6=B7=BB=E5=8A=A0=20ignoreCheck=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...iService.java => IPermissionUrlService.java} | 14 +++++++++++++- .../PermissionAuthorizationManager.java | 17 ++++++++++++++++- .../PermissionAutoConfiguration.java | 6 +++--- 3 files changed, 32 insertions(+), 5 deletions(-) rename security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/{IPermissionUriService.java => IPermissionUrlService.java} (61%) diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUrlService.java similarity index 61% rename from security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java rename to security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUrlService.java index 3cfdc96..26e9863 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUriService.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUrlService.java @@ -8,8 +8,18 @@ * @author andanyang * @since 2023/12/19 14:34 */ -public interface IPermissionUriService { +public interface IPermissionUrlService { + /** + * 是否忽略 检查权限 + * 例如 admin、管理员可以直接忽略检查拥有全部权限 + * + * @return + */ + default boolean ignoreCheck() { + return false; + } + /** * 获取 系统 所有的 PermissionUri * @@ -23,4 +33,6 @@ public interface IPermissionUriService { * @return */ List getMyPermissionUrls(); + + } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java index 99a8ee3..543ac58 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java @@ -29,7 +29,7 @@ public class PermissionAuthorizationManager implements AuthorizationManager authentication, RequestAuthorizationContext object) { + if (ignoreCheck()) { + return GRANTED; + } + // 获取当前请求的 URL 地址 String requestURI = object.getRequest().getRequestURI(); String method = object.getRequest().getMethod(); @@ -55,6 +59,17 @@ public AuthorizationDecision check(Supplier authentication, Requ return urlNeedPermission(requestURI, method) ? UN_AUTHORIZED : GRANTED; } + /** + * 是否忽略 检查权限 + * 例如 admin、管理员可以直接忽略检查拥有全部权限 + * + * @return + */ + protected boolean ignoreCheck() { + + return permissionUriService.ignoreCheck(); + } + /** * url 是否需要授权 * TODO 放在 service 立马 diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java index b0f49f5..38445fa 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/PermissionAutoConfiguration.java @@ -1,6 +1,6 @@ package com.admin4j.framework.security.configuration; -import com.admin4j.framework.security.authorization.IPermissionUriService; +import com.admin4j.framework.security.authorization.IPermissionUrlService; import com.admin4j.framework.security.authorization.PermissionAuthorizationManager; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -10,12 +10,12 @@ * @author andanyang * @since 2023/12/19 14:40 */ -@ConditionalOnBean(IPermissionUriService.class) +@ConditionalOnBean(IPermissionUrlService.class) @ConditionalOnMissingBean(PermissionAuthorizationManager.class) public class PermissionAutoConfiguration { @Bean - public PermissionAuthorizationManager permissionAuthorizationManager(IPermissionUriService permissionUriService) { + public PermissionAuthorizationManager permissionAuthorizationManager(IPermissionUrlService permissionUriService) { return new PermissionAuthorizationManager(permissionUriService); } From 049828dcf4721463e8993a2c7463c9b9cdd73d81 Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Tue, 19 Dec 2023 17:26:50 +0800 Subject: [PATCH 28/30] =?UTF-8?q?feat(security):=20IPermissionUrlService?= =?UTF-8?q?=20=E6=B7=BB=E5=8A=A0=20ignoreCheck=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/security/properties/IgnoringUrlProperties.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java index c172df5..18fe56c 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/properties/IgnoringUrlProperties.java @@ -5,6 +5,7 @@ /** * 根据配置文件 忽略url + * 注意支持 ant-style 风格语法;如果开头没有模糊匹配,请以 / 开头。 * * @author andanyang * @since 2023/3/24 17:00 @@ -15,6 +16,7 @@ public class IgnoringUrlProperties { /** * 包含所有请求类型的路径,不考虑请求方法 + * 注意支持 ant-style 风格语法;如果开头没有模糊匹配,请以 / 开头。 */ private String[] uris; /** @@ -37,6 +39,4 @@ public class IgnoringUrlProperties { * patch 请求 */ private String[] patch; - - } From e6023b6218403cbe52e66b9fb8b81e4b02be8cdd Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Wed, 20 Dec 2023 11:54:33 +0800 Subject: [PATCH 29/30] =?UTF-8?q?feat(security):=20PermissionCode=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/pojo/AuthenticationUser.java | 4 +- .../com/admin4j/common/pojo/ResponseEnum.java | 2 +- admin4j-dependencies/pom.xml | 4 +- pom.xml | 2 +- security-spring-boot-starter/README.md | 180 +++++++++++++++++- .../authorization/IPermissionUrlService.java | 13 +- .../PermissionAuthorizationManager.java | 20 ++ .../PermissionGrantedAuthority.java | 44 +++++ .../configuration/SecurityConfiguration.java | 6 +- .../SecurityHandlerConfiguration.java | 3 +- .../handler/SecurityExceptionHandler.java | 18 +- .../security/jwt/JwtUserDetails.java | 39 ++-- .../security/jwt/TestJwtUserDetails.java | 12 -- .../autoconfigure/WebAutoConfiguration.java | 2 +- 14 files changed, 296 insertions(+), 53 deletions(-) create mode 100644 security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionGrantedAuthority.java diff --git a/admin4j-common-spring-web/src/main/java/com/admin4j/common/pojo/AuthenticationUser.java b/admin4j-common-spring-web/src/main/java/com/admin4j/common/pojo/AuthenticationUser.java index a6e6f83..68cd82e 100644 --- a/admin4j-common-spring-web/src/main/java/com/admin4j/common/pojo/AuthenticationUser.java +++ b/admin4j-common-spring-web/src/main/java/com/admin4j/common/pojo/AuthenticationUser.java @@ -5,7 +5,7 @@ import lombok.Data; import java.io.Serializable; -import java.util.Set; +import java.util.Collection; /** * UserContext 用户上下文 @@ -35,7 +35,7 @@ public class AuthenticationUser implements Serializable { * 权限列表 */ @ApiModelProperty("权限code列表") - private Set permissions; + private Collection permissions; // private String fromService; diff --git a/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java b/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java index e56820b..3e921c1 100644 --- a/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java +++ b/admin4j-common/src/main/java/com/admin4j/common/pojo/ResponseEnum.java @@ -33,7 +33,7 @@ public enum ResponseEnum implements IResponse, Assert { */ FAIL_AUTH(402, "登录失败,账号或者密码错误"), /** - * + * 没有权限 */ FAIL_AUTH_FORBIDDEN(403, "FAIL_AUTH_FORBIDDEN"), diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 3c2811c..9901d44 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -6,7 +6,7 @@ com.admin4j admin4j-dependencies - 0.9.3-SNAPSHOT + 0.9.5-SNAPSHOT pom ${project.artifactId} @@ -64,7 +64,7 @@ 6.7.2 0.1.2 - 0.9.0 + 0.9.5-SNAPSHOT 0.8.2 0.8.0 0.8.0 diff --git a/pom.xml b/pom.xml index 304e251..103530f 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ enum-spring-boot-starter - 0.9.3-SNAPSHOT + 0.9.5-SNAPSHOT 0.9.3-SNAPSHOT 8 8 diff --git a/security-spring-boot-starter/README.md b/security-spring-boot-starter/README.md index 90a0a4a..2ac34f2 100644 --- a/security-spring-boot-starter/README.md +++ b/security-spring-boot-starter/README.md @@ -1,14 +1,19 @@ # admin security -## Features +Spring Security 最佳实践封装。 + +# Features - 多渠道登录 -- 一个注解/一个配置,解决匿名url访问(忽略认证) +- 匿名url访问:一个注解/一个配置,解决匿名url访问(忽略认证) +- 注解式权限 - 基于数据库的动态权限 -## USAGES +# USAGES + +## 1. 基础使用 -1. 引入 pom +### 1.1 引入 pom ``` @@ -18,7 +23,7 @@ ``` -2. 实现 JwtUserDetailsService 接口,用于根据用户ID获取用户详情。由于我们的JWT Token 存的是 userId,所以这里的入参为userId +### 1.2 实现 JwtUserDetailsService 接口,用于根据用户ID获取用户详情。由于我们的JWT Token 存的是 userId,所以这里的入参为userId ```java @@ -36,7 +41,7 @@ public class Admin4jJwtUserDetailsService implements JwtUserDetailsService { } ``` -3. 账号密码登录。需要实现 `Spring Security`的`UserDetailsService` 接口,用于根据 username 查询用户详情 +### 1.3 账号密码登录。需要实现 `Spring Security`的`UserDetailsService` 接口,用于根据 username 查询用户详情 ```java @@ -63,7 +68,7 @@ public class Admin4jJwtUserDetailsService implements JwtUserDetailsService, User ``` -### 测试 +### 1.4 测试 - 登录接口 @@ -86,11 +91,166 @@ curl --location 'http://localhost:8080/login' \ } ``` -## 多渠道登录 +## 2. 匿名url访问 + +### 2.1 注解式 + +> `@AnonymousAccess`需要放在 `controller`方法上 + +``` +public class UserProfileController { + + @GetMapping("1") + @ApiModelProperty("注解式匿名url访问") + @AnonymousAccess + public R get() { + return R.ok("1"); + } +``` + +### 2.2 yml配置式 + +> 支持HttpMethod(get,post,put,delete)配置;uris下面表示所有HttpMethod + +``` +admin4j: + security: + ignoring: + uris: + - "/login/sendPhoneCode" + - "/profile/**" + get: + - "/profile/3" +``` + +## 3. 注解式权限 + +### 3.1 开启方法注解式权限 + +``` +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class AdminServerApplication { +} +``` + +### 3.2 使用 + +``` + @GetMapping("4") + @PreAuthorize("hasAuthority('profile')") + public R get4() { + return R.ok("4"); + } + + @GetMapping("5") + @PreAuthorize("hasAuthority('menus')") + public R get5() { + return R.ok("5"); + } +``` + +### 3.3 `@PreAuthorize` 支持el表达式 + +#### 3.3.1. returnObject 保留名 + +对于 @PostAuthorize 和 @PostFilter 注解, 可以在表达式中使用 returnObject 保留名, returnObject 代表着被注解方法的返回值, +我们可以使用 returnObject 保留名对注解方法的结果进行验证. +比如: + +```java + +@PostAuthorize("returnObject.owner == authentication.name") +public Book getBook(); +12 +``` + +#### 3.3.2. 表达式中的 # 号 + +在表达式中, 可以使用 #argument123 的形式来代表注解方法中的参数 argument123. +比如: + +```java + +@PreAuthorize("#book.owner == authentication.name") +public void deleteBook(Book book); +12 +``` + +还有一种 #argument123 的写法, 即使用 Spring Security @P注解来为方法参数起别名, 然后在 @PreAuthorize 等注解表达式中使用该别名. +不推荐这种写法, 代码可读性较差. + +```java + +@PreAuthorize("#c.name == authentication.name") +public void doSomething(@P("c") Contact contact); +``` + +#### 3.3.3 内置表达式有: + +| 表达式 | 备注 | +|--------------------------------------------------------------------|----------------------------------------| +| hasRole([role]) | 如果有当前角色, 则返回 true(会自动加上 ROLE_ 前缀) | +| hasAnyRole([role1, role2]) | 如果有任一角色即可通过校验, 返回true,(会自动加上 ROLE_ 前缀) | +| hasAuthority([authority]) | 如果有指定权限, 则返回 true | +| hasAnyAuthority([authority1, authority2]) | 如果有任一指定权限, 则返回true | +| principal | 获取当前用户的 principal 主体对象 | +| authentication | 获取当前用户的 authentication 对象, | +| permitAll | 总是返回 true, 表示全部允许 | +| denyAll | 总是返回 false, 代表全部拒绝 | +| isAnonymous() | 如果是匿名访问, 返回true | +| isRememberMe() | 如果是remember-me 自动认证, 则返回 true | +| isAuthenticated() | 如果不是匿名访问, 则返回true | +| isFullAuthenticated() | 如果不是匿名访问或remember-me认证登陆, 则返回true | +| hasPermission(Object target, Object permission) | | +| hasPermission(Object target, String targetType, Object permission) | | + +## 4. 基于数据库的动态权限 + +实现 接口`IPermissionUrlService` + +``` +public interface IPermissionUrlService { + + /** + * 是否忽略 检查权限 + * 例如 admin、管理员可以直接忽略检查拥有全部权限 + * + * @return + */ + default boolean ignoreCheck() { + return false; + } + + /** + * 是否允许匿名访问 + * + * @return + */ + default boolean canAnonymousAccess() { + return false; + } + + /** + * 获取系统所有需要授权的 PermissionUri + * + * @return + */ + List allPermissionUrl(); + + /** + * 当前用户拥有的权限 + * + * @return + */ + List getMyPermissionUrls(); +} +``` + +## 5.多渠道登录 通过配置的方式,支持微信,手机号等多渠道登录 -### 验证码手机号登录 +### 5.1 验证码手机号登录 - yaml配置方式 @@ -127,7 +287,6 @@ curl --location 'http://localhost:8080/login/phone' \ 3. 根据手机号获取用户详情 ```java - /** * 验证码手机号登录 * @@ -162,3 +321,4 @@ public class PhoneMultiUserDetailsService implements MultiUserDetailsService { ``` ### 其他渠道登录,如微信openid 登录,参考上方可实现 + diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUrlService.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUrlService.java index 26e9863..fa1458b 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUrlService.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/IPermissionUrlService.java @@ -19,9 +19,18 @@ public interface IPermissionUrlService { default boolean ignoreCheck() { return false; } - + + /** + * 是否允许匿名访问 + * + * @return + */ + default boolean canAnonymousAccess() { + return false; + } + /** - * 获取 系统 所有的 PermissionUri + * 获取系统所有需要授权的 PermissionUri * * @return */ diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java index 543ac58..32af2ce 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionAuthorizationManager.java @@ -1,6 +1,7 @@ package com.admin4j.framework.security.authorization; import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; @@ -43,6 +44,15 @@ public class PermissionAuthorizationManager implements AuthorizationManager authentication, RequestAuthorizationContext object) { + // 是否允许匿名访问 + if (!canAnonymousAccess()) { + Authentication authenticationGet = authentication.get(); + if (authenticationGet instanceof AnonymousAuthenticationToken) { + // 匿名访问 + return UN_AUTHORIZED; + } + } + if (ignoreCheck()) { return GRANTED; } @@ -59,6 +69,16 @@ public AuthorizationDecision check(Supplier authentication, Requ return urlNeedPermission(requestURI, method) ? UN_AUTHORIZED : GRANTED; } + /** + * 是否允许匿名访问。 + * 默认不允许。 + * + * @return + */ + protected boolean canAnonymousAccess() { + return permissionUriService.canAnonymousAccess(); + } + /** * 是否忽略 检查权限 * 例如 admin、管理员可以直接忽略检查拥有全部权限 diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionGrantedAuthority.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionGrantedAuthority.java new file mode 100644 index 0000000..af25bd1 --- /dev/null +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/authorization/PermissionGrantedAuthority.java @@ -0,0 +1,44 @@ +package com.admin4j.framework.security.authorization; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.core.GrantedAuthority; + +/** + * 权限字符串code + * + * @author andanyang + * @since 2023/12/20 10:29 + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PermissionGrantedAuthority implements GrantedAuthority { + + private static final long serialVersionUID = -2619854289135953331L; + // 权限字符串code + private String permission; + + /** + * If the GrantedAuthority can be represented as a String + * and that String is sufficient in precision to be relied upon for an + * access control decision by an {@link AccessDecisionManager} (or delegate), this + * method should return such a String. + *

+ * If the GrantedAuthority cannot be expressed with sufficient precision + * as a String, null should be returned. Returning + * null will require an AccessDecisionManager (or delegate) + * to specifically support the GrantedAuthority implementation, so + * returning null should be avoided unless actually required. + * + * @return a representation of the granted authority (or null if the + * granted authority cannot be expressed as a String with sufficient + * precision). + */ + @Override + public String getAuthority() { + return permission; + } +} diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java index d9bb4b5..642ae8c 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityConfiguration.java @@ -16,7 +16,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; @@ -43,11 +42,12 @@ */ /** - * 开启方法级别的注解支持 + * spring security 配置。 * * @author andanyang + * @EnableGlobalMethodSecurity 有应用出自己开启 */ -@EnableGlobalMethodSecurity(prePostEnabled = true) +// @EnableGlobalMethodSecurity(prePostEnabled = true) @EnableConfigurationProperties({IgnoringUrlProperties.class, JwtProperties.class, FormLoginProperties.class, MultiAuthenticationProperties.class}) @AutoConfigureBefore(UserDetailsServiceAutoConfiguration.class) public class SecurityConfiguration { diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java index 8734f9c..e34b926 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/configuration/SecurityHandlerConfiguration.java @@ -3,7 +3,6 @@ import com.admin4j.framework.security.AuthenticationHandler; import com.admin4j.framework.security.UserTokenService; import com.admin4j.framework.security.handler.*; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.security.web.AuthenticationEntryPoint; @@ -51,7 +50,7 @@ public LogoutSuccessHandler logoutSuccessHandler(AuthenticationHandler authentic } @Bean - @ConditionalOnClass(name = {"io.jsonwebtoken.SignatureException"}) + // @ConditionalOnClass(name = {"io.jsonwebtoken.SignatureException"}) public SecurityExceptionHandler securityExceptionHandler() { return new SecurityExceptionHandler(); } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/SecurityExceptionHandler.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/SecurityExceptionHandler.java index 73220d9..5a8aa7c 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/SecurityExceptionHandler.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/handler/SecurityExceptionHandler.java @@ -1,8 +1,14 @@ package com.admin4j.framework.security.handler; import com.admin4j.common.exception.handler.AbstractExceptionHandler; +import com.admin4j.common.pojo.IResponse; +import com.admin4j.common.pojo.ResponseEnum; +import com.admin4j.common.pojo.SimpleResponse; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; /** * @author andanyang @@ -12,11 +18,11 @@ @ControllerAdvice public class SecurityExceptionHandler extends AbstractExceptionHandler { -// @ExceptionHandler(SignatureException.class) -// @Deprecated -// public ResponseEntity handleException(Exception e) { -// log.error("SignatureException:" + e.getMessage(), e); -// return renderException(e, SimpleResponse.of(ResponseEnum.FAIL_AUTH_TOKEN_ERROR.getCode(), e.getMessage())); -// } + @ExceptionHandler(AccessDeniedException.class) + @Deprecated + public ResponseEntity handleException(Exception e) { + log.error("AccessDeniedException:" + e.getMessage(), e); + return renderException(e, SimpleResponse.of(ResponseEnum.FAIL_AUTH_FORBIDDEN.getCode(), e.getMessage())); + } } diff --git a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/jwt/JwtUserDetails.java b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/jwt/JwtUserDetails.java index 9006ed4..9a73a8d 100644 --- a/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/jwt/JwtUserDetails.java +++ b/security-spring-boot-starter/src/main/java/com/admin4j/framework/security/jwt/JwtUserDetails.java @@ -1,8 +1,15 @@ package com.admin4j.framework.security.jwt; +import com.admin4j.framework.security.authorization.PermissionGrantedAuthority; +import com.alibaba.fastjson2.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -import java.util.Set; +import java.util.Collection; +import java.util.Collections; +import java.util.stream.Collectors; /** * @author andanyang @@ -11,14 +18,14 @@ public interface JwtUserDetails extends UserDetails { - //private static final long serialVersionUID = -5943535608623539244L; - //private String password; - //private String username; - //private Set authorities; - //private boolean accountNonExpired = true; - //private boolean accountNonLocked = true; - //private boolean credentialsNonExpired = true; - //private boolean enabled = true; + // private static final long serialVersionUID = -5943535608623539244L; + // private String password; + // private String username; + // private Set authorities; + // private boolean accountNonExpired = true; + // private boolean accountNonLocked = true; + // private boolean credentialsNonExpired = true; + // private boolean enabled = true; /** * jwt 盐 @@ -44,11 +51,21 @@ default Long getTenantId() { /** * 登录方式 */ - String getAuthType(); + default String getAuthType() { + return ""; + } /** * 权限列表 */ - Set getPermissions(); + Collection getPermissions(); + @JsonIgnore + @JSONField(serialize = false) + default Collection getAuthorities() { + if (ObjectUtils.isEmpty(getPermissions())) { + return Collections.emptySet(); + } + return getPermissions().stream().map(PermissionGrantedAuthority::new).collect(Collectors.toSet()); + } } diff --git a/security-spring-boot-starter/src/test/java/com/admin4j/framework/security/jwt/TestJwtUserDetails.java b/security-spring-boot-starter/src/test/java/com/admin4j/framework/security/jwt/TestJwtUserDetails.java index 9210063..8974a27 100644 --- a/security-spring-boot-starter/src/test/java/com/admin4j/framework/security/jwt/TestJwtUserDetails.java +++ b/security-spring-boot-starter/src/test/java/com/admin4j/framework/security/jwt/TestJwtUserDetails.java @@ -1,9 +1,7 @@ package com.admin4j.framework.security.jwt; import lombok.Data; -import org.springframework.security.core.GrantedAuthority; -import java.util.Collection; import java.util.Set; /** @@ -33,16 +31,6 @@ public Set getPermissions() { } - /** - * Returns the authorities granted to the user. Cannot return null. - * - * @return the authorities, sorted by natural key (never null) - */ - @Override - public Collection getAuthorities() { - return null; - } - /** * Returns the password used to authenticate the user. * diff --git a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java index 1c3e74a..908aa35 100644 --- a/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java +++ b/web-spring-boot-starter/src/main/java/com/admin4j/framework/web/autoconfigure/WebAutoConfiguration.java @@ -46,7 +46,7 @@ public Admin4jErrorController basicErrorController(ErrorAttributes errorAttribut * * @return */ - @Bean + @Bean("admin4jGlobalExceptionHandler") public GlobalExceptionHandler globalExceptionHandler() { return new GlobalExceptionHandler(); } From 98a83a23e404cf977d1acb30d2b5018480fc6e2d Mon Sep 17 00:00:00 2001 From: andanyang <1218853253@qq.com> Date: Wed, 20 Dec 2023 14:23:46 +0800 Subject: [PATCH 30/30] feat(version): update version 0.9.5 --- admin4j-common-spring-web/pom.xml | 2 +- admin4j-dependencies/pom.xml | 32 +++++++++++++------------- admin4j-parent/pom.xml | 4 ++-- excel-spring-boot-starter/pom.xml | 4 ++-- mybatis-plus-boot-starter/pom.xml | 2 +- pom.xml | 4 ++-- prometheus-spring-boot-starter/pom.xml | 3 ++- security-spring-boot-starter/README.md | 13 ++++++----- security-spring-boot-starter/pom.xml | 2 +- tenant-spring-boot-starter/pom.xml | 2 +- test-spring-boot-starter/pom.xml | 2 +- ttl-spring-boot-starter/pom.xml | 1 + web-spring-boot-starter/pom.xml | 2 +- 13 files changed, 38 insertions(+), 35 deletions(-) diff --git a/admin4j-common-spring-web/pom.xml b/admin4j-common-spring-web/pom.xml index bbf0a85..d9f85c7 100644 --- a/admin4j-common-spring-web/pom.xml +++ b/admin4j-common-spring-web/pom.xml @@ -11,7 +11,7 @@ com.admin4j.common admin4j-common-spring-web - 0.9.3-SNAPSHOT + 0.9.5 与业务无关的工具类库 diff --git a/admin4j-dependencies/pom.xml b/admin4j-dependencies/pom.xml index 9901d44..83fcdea 100644 --- a/admin4j-dependencies/pom.xml +++ b/admin4j-dependencies/pom.xml @@ -6,7 +6,7 @@ com.admin4j admin4j-dependencies - 0.9.5-SNAPSHOT + 0.9.5 pom ${project.artifactId} @@ -64,7 +64,7 @@ 6.7.2 0.1.2 - 0.9.5-SNAPSHOT + 0.9.5 0.8.2 0.8.0 0.8.0 @@ -125,42 +125,42 @@ com.admin4j.framework tenant-spring-boot-starter - 0.9.3-SNAPSHOT + 0.9.5 com.admin4j.framework ttl-spring-boot-starter - ${admin4j.version} + 0.9.0 com.admin4j.framework excel-spring-boot-starter - ${admin4j.version} + 0.9.0 com.admin4j.framework log-spring-boot-starter - ${admin4j.version} + 0.9.0 com.admin4j.framework test-spring-boot-starter - ${admin4j.version} + 0.9.0 com.admin4j.framework desensitize-spring-boot-starter - ${admin4j.version} + 0.9.0 com.admin4j.framework security-spring-boot-starter - 0.9.4-SNAPSHOT + 0.9.5 com.admin4j.framework mybatis-plus-boot-starter - 0.9.3-SNAPSHOT + 0.9.5 com.admin4j.framework @@ -215,12 +215,12 @@ com.admin4j.common admin4j-common-spring-web - 0.9.3-SNAPSHOT + 0.9.5 com.admin4j.framework web-spring-boot-starter - 0.9.3-SNAPSHOT + 0.9.5 com.admin4j.framework @@ -230,12 +230,12 @@ com.admin4j.framework prometheus-spring-boot-starter - ${admin4j.version} + 0.9.5 com.admin4j.framework excel-spring-boot-starter - ${admin4j.version} + 0.9.0 com.admin4j @@ -356,7 +356,7 @@ com.alibaba.fastjson2 fastjson2 - 2.0.29 + 2.0.43 @@ -375,7 +375,7 @@ com.github.pagehelper pagehelper-spring-boot-starter - 2.0.0 + 2.1.0 diff --git a/admin4j-parent/pom.xml b/admin4j-parent/pom.xml index ff170c8..55f1299 100644 --- a/admin4j-parent/pom.xml +++ b/admin4j-parent/pom.xml @@ -6,13 +6,13 @@ com.admin4j admin4j-parent - 0.9.2 + 0.9.5 用于业务框架的父工程 pom admin4j-parent https://github.com/admin4j/admin4j-framework - 0.9.2 + 0.9.5 8 8 UTF-8 diff --git a/excel-spring-boot-starter/pom.xml b/excel-spring-boot-starter/pom.xml index 08a4068..1f8f549 100644 --- a/excel-spring-boot-starter/pom.xml +++ b/excel-spring-boot-starter/pom.xml @@ -1,4 +1,4 @@ - 4.0.0 @@ -9,7 +9,7 @@ com.admin4j.framework excel-spring-boot-starter - + 0.9.0 jar excel-spring-boot-starter diff --git a/mybatis-plus-boot-starter/pom.xml b/mybatis-plus-boot-starter/pom.xml index 0b86dfd..f580ead 100644 --- a/mybatis-plus-boot-starter/pom.xml +++ b/mybatis-plus-boot-starter/pom.xml @@ -12,7 +12,7 @@ jar mybatis-plus-boot-starter - 0.9.3-SNAPSHOT + 0.9.5 diff --git a/pom.xml b/pom.xml index 103530f..8e1fec0 100644 --- a/pom.xml +++ b/pom.xml @@ -43,8 +43,8 @@ enum-spring-boot-starter - 0.9.5-SNAPSHOT - 0.9.3-SNAPSHOT + 0.9.5 + 0.9.5 8 8 UTF-8 diff --git a/prometheus-spring-boot-starter/pom.xml b/prometheus-spring-boot-starter/pom.xml index e1298fe..2135903 100644 --- a/prometheus-spring-boot-starter/pom.xml +++ b/prometheus-spring-boot-starter/pom.xml @@ -1,4 +1,4 @@ - 4.0.0 @@ -9,6 +9,7 @@ com.admin4j.framework prometheus-spring-boot-starter + 0.9.5 jar prometheus-spring-boot-starter diff --git a/security-spring-boot-starter/README.md b/security-spring-boot-starter/README.md index 2ac34f2..6b7c10d 100644 --- a/security-spring-boot-starter/README.md +++ b/security-spring-boot-starter/README.md @@ -15,12 +15,13 @@ Spring Security 最佳实践封装。 ### 1.1 引入 pom -``` - - com.admin4j.framework - security-spring-boot-starter - 0.9.0 - +```xml + + + com.admin4j.framework + security-spring-boot-starter + 0.9.0 + ``` ### 1.2 实现 JwtUserDetailsService 接口,用于根据用户ID获取用户详情。由于我们的JWT Token 存的是 userId,所以这里的入参为userId diff --git a/security-spring-boot-starter/pom.xml b/security-spring-boot-starter/pom.xml index 142144f..a5429cf 100644 --- a/security-spring-boot-starter/pom.xml +++ b/security-spring-boot-starter/pom.xml @@ -10,7 +10,7 @@ com.admin4j.framework security-spring-boot-starter jar - 0.9.5-SNAPSHOT + 0.9.5 security-spring-boot-starter diff --git a/tenant-spring-boot-starter/pom.xml b/tenant-spring-boot-starter/pom.xml index 114a7d0..d073cf5 100644 --- a/tenant-spring-boot-starter/pom.xml +++ b/tenant-spring-boot-starter/pom.xml @@ -9,7 +9,7 @@ com.admin4j.framework tenant-spring-boot-starter - 0.9.3-SNAPSHOT + 0.9.5 tenant-spring-boot-starter 多各大中间件的,多租户能力的适配 diff --git a/test-spring-boot-starter/pom.xml b/test-spring-boot-starter/pom.xml index 2616af7..5e68001 100644 --- a/test-spring-boot-starter/pom.xml +++ b/test-spring-boot-starter/pom.xml @@ -1,4 +1,4 @@ - 4.0.0 diff --git a/ttl-spring-boot-starter/pom.xml b/ttl-spring-boot-starter/pom.xml index 3d5bb33..7e70ae0 100644 --- a/ttl-spring-boot-starter/pom.xml +++ b/ttl-spring-boot-starter/pom.xml @@ -9,6 +9,7 @@ com.admin4j.framework ttl-spring-boot-starter + 0.9.0 jar ttl-spring-boot-starter diff --git a/web-spring-boot-starter/pom.xml b/web-spring-boot-starter/pom.xml index 33f343e..258faa0 100644 --- a/web-spring-boot-starter/pom.xml +++ b/web-spring-boot-starter/pom.xml @@ -12,7 +12,7 @@ com.admin4j.framework web-spring-boot-starter ${project.artifactId} - 0.9.3-SNAPSHOT + 0.9.5