From 10196c65582ad988bad1d98ec9a2e0f2612b5b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 3 Mar 2020 10:21:43 +0800 Subject: [PATCH 001/228] =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E7=9A=84=E5=88=9D=E5=A7=8B=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 80 ++++++++ seqdata-cloud-authc/pom.xml | 40 ++++ .../cn/seqdata/oauth2/GlobalCorsConfig.java | 27 +++ .../oauth2/MethodSecurityConfiguration.java | 71 +++++++ .../seqdata/oauth2/ResServerApplication.java | 20 ++ .../oauth2/ResourceServerConfiguration.java | 62 ++++++ .../seqdata/oauth2/SwaggerConfiguration.java | 74 +++++++ .../oauth2/WebSecurityConfiguration.java | 33 ++++ .../oauth2/controller/UserInfoController.java | 33 ++++ .../security/EnhancePrincipalExtractor.java | 25 +++ .../security/TestPermissionEvaluator.java | 32 +++ .../src/main/resources/application.yml | 16 ++ .../src/main/resources/bootstrap.yml | 9 + seqdata-cloud-authz/pom.xml | 51 +++++ .../oauth2/AuthcServerConfiguration.java | 30 +++ .../oauth2/AuthzServerApplication.java | 18 ++ .../oauth2/AuthzServerConfiguration.java | 183 ++++++++++++++++++ .../oauth2/JsonSerializationStrategy.java | 73 +++++++ .../oauth2/WebSecurityConfiguration.java | 40 ++++ .../oauth2/controller/ConnectController.java | 65 +++++++ .../controller/OAuth2CodeController.java | 92 +++++++++ .../oauth2/controller/UserInfoController.java | 63 ++++++ .../oauth2/github/GithubUserProfile.java | 31 +++ .../MobileNonceAuthenticationFilter.java | 61 ++++++ .../MobileNonceAuthenticationProvider.java | 36 ++++ .../MobileNonceAuthenticationToken.java | 56 ++++++ .../oauth2/mobile/MobileNonceService.java | 11 ++ .../oauth2/mobile/MobileTokenGranter.java | 52 +++++ .../DefaultRestTemplateCustomizer.java | 26 +++ .../oauth2/provider/OAuth2Operations.java | 34 ++++ .../oauth2/provider/OAuth2Provider.java | 100 ++++++++++ .../oauth2/provider/OAuth2Template.java | 141 ++++++++++++++ .../seqdata/oauth2/provider/UserProfile.java | 92 +++++++++ .../seqdata/oauth2/service/UserService.java | 136 +++++++++++++ .../cn/seqdata/oauth2/util/SecurityUtils.java | 51 +++++ ...zationCodeGrantRequestEntityConverter.java | 44 +++++ .../oauth2/wechat/WechatOAuth2Template.java | 58 ++++++ .../wechat/WechatRestTemplateCustomizer.java | 38 ++++ .../wechat/WechatTokenResponseConverter.java | 30 +++ .../oauth2/wechat/WechatUserProfile.java | 32 +++ .../wechat/WechatUserRequestConverter.java | 35 ++++ .../src/main/resources/application.yml | 39 ++++ .../src/main/resources/bootstrap.yml | 9 + seqdata-cloud-gateway/pom.xml | 47 +++++ .../gateway/AuthClientConfiguration.java | 63 ++++++ .../seqdata/gateway/GatewayApplication.java | 17 ++ .../seqdata/gateway/GatewayConfiguration.java | 47 +++++ .../seqdata/gateway/JacksonConfiguration.java | 57 ++++++ .../gateway/filter/BearerTokenResolver.java | 11 ++ .../CachedResourceServerTokenServices.java | 51 +++++ .../filter/DefaultBearerTokenResolver.java | 38 ++++ .../filter/ResourceServerProperties.java | 18 ++ .../filter/authc/PathMatcherGlobalFilter.java | 77 ++++++++ .../filter/authc/WhitelistPredicate.java | 53 +++++ .../filter/logging/LoggingGlobalFilter.java | 106 ++++++++++ .../filter/verify/HmacVerifyGlobalFilter.java | 110 +++++++++++ .../filter/verify/ReplayDigestFunction.java | 49 +++++ .../gateway/nacos/NacosConfiguration.java | 18 ++ .../nacos/NacosReactiveDiscoveryClient.java | 55 ++++++ .../swagger/OAuth2ClientProperties.java | 23 +++ .../gateway/swagger/SwaggerController.java | 19 ++ .../gateway/swagger/SwaggerHandler.java | 64 ++++++ .../gateway/swagger/SwaggerProvider.java | 88 +++++++++ .../seqdata/gateway/util/GatewayAssert.java | 35 ++++ .../seqdata/gateway/util/MultiMapUtils.java | 38 ++++ .../src/main/resources/application.yml | 15 ++ .../src/main/resources/bootstrap.yml | 15 ++ 67 files changed, 3363 insertions(+) create mode 100644 pom.xml create mode 100644 seqdata-cloud-authc/pom.xml create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/TestPermissionEvaluator.java create mode 100644 seqdata-cloud-authc/src/main/resources/application.yml create mode 100644 seqdata-cloud-authc/src/main/resources/bootstrap.yml create mode 100644 seqdata-cloud-authz/pom.xml create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/JsonSerializationStrategy.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/github/GithubUserProfile.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/DefaultRestTemplateCustomizer.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Operations.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Template.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatOAuth2Template.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatTokenResponseConverter.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserRequestConverter.java create mode 100644 seqdata-cloud-authz/src/main/resources/application.yml create mode 100644 seqdata-cloud-authz/src/main/resources/bootstrap.yml create mode 100644 seqdata-cloud-gateway/pom.xml create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/BearerTokenResolver.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/DefaultBearerTokenResolver.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/ResourceServerProperties.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/ReplayDigestFunction.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/OAuth2ClientProperties.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerController.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerHandler.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/GatewayAssert.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/MultiMapUtils.java create mode 100644 seqdata-cloud-gateway/src/main/resources/application.yml create mode 100644 seqdata-cloud-gateway/src/main/resources/bootstrap.yml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..536badf --- /dev/null +++ b/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.1.RELEASE + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + pom + + 1.8 + Hoxton.SR1 + 2.9.2 + 2.1.1.RELEASE + + + seqdata-cloud-gateway + seqdata-cloud-authz + seqdata-cloud-authc + + + + org.projectlombok + lombok + + compile + true + + + org.apache.commons + commons-lang3 + 3.9 + + + com.google.guava + guava + 21.0 + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + ${nacos.version} + + + + io.springfox + springfox-swagger2 + ${swagger.version} + + + io.springfox + springfox-swagger-ui + ${swagger.version} + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml new file mode 100644 index 0000000..43baf1a --- /dev/null +++ b/seqdata-cloud-authc/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-authc + 资源服务器,所有的业务模块应该继承自资源服务器,自动和授权服务器交互实现认证和授权 + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.security + spring-security-oauth2-resource-server + + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + + + cn.seqdata.cloud + seqdata-cloud-auth-client + ${project.version} + + + diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java new file mode 100644 index 0000000..6a347da --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java @@ -0,0 +1,27 @@ +package cn.seqdata.oauth2; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +/** + * Author: jrxian + * Date: 2020-02-04 12:53 + */ +@Configuration +public class GlobalCorsConfig { + + @Bean + public CorsFilter corsFilter() { + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.setAllowCredentials(true); + corsConfiguration.addAllowedOrigin("*"); + corsConfiguration.addAllowedHeader("*"); + corsConfiguration.addAllowedMethod("*"); + UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); + urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); + return new CorsFilter(urlBasedCorsConfigurationSource); + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java new file mode 100644 index 0000000..649a0cc --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java @@ -0,0 +1,71 @@ +package cn.seqdata.oauth2; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.access.AccessDecisionVoter; +import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory; +import org.springframework.security.access.method.MethodSecurityMetadataSource; +import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource; +import org.springframework.security.access.vote.AffirmativeBased; +import org.springframework.security.access.vote.UnanimousBased; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; + +import cn.seqdata.oauth2.client.security.*; + +/** + * Author: jrxian + * Date: 2020-02-15 23:50 + */ +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration { + + @Bean + @Primary + public MethodSecurityMetadataSource methodSecurityMetadataSource() { + ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(getExpressionHandler()); + PrePostAnnotationSecurityMetadataSource prePostSource = new PrePostAnnotationSecurityMetadataSource(attributeFactory); + + MethodSecurityMetadataSource customSource = new ControllerMethodSecurityMetadataSource(); + + //对此类的大幅改造主要是由于原生的DelegatingMethodSecurityMetadataSource无法同时支持多个注解的bug导致 + return new DelegatingMethodSecurityMetadataSource(Arrays.asList(prePostSource, customSource)); + } + + @Bean + public RowsPermissionEvaluator rowsPermissionEvaluator(List evaluators) { + return new RowsPermissionEvaluator(evaluators); + } + + @Bean + public PathPermissionEvaluator pathPermissionEvaluator() { + return new PathPermissionEvaluatorImpl("repo"); + } + + @Bean + public ControllerMethodVoter controllerMethodVoter(PathPermissionEvaluator permissionEvaluator) { + return new ControllerMethodVoter(permissionEvaluator); + } + + @Autowired + private ControllerMethodVoter controllerMethodVoter; + + @Override + protected AccessDecisionManager accessDecisionManager() { + AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager(); + + //增加@RestController带有@RequestMapping注解的Method的拦截器 + List> decisionVoters = accessDecisionManager.getDecisionVoters(); + decisionVoters.add(controllerMethodVoter); + + //必须全票通过 + return new UnanimousBased(decisionVoters); + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java new file mode 100644 index 0000000..b82082e --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java @@ -0,0 +1,20 @@ +package cn.seqdata.oauth2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; + +import cn.seqdata.oauth2.client.FeignAuthzClient; + +/** + * Author: jrxian + * Date: 2020-01-20 00:37 + */ +@SpringBootApplication +@EnableFeignClients(basePackageClasses = FeignAuthzClient.class) +public class ResServerApplication { + + public static void main(String[] args) { + new SpringApplication(ResServerApplication.class).run(args); + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java new file mode 100644 index 0000000..13db4e1 --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java @@ -0,0 +1,62 @@ +package cn.seqdata.oauth2; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; +import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.RemoteTokenServices; +import org.springframework.security.web.authentication.HttpStatusEntryPoint; + +import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; + +/** + * Author: jrxian + * Date: 2019-09-29 23:09 + */ +@Configuration +@EnableResourceServer +@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) +public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { + + @Bean + public RemoteTokenServices remoteTokenServices(OAuth2ClientProperties credentials, ResourceServerProperties resource) { + EnhanceUserAuthenticationConverter authenticationConverter = new EnhanceUserAuthenticationConverter(); + authenticationConverter.setDefaultAuthorities(new String[]{"guest"}); + + DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); + tokenConverter.setUserTokenConverter(authenticationConverter); + + RemoteTokenServices services = new RemoteTokenServices(); + services.setAccessTokenConverter(tokenConverter); + + services.setClientId(credentials.getClientId()); + services.setClientSecret(credentials.getClientSecret()); + + services.setCheckTokenEndpointUrl(resource.getTokenInfoUri()); + + return services; + } + + @Override + public void configure(HttpSecurity http) throws Exception { + http.csrf(); + + http.authorizeRequests() + .antMatchers("/v2/api-docs/**", "/swagger-resources/**") + .permitAll(); + + http.authorizeRequests() + .antMatchers("/**") + .authenticated(); + + http.exceptionHandling() + .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java new file mode 100644 index 0000000..900563a --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java @@ -0,0 +1,74 @@ +package cn.seqdata.oauth2; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import lombok.AllArgsConstructor; +import springfox.documentation.builders.ParameterBuilder; +import springfox.documentation.schema.ModelRef; +import springfox.documentation.service.*; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Author: jrxian + * Date: 2020-02-04 01:36 + */ +@Configuration +@EnableSwagger2 +@AllArgsConstructor +public class SwaggerConfiguration { + private final ResourceServerProperties resourceServerProperties; + + @Bean + public Docket apiDocker() { + return new Docket(DocumentationType.SWAGGER_2) +// .securitySchemes(Collections.singletonList(password())) + .globalOperationParameters(operationParameters()); + } + + private SecurityScheme password() { + return new OAuth("password", scopes(), grantTypes()); + } + + private List scopes() { + List scopes = new ArrayList<>(); + + scopes.add(new AuthorizationScope("dat", "数据")); + scopes.add(new AuthorizationScope("eff", "能效")); + scopes.add(new AuthorizationScope("ops", "运维")); + scopes.add(new AuthorizationScope("trd", "售电")); + scopes.add(new AuthorizationScope("sys", "后台")); + + return scopes; + } + + private List grantTypes() { + List grantTypes = new ArrayList<>(); + + String tokenInfoUri = resourceServerProperties.getTokenInfoUri(); + String tokenUrl = StringUtils.replace(tokenInfoUri, "/check_token", "/token"); + grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)); + + return grantTypes; + } + + private List operationParameters() { + List operationParameters = new ArrayList<>(); + + operationParameters.add(new ParameterBuilder() + .name("Authorization") + .required(false) + .parameterType("header") + .modelRef(new ModelRef("string")) + .defaultValue("Bearer 00000000-0000-0000-0000-000000000000") + .build()); + + return operationParameters; + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java new file mode 100644 index 0000000..f1bcec7 --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -0,0 +1,33 @@ +package cn.seqdata.oauth2; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; + +/** + * Author: jrxian + * Date: 2019-10-02 19:27 + */ +@Configuration +@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) throws Exception { + super.configure(web); + + web.ignoring() + .antMatchers("/favicon.ico", + "/resources/**", "/css/**", "/js/**", + "/swagger-ui.html", "/webjars/**", "/plugins/**" + ); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + super.configure(http); + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java new file mode 100644 index 0000000..5db2c80 --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java @@ -0,0 +1,33 @@ +package cn.seqdata.oauth2.controller; + +import java.security.Principal; + +import io.swagger.annotations.ApiOperation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import lombok.AllArgsConstructor; + +/** + * Author: jrxian + * Date: 2020-01-27 17:16 + */ +@RestController +@RequestMapping("/user") +@AllArgsConstructor +public class UserInfoController { + + @ApiOperation("通行证") + @GetMapping("/principal") + public Principal principal(Principal principal) { + return principal; + } + + @ApiOperation("用于测试行级权限是否生效,当输入的id是用户id时通过验证,否则报403") + @GetMapping("/test") + @PreAuthorize("hasPermission(#id, null, 'read')") + public long test(long id) { + return id; + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java new file mode 100644 index 0000000..b32490a --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java @@ -0,0 +1,25 @@ +package cn.seqdata.oauth2.security; + +import java.util.Map; + +import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor; +import org.springframework.stereotype.Component; + +/** + * Author: jrxian + * Date: 2020-02-14 08:47 + */ +@Component +public class EnhancePrincipalExtractor implements PrincipalExtractor { + private static final String[] PRINCIPAL_KEYS = new String[]{"username", "mobile", "email", "id"}; + + @Override + public Object extractPrincipal(Map map) { + for(String key : PRINCIPAL_KEYS) { + if(map.containsKey(key)) { + return map.get(key); + } + } + return null; + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/TestPermissionEvaluator.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/TestPermissionEvaluator.java new file mode 100644 index 0000000..3a4d187 --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/TestPermissionEvaluator.java @@ -0,0 +1,32 @@ +package cn.seqdata.oauth2.security; + +import java.util.Objects; + +import org.springframework.stereotype.Component; + +import cn.seqdata.oauth2.client.domain.AuthUser; +import cn.seqdata.oauth2.client.domain.RepoScope; +import cn.seqdata.oauth2.client.security.EntityPermissionEvaluator; + +/** + * Author: jrxian + * Date: 2020-02-14 20:48 + */ +@Component +public class TestPermissionEvaluator implements EntityPermissionEvaluator { + + @Override + public Class entityType() { + return Object.class; + } + + @Override + public long getId(Object entity) { + return 0; + } + + @Override + public boolean hasPermission(AuthUser authUser, long entityId, RepoScope scope) { + return Objects.nonNull(authUser.getUserId()) && authUser.getUserId() == entityId && RepoScope.read.equals(scope); + } +} diff --git a/seqdata-cloud-authc/src/main/resources/application.yml b/seqdata-cloud-authc/src/main/resources/application.yml new file mode 100644 index 0000000..fe8dd46 --- /dev/null +++ b/seqdata-cloud-authc/src/main/resources/application.yml @@ -0,0 +1,16 @@ +spring: + profiles: + active: jackson +security: + oauth2: + resource: + prefer-token-info: false + token-info-uri: http://localhost:30001/oauth/check_token + user-info-uri: http://localhost:30001/user/info + client: + client-id: client + client-secret: secret +logging: + level: + root: info + cn.seqdata: trace \ No newline at end of file diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..e8ed434 --- /dev/null +++ b/seqdata-cloud-authc/src/main/resources/bootstrap.yml @@ -0,0 +1,9 @@ +spring: + application: + name: authc + cloud: + nacos: + discovery: + server-addr: nacos.seqdata.cn:8848 +server: + port: 30002 \ No newline at end of file diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml new file mode 100644 index 0000000..2464294 --- /dev/null +++ b/seqdata-cloud-authz/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-authz + 认证服务器,提供oauth2认证,接入第三方认证(如微信、钉钉) + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + + + + cn.seqdata.cloud + seqdata-cloud-oauth2 + ${project.version} + + + + com.microsoft.sqlserver + mssql-jdbc + runtime + + + diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java new file mode 100644 index 0000000..e24dacc --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -0,0 +1,30 @@ +package cn.seqdata.oauth2; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; + +/** + * Author: jrxian + * Date: 2019-09-29 23:09 + */ +@Configuration +@EnableResourceServer +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { + + @Override + public void configure(HttpSecurity http) throws Exception { + http.cors(); + + http.authorizeRequests() + .antMatchers( + "/v2/api-docs/**", "/swagger-resources/**", + "/connect/**", "/oauth/code/**", "/rbac/**", "/perm/**") + .permitAll(); + + super.configure(http); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java new file mode 100644 index 0000000..c9e61fc --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -0,0 +1,18 @@ +package cn.seqdata.oauth2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Author: jrxian + * Date: 2019-10-02 19:11 + */ +@SpringBootApplication +@EnableSwagger2 +public class AuthzServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AuthzServerApplication.class, args); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java new file mode 100644 index 0000000..de3bef5 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -0,0 +1,183 @@ +package cn.seqdata.oauth2; + +import java.util.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.CompositeTokenGranter; +import org.springframework.security.oauth2.provider.OAuth2RequestFactory; +import org.springframework.security.oauth2.provider.TokenGranter; +import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter; +import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; +import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; +import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; +import com.fasterxml.jackson.databind.ObjectMapper; + +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; +import cn.seqdata.oauth2.mobile.MobileNonceService; +import cn.seqdata.oauth2.mobile.MobileTokenGranter; +import cn.seqdata.oauth2.repos.oauth.ClientDetailRepos; +import cn.seqdata.oauth2.service.JpaClientDetailsService; +import cn.seqdata.oauth2.service.JpaUserDetailsManager; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * Author: jrxian + * Date: 2019-10-02 19:11 + */ +@Configuration +@EnableAuthorizationServer +public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { + @Autowired + private ObjectMapper objectMapper; + @Autowired + private RedisConnectionFactory redisConnectionFactory; + @Autowired + private AuthenticationManager authenticationManager; + @Autowired + private InMemoryClientRegistrationRepository registrationRepository; + @Autowired + private UserService userService; + @Autowired + private ClientDetailRepos clientDetailRepos; + @Autowired(required = false) + public MobileNonceService mobileNonceService; + + @Bean + public MobileNonceService mobileNonceService() { + return new MobileNonceService() { + @Override + public void generate(String mobile, int expire) { + } + + @Override + public boolean check(String mobile, String nonce) { + return true; + } + }; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return NoOpPasswordEncoder.getInstance(); +// return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + + @Bean + public JpaUserDetailsManager userDetailsManager() { + return new JpaUserDetailsManager(); + } + + @Bean + public TokenStore tokenStore() { + return new RedisTokenStore(redisConnectionFactory); + } + + @Bean + public TokenEnhancer tokenEnhancer() { + return (accessToken, authentication) -> { + if(accessToken instanceof DefaultOAuth2AccessToken) { + Map attributes = new HashMap<>(); + + String clientId = SecurityUtils.getClientId(authentication); + String username = SecurityUtils.getUsername(authentication); + User user = userService.loadUser(clientId, username); + + if(Objects.nonNull(user)) { + attributes.put("user_id", user.getId()); + attributes.put("org_id", user.getOrgId()); + } + + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); + } + + return accessToken; + }; + } + + @Override + public void configure(AuthorizationServerSecurityConfigurer security) { + security + .tokenKeyAccess("isAuthenticated()") + .checkTokenAccess("isAuthenticated()"); + } + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.withClientDetails(new JpaClientDetailsService(clientDetailRepos)); + +// InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); +// builder.withClient("client") +// .secret("secret") +// .authorizedGrantTypes(MobileTokenGranter.GRANT_TYPE, +// AuthorizationGrantType.PASSWORD.getValue(), +// AuthorizationGrantType.AUTHORIZATION_CODE.getValue(), +// AuthorizationGrantType.CLIENT_CREDENTIALS.getValue(), +// AuthorizationGrantType.REFRESH_TOKEN.getValue()) +// .autoApprove(true); +// +// registrationRepository.forEach(clientRegistration -> builder +// .withClient(clientRegistration.getRegistrationId()) +// .authorizedGrantTypes(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())); + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + endpoints + .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsManager()) + .tokenStore(tokenStore()) + .tokenEnhancer(tokenEnhancer()) + .tokenGranter(tokenGranter(endpoints)) + ; + } + + /** + * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter + */ + private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { + ClientDetailsService clientDetails = endpoints.getClientDetailsService(); + AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); + AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); + OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); + + List tokenGranters = new ArrayList<>(); + tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); + tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + if(Objects.nonNull(authenticationManager)) { + tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + } + //自定义的手机验证码登录 + if(Objects.nonNull(mobileNonceService)) { + MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); + tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); + } + + return new CompositeTokenGranter(tokenGranters); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/JsonSerializationStrategy.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/JsonSerializationStrategy.java new file mode 100644 index 0000000..f33804d --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/JsonSerializationStrategy.java @@ -0,0 +1,73 @@ +package cn.seqdata.oauth2; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.store.redis.StandardStringSerializationStrategy; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Author: jrxian + * Date: 2020-02-15 13:37 + */ +public class JsonSerializationStrategy extends StandardStringSerializationStrategy { + private final ObjectMapper objectMapper; + + public JsonSerializationStrategy(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper.addMixIn(OAuth2Authentication.class, JsonSerializationStrategy.OAuth2AuthenticationMixIn.class); + objectMapper.addMixIn(OAuth2Request.class, JsonSerializationStrategy.OAuth2RequestMixIn.class); + } + + @Override + protected byte[] serializeInternal(Object object) { + try { + return objectMapper.writeValueAsBytes(object); + } catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + @Override + protected T deserializeInternal(byte[] bytes, Class clazz) { + try { + return objectMapper.readValue(bytes, clazz); + } catch(IOException ex) { + throw new RuntimeException(ex); + } + } + + //由于OAuth2Authentication只提供了final参数,jackson无法直接反序列化 + private static class OAuth2AuthenticationMixIn { + @JsonCreator + public OAuth2AuthenticationMixIn(@JsonProperty("oauth2Request") OAuth2Request storedRequest, + @JsonProperty("userAuthentication") Authentication userAuthentication) { + } + } + + private static class OAuth2RequestMixIn { + @JsonCreator + public OAuth2RequestMixIn( + @JsonProperty("requestParameters") Map requestParameters, + @JsonProperty("clientId") String clientId, + @JsonProperty("authorities") Collection authorities, + @JsonProperty("approved") boolean approved, + @JsonProperty("scope") Set scope, + @JsonProperty("resourceIds") Set resourceIds, + @JsonProperty("redirectUri") String redirectUri, + @JsonProperty("responseTypes") Set responseTypes, + @JsonProperty("extensions") Map extensionProperties) { + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java new file mode 100644 index 0000000..452f4d7 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -0,0 +1,40 @@ +package cn.seqdata.oauth2; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +/** + * Author: jrxian + * Date: 2019-10-02 19:27 + */ +@Configuration +@EnableWebSecurity +public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Override + public void configure(WebSecurity web) throws Exception { + super.configure(web); + + web.ignoring() + .antMatchers("/favicon.ico", + "/resources/**", "/css/**", "/js/**", + "/swagger-ui.html", "/webjars/**", "/plugins/**" + ); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + super.configure(http); + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java new file mode 100644 index 0000000..4b1edff --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -0,0 +1,65 @@ +package cn.seqdata.oauth2.controller; + +import java.net.URI; +import java.security.Principal; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.web.bind.annotation.*; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.oauth.UserDetailId; +import cn.seqdata.oauth2.provider.OAuth2Provider; +import cn.seqdata.oauth2.provider.OAuth2Template; +import cn.seqdata.oauth2.repos.oauth.UserDetailRepos; +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * Author: jrxian + * Date: 2020-01-24 01:13 + */ +@RestController +@RequestMapping("/connect") +@AllArgsConstructor +public class ConnectController { + private final ClientRegistrationRepository repository; + private final UserDetailRepos userDetailRepos; + + /** + * 向手机发送一次性验证码 + */ + @GetMapping(value = "/mobile") + public ResponseEntity mobile(@RequestParam String mobile, HttpServletRequest request) { + return ResponseEntity.ok("已经成功的将验证码发送到您的手机,请注意查收!"); + } + + @GetMapping(value = "/{registrationId}") + public ResponseEntity connect(@PathVariable String registrationId, HttpServletRequest request) { + ClientRegistration registration = repository.findByRegistrationId(registrationId); + OAuth2Provider provider = SecurityUtils.getProvider(registration); + OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); + + //从 request 中分离出 accessToken + String accessToken = SecurityUtils.getAccessToken(request.getUserPrincipal()); + + //重定向 URI,返回信息由 OAuth2CodeController 负责接收 + String redirectURL = String.valueOf((request.getRequestURL())); + redirectURL = redirectURL.replace("/connect", "/oauth/code"); + + //将 accessToken 以 state 的形式传送出去,返回后用来判断被绑定的用户 + URI authorizeURI = oAuth2Template.buildAuthorizeURI(accessToken, redirectURL); + + return ResponseEntity.status(HttpStatus.SEE_OTHER) + .location(authorizeURI) + .build(); + } + + @DeleteMapping(value = "/{registrationId}") + public void unbind(@PathVariable String registrationId, Principal principal) { + UserDetailId entityId = new UserDetailId(registrationId, principal.getName()); + userDetailRepos.deleteById(entityId); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java new file mode 100644 index 0000000..7d99a3b --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -0,0 +1,92 @@ +package cn.seqdata.oauth2.controller; + +import java.security.Principal; +import java.util.Objects; + +import io.swagger.annotations.ApiOperation; +import org.springframework.http.HttpStatus; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.web.bind.annotation.*; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.provider.OAuth2Provider; +import cn.seqdata.oauth2.provider.OAuth2Template; +import cn.seqdata.oauth2.provider.UserProfile; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * Author: jrxian + * Date: 2020-01-24 02:09 + */ +@RestController +@RequestMapping("/oauth/code") +@AllArgsConstructor +public class OAuth2CodeController { + private final ClientRegistrationRepository repository; + private final TokenStore tokenStore; + private final UserService userService; + + /** + * 修改用户名和密码 + */ + @GetMapping(value = "/password") + public void password(Principal principal, @RequestParam String username, @RequestParam String password) { + } + + /** + * 更换手机号 + */ + @GetMapping(value = "/mobile") + public void mobile(Principal principal, @RequestParam String mobile, @RequestParam String nonce) { + } + + @ApiOperation("authorization_code的回调函数,处理code和state") + @GetMapping(value = "/{registrationId}", params = OAuth2ParameterNames.CODE) + public OAuth2AccessToken bind(@PathVariable String registrationId, + @RequestParam(OAuth2ParameterNames.CODE) String code, + @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { + ClientRegistration registration = repository.findByRegistrationId(registrationId); + OAuth2Provider provider = SecurityUtils.getProvider(registration); + OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); + + //从远程拉取用户信息,以 registrationId + remoteUser.name 作为唯一识别符 + OAuth2AccessTokenResponse tokenResponse = oAuth2Template.exchangeForAccess(code, state); + OAuth2User remoteUser = oAuth2Template.loadUser(tokenResponse); + UserProfile userProfile = provider.profiler.apply(remoteUser); + + //这里的 state 是 accessToken + OAuth2Authentication authentication = tokenStore.readAuthentication(state); + if(Objects.nonNull(authentication)) { + //获取当前用户身份 + String localClientId = SecurityUtils.getClientId(authentication); + String localUsername = SecurityUtils.getUsername(authentication); + User user = userService.loadUser(localClientId, localUsername); + userService.bindUser(registrationId, authentication.getName(), user); + } else { + //当用户不存在时自动创建 + userService.createUser(registrationId, userProfile); + } + + return userService.createToken(registrationId, remoteUser); + } + + @ApiOperation("authorization_code的错误回调函数,处理error, error_description, error_uri") + @ResponseStatus(HttpStatus.BAD_REQUEST) + @GetMapping(value = "/{registrationId}", params = OAuth2ParameterNames.ERROR) + public OAuth2Error error(@PathVariable String registrationId, + @RequestParam(OAuth2ParameterNames.ERROR) String error, + @RequestParam(value = OAuth2ParameterNames.ERROR_DESCRIPTION, required = false) String errorDescription, + @RequestParam(value = OAuth2ParameterNames.ERROR_URI, required = false) String errorURI) { + return new OAuth2Error(error, errorDescription, errorURI); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java new file mode 100644 index 0000000..fe2c042 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java @@ -0,0 +1,63 @@ +package cn.seqdata.oauth2.controller; + +import java.security.Principal; +import java.util.Collection; +import java.util.Collections; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * Author: jrxian + * Date: 2020-01-27 17:16 + */ +@Api("授权服务器保持的用户信息") +@RestController +@RequestMapping("/user") +@AllArgsConstructor +public class UserInfoController { + private final TokenStore tokenStore; + private final UserService userService; + + @ApiOperation("通行证") + @GetMapping("/principal") + public Principal principal(Principal principal) { + return principal; + } + + @ApiOperation("令牌") + @GetMapping("/token") + public OAuth2AccessToken token(Principal principal) { + String tokenValue = SecurityUtils.getAccessToken(principal); + return tokenStore.readAccessToken(tokenValue); + } + + @ApiOperation("用户信息") + @GetMapping("/info") + public User info(Principal principal) { + String clientId = SecurityUtils.getClientId(principal); + String username = SecurityUtils.getUsername(principal); + return userService.loadUser(clientId, username); + } + + @ApiOperation("拥有角色") + @GetMapping("/authorities") + public Collection authorities(Principal principal) { + if(principal instanceof OAuth2Authentication) { + return ((OAuth2Authentication) principal).getAuthorities(); + } + return Collections.emptyList(); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/github/GithubUserProfile.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/github/GithubUserProfile.java new file mode 100644 index 0000000..fbad90c --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/github/GithubUserProfile.java @@ -0,0 +1,31 @@ +package cn.seqdata.oauth2.github; + +import org.springframework.security.oauth2.core.user.OAuth2User; + +import cn.seqdata.oauth2.provider.UserProfile; + +/** + * Author: jrxian + * Date: 2020-01-25 08:36 + */ +public class GithubUserProfile extends UserProfile { + + public GithubUserProfile(OAuth2User delegate) { + super(delegate); + } + + @Override + public String getNickname() { + return getAttribute("name"); + } + + @Override + public String getUsername() { + return getAttribute("login"); + } + + @Override + public String getAvatar() { + return getAttribute("avatar_url"); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java new file mode 100644 index 0000000..eb88e81 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java @@ -0,0 +1,61 @@ +package cn.seqdata.oauth2.mobile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +/** + * Author: jrxian + * Date: 2020-01-22 12:06 + */ +public class MobileNonceAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + + private boolean postOnly = true; + + public MobileNonceAuthenticationFilter() { + super(new AntPathRequestMatcher("/mobile", HttpMethod.POST.name())); + } + + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { + if(postOnly && !StringUtils.equals(HttpMethod.POST.name(), request.getMethod())) { + throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); + } + + String username = obtainUsername(request); + String password = obtainPassword(request); + + MobileNonceAuthenticationToken authRequest = new MobileNonceAuthenticationToken(username, password); + authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); + + return getAuthenticationManager().authenticate(authRequest); + } + + protected String obtainUsername(HttpServletRequest request) { + String username = request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); + if(StringUtils.isBlank(username)) { + username = request.getParameter("mobile"); + } + return ObjectUtils.defaultIfNull(username, ""); + } + + protected String obtainPassword(HttpServletRequest request) { + String password = request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); + if(StringUtils.isBlank(password)) { + password = request.getParameter("nonce"); + } + return ObjectUtils.defaultIfNull(password, ""); + } + + public void setPostOnly(boolean postOnly) { + this.postOnly = postOnly; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java new file mode 100644 index 0000000..8a1acc2 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java @@ -0,0 +1,36 @@ +package cn.seqdata.oauth2.mobile; + +import java.util.Collections; + +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; + +/** + * Author: jrxian + * Date: 2020-01-26 00:52 + */ +public class MobileNonceAuthenticationProvider implements AuthenticationProvider { + private final MobileNonceService mobileNonceService; + + public MobileNonceAuthenticationProvider(MobileNonceService mobileNonceService) { + this.mobileNonceService = mobileNonceService; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + Object mobile = authentication.getPrincipal(); + Object nonce = authentication.getCredentials(); + if(!mobileNonceService.check(String.valueOf(mobile), String.valueOf(nonce))) { + throw new BadCredentialsException("Invalid Credentials"); + } + return new UsernamePasswordAuthenticationToken(mobile, null, Collections.emptyList()); + } + + @Override + public boolean supports(Class authentication) { + return authentication.isAssignableFrom(MobileNonceAuthenticationToken.class); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java new file mode 100644 index 0000000..6bcb9a8 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java @@ -0,0 +1,56 @@ +package cn.seqdata.oauth2.mobile; + +import java.util.Collection; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; + +/** + * Author: jrxian + * Date: 2020-01-22 12:12 + */ +public class MobileNonceAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + private final Object principal; + private Object credentials; + + public MobileNonceAuthenticationToken(Object principal, Object credentials) { + super(null); + this.principal = principal; + this.credentials = credentials; + setAuthenticated(false); + } + + public MobileNonceAuthenticationToken(Object principal, Object credentials, + Collection authorities) { + super(authorities); + this.principal = principal; + this.credentials = credentials; + super.setAuthenticated(true); // must use super, as we override + } + + public Object getCredentials() { + return this.credentials; + } + + public Object getPrincipal() { + return this.principal; + } + + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + if(isAuthenticated) { + throw new IllegalArgumentException( + "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); + } + + super.setAuthenticated(false); + } + + @Override + public void eraseCredentials() { + super.eraseCredentials(); + credentials = null; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java new file mode 100644 index 0000000..7fbae74 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java @@ -0,0 +1,11 @@ +package cn.seqdata.oauth2.mobile; + +/** + * Author: jrxian + * Date: 2020-01-26 10:28 + */ +public interface MobileNonceService { + void generate(String mobile, int expire); + + boolean check(String mobile, String nonce); +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java new file mode 100644 index 0000000..f41019d --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java @@ -0,0 +1,52 @@ +package cn.seqdata.oauth2.mobile; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.AccountStatusException; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +/** + * Author: jrxian + * Date: 2020-01-26 00:16 + */ +public class MobileTokenGranter extends AbstractTokenGranter { + public static final String GRANT_TYPE = "mobile"; + + private final AuthenticationProvider authenticationProvider; + + public MobileTokenGranter(AuthenticationProvider authenticationProvider, + AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, + OAuth2RequestFactory requestFactory) { + super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + this.authenticationProvider = authenticationProvider; + } + + @Override + protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { + Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); + String username = parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); + String password = parameters.remove(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); + + Authentication userAuth = new MobileNonceAuthenticationToken(username, password); + ((AbstractAuthenticationToken) userAuth).setDetails(parameters); + try { + userAuth = authenticationProvider.authenticate(userAuth); + } catch(AccountStatusException ex) { + throw new InvalidGrantException(ex.getMessage()); + } + if(userAuth == null || !userAuth.isAuthenticated()) { + throw new InvalidGrantException("Could not authenticate username: " + username); + } + + OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); + return new OAuth2Authentication(storedOAuth2Request, userAuth); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/DefaultRestTemplateCustomizer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/DefaultRestTemplateCustomizer.java new file mode 100644 index 0000000..6366d0f --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/DefaultRestTemplateCustomizer.java @@ -0,0 +1,26 @@ +package cn.seqdata.oauth2.provider; + +import java.util.List; + +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler; +import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +/** + * Author: jrxian + * Date: 2020-01-25 11:33 + */ +public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer { + + @Override + public void customize(RestTemplate restTemplate) { + List> messageConverters = restTemplate.getMessageConverters(); + messageConverters.add(new OAuth2AccessTokenResponseHttpMessageConverter()); + messageConverters.add(new MappingJackson2HttpMessageConverter()); + + restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Operations.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Operations.java new file mode 100644 index 0000000..8f7bf91 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Operations.java @@ -0,0 +1,34 @@ +package cn.seqdata.oauth2.provider; + +import java.net.URI; + +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2RefreshToken; +import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.core.user.OAuth2User; + +/** + * https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/ + */ +public interface OAuth2Operations { + + /** + * GET https://github.com/login/oauth/authorize?client_id=&scope=&state=&redirect_uri= + */ + URI buildAuthorizeURI(String state, String redirectURI); + + /** + * POST https://github.com/login/oauth/access_token?client_id=&client_secret=&code=&state= + */ + OAuth2AccessTokenResponse exchangeForAccess(String code, String state); + + OAuth2AccessTokenResponse exchangeCredentialsForAccess(String username, String password); + + OAuth2AccessTokenResponse refreshAccess(OAuth2AccessToken accessToken, OAuth2RefreshToken refreshToken); + + /** + * Authorization: token OAUTH-TOKEN + * GET https://api.github.com/user + */ + OAuth2User loadUser(OAuth2AccessTokenResponse accessTokenResponse); +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java new file mode 100644 index 0000000..9b650e1 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java @@ -0,0 +1,100 @@ +package cn.seqdata.oauth2.provider; + +import java.util.function.Function; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import cn.seqdata.oauth2.github.GithubUserProfile; +import cn.seqdata.oauth2.wechat.WechatOAuth2Template; +import cn.seqdata.oauth2.wechat.WechatUserProfile; + +/** + * Author: jrxian + * Date: 2020-01-24 00:39 + */ +public enum OAuth2Provider { + GITHUB("github.com", GithubUserProfile::new) { + @Override + public ClientRegistration.Builder getBuilder(String registrationId) { + return CommonOAuth2Provider.GITHUB.getBuilder(registrationId); + } + }, + TENCENT("graph.qq.com", UserProfile::new) { + @Override + public ClientRegistration.Builder getBuilder(String registrationId) { + return ClientRegistration.withRegistrationId(registrationId) + .authorizationUri("https://graph.qq.com/oauth2.0/show") + .tokenUri("https://graph.qq.com/oauth2.0/token") + .userInfoUri("https://graph.qq.com/user/get_user_info"); + } + }, + WECHAT("api.weixin.qq.com", WechatUserProfile::new) { + @Override + public ClientRegistration.Builder getBuilder(String registrationId) { + return ClientRegistration.withRegistrationId(registrationId) + .authorizationUri("https://open.weixin.qq.com/connect/qrconnect") + .tokenUri("https://api.weixin.qq.com/sns/oauth2/access_token") + .userInfoUri("https://api.weixin.qq.com/sns/userinfo"); + } + + @Override + public OAuth2Template createOAuth2Template(ClientRegistration registration) { + return new WechatOAuth2Template(registration); + } + }, + ALIPAY("openapi.alipay.com", UserProfile::new) { + @Override + public ClientRegistration.Builder getBuilder(String registrationId) { + return ClientRegistration.withRegistrationId(registrationId) + .authorizationUri("https://openauth.alipay.com/oauth2/publicAppAuthorize.htm") + .tokenUri("https://openapi.alipay.com/gateway.do") + .userInfoUri("https://openapi.alipay.com/gateway.do"); + } + }, + DEFAULT("localhost", UserProfile::new) { + @Override + public ClientRegistration.Builder getBuilder(String registrationId) { + return ClientRegistration.withRegistrationId(registrationId) + .authorizationUri("https://localhost/oauth/authorize") + .tokenUri("https://localhost/oauth/token") + .userInfoUri("https://localhost/oauth/check_token"); + } + }; + + public final String hostname; + public final Function profiler; + + OAuth2Provider(String hostname, Function profiler) { + this.hostname = hostname; + this.profiler = profiler; + } + + public abstract ClientRegistration.Builder getBuilder(String registrationId); + + public OAuth2Template createOAuth2Template(ClientRegistration registration) { + return new OAuth2Template(registration); + } + + public static OAuth2Provider fromString(String providerId) { + for(OAuth2Provider value : values()) { + if(0 == StringUtils.compareIgnoreCase(value.name(), providerId)) { + return value; + } + } + + return DEFAULT; + } + + public static OAuth2Provider matcher(String url) { + for(OAuth2Provider value : values()) { + if(StringUtils.contains(url, value.hostname)) { + return value; + } + } + + return DEFAULT; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Template.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Template.java new file mode 100644 index 0000000..b716021 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Template.java @@ -0,0 +1,141 @@ +package cn.seqdata.oauth2.provider; + +import java.net.URI; +import java.util.Collections; +import java.util.Objects; + +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.core.convert.converter.Converter; +import org.springframework.http.RequestEntity; +import org.springframework.http.converter.FormHttpMessageConverter; +import org.springframework.security.oauth2.client.endpoint.*; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2RefreshToken; +import org.springframework.security.oauth2.core.endpoint.*; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Author: jrxian + * Date: 2020-01-24 09:29 + */ +public class OAuth2Template implements OAuth2Operations { + protected final ClientRegistration registration; + protected final RestTemplate restTemplate = createRestTemplate(); + private Converter> authorizationCodeEntityConverter; + private Converter> passwordEntityConverter; + private Converter> refreshTokenEntityConverter; + + public OAuth2Template(ClientRegistration registration) { + this(registration, new DefaultRestTemplateCustomizer()); + } + + public OAuth2Template(ClientRegistration registration, RestTemplateCustomizer restTemplateCustomizer) { + this.registration = registration; + restTemplateCustomizer.customize(restTemplate); + } + + @Override + public URI buildAuthorizeURI(String state, String redirectURI) { + ClientRegistration.ProviderDetails providerDetails = registration.getProviderDetails(); + + return UriComponentsBuilder.fromUriString(providerDetails.getAuthorizationUri()) + .queryParam(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2ParameterNames.CODE) + .queryParam(OAuth2ParameterNames.CLIENT_ID, registration.getClientId()) + .queryParam(OAuth2ParameterNames.SCOPE, registration.getScopes()) + .queryParam(OAuth2ParameterNames.STATE, state) + .queryParam(OAuth2ParameterNames.REDIRECT_URI, redirectURI) + .build() + .toUri(); + } + + @Override + public OAuth2AccessTokenResponse exchangeForAccess(String code, String state) { + DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient(); + client.setRestOperations(restTemplate); + + if(Objects.nonNull(authorizationCodeEntityConverter)) { + client.setRequestEntityConverter(authorizationCodeEntityConverter); + } + + OAuth2AuthorizationRequest.Builder requestBuilder = buildOAuth2AuthorizationRequest(state); + OAuth2AuthorizationResponse.Builder responseBuilder = buildOAuth2AuthorizationResponse(code, state); + + OAuth2AuthorizationExchange exchange = new OAuth2AuthorizationExchange(requestBuilder.build(), responseBuilder.build()); + OAuth2AuthorizationCodeGrantRequest request = new OAuth2AuthorizationCodeGrantRequest(registration, exchange); + return client.getTokenResponse(request); + } + + @Override + public OAuth2AccessTokenResponse exchangeCredentialsForAccess(String username, String password) { + DefaultPasswordTokenResponseClient client = new DefaultPasswordTokenResponseClient(); + client.setRestOperations(restTemplate); + + if(Objects.nonNull(passwordEntityConverter)) { + client.setRequestEntityConverter(passwordEntityConverter); + } + + OAuth2PasswordGrantRequest request = new OAuth2PasswordGrantRequest(registration, username, password); + return client.getTokenResponse(request); + } + + @Override + public OAuth2AccessTokenResponse refreshAccess(OAuth2AccessToken accessToken, OAuth2RefreshToken refreshToken) { + DefaultRefreshTokenTokenResponseClient client = new DefaultRefreshTokenTokenResponseClient(); + client.setRestOperations(restTemplate); + + if(Objects.nonNull(refreshTokenEntityConverter)) { + client.setRequestEntityConverter(refreshTokenEntityConverter); + } + + OAuth2RefreshTokenGrantRequest request = new OAuth2RefreshTokenGrantRequest(registration, accessToken, refreshToken); + return client.getTokenResponse(request); + } + + @Override + public OAuth2User loadUser(OAuth2AccessTokenResponse accessTokenResponse) { + DefaultOAuth2UserService client = new DefaultOAuth2UserService(); + client.setRestOperations(restTemplate); + + OAuth2UserRequest userRequest = new OAuth2UserRequest(registration, accessTokenResponse.getAccessToken()); + return client.loadUser(userRequest); + } + + public void setAuthorizationCodeEntityConverter(Converter> authorizationCodeEntityConverter) { + this.authorizationCodeEntityConverter = authorizationCodeEntityConverter; + } + + public void setPasswordEntityConverter(Converter> passwordEntityConverter) { + this.passwordEntityConverter = passwordEntityConverter; + } + + public void setRefreshTokenEntityConverter(Converter> refreshTokenEntityConverter) { + this.refreshTokenEntityConverter = refreshTokenEntityConverter; + } + + protected RestTemplate createRestTemplate() { + return new RestTemplate(Collections.singletonList(new FormHttpMessageConverter())); + } + + protected OAuth2AuthorizationRequest.Builder buildOAuth2AuthorizationRequest(String state) { + ClientRegistration.ProviderDetails providerDetails = registration.getProviderDetails(); + + return OAuth2AuthorizationRequest + .authorizationCode() + .clientId(registration.getClientId()) + .scopes(registration.getScopes()) + .state(state) + .authorizationUri(providerDetails.getAuthorizationUri()); + } + + protected OAuth2AuthorizationResponse.Builder buildOAuth2AuthorizationResponse(String code, String state) { + return OAuth2AuthorizationResponse + .success(code) + .state(state) + .redirectUri(registration.getRedirectUriTemplate()); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java new file mode 100644 index 0000000..b635ca4 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java @@ -0,0 +1,92 @@ +package cn.seqdata.oauth2.provider; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.core.user.OAuth2User; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +/** + * Author: jrxian + * Date: 2020-01-23 23:26 + */ +@lombok.Getter +@lombok.Setter +public class UserProfile implements OAuth2User, Serializable { + private final OAuth2User delegate; + + public UserProfile(OAuth2User delegate) { + this.delegate = delegate; + } + + /** + * 唯一编号或者唯一名称 + */ + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public A getAttribute(String name) { + return delegate.getAttribute(name); + } + + @JsonAnySetter + public void setAttribute(String key, Object value) { + getAttributes().put(key, value); + } + + @JsonAnyGetter + @Override + public Map getAttributes() { + return delegate.getAttributes(); + } + + @Override + public Collection getAuthorities() { + return delegate.getAuthorities(); + } + + /** + * 昵称 + */ + public String getNickname() { + return getAttribute("nickname"); + } + + /** + * 登录名 + */ + public String getUsername() { + return getAttribute("username"); + } + + /** + * 邮箱 + */ + public String getEmail() { + return getAttribute("email"); + } + + public String getMobile() { + return getAttribute("mobile"); + } + + /** + * 头像 + */ + public String getAvatar() { + return getAttribute("avatar"); + } + + /** + * 地址 + */ + public String getLocation() { + return getAttribute("location"); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java new file mode 100644 index 0000000..6109523 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -0,0 +1,136 @@ +package cn.seqdata.oauth2.service; + +import java.util.Collections; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.oauth.UserDetail; +import cn.seqdata.oauth2.jpa.oauth.UserDetailId; +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.provider.UserProfile; +import cn.seqdata.oauth2.repos.oauth.UserDetailRepos; +import cn.seqdata.oauth2.repos.rbac.UserRepos; +import cn.seqdata.oauth2.wechat.WechatUserProfile; + +/** + * Author: jrxian + * Date: 2020-01-27 01:46 + */ +@Service +@AllArgsConstructor +public class UserService { + private final AuthorizationServerTokenServices tokenServices; + private final TokenStore tokenStore; + private final UserDetailsService userDetailsService; + private final JpaUserDetailsManager userDetailsManager; + private final UserRepos userRepos; + private final UserDetailRepos userDetailRepos; + + /** + * 将第三方认证获取的用户信息转换为本系统的 User + */ + @Transactional + public User createUser(String clientId, UserProfile userProfile) { + UserDetailId entityId = new UserDetailId(clientId, userProfile.getName()); + Optional optional = userDetailRepos.findById(entityId); + UserDetail userDetail = new UserDetail(entityId); + + //用户不存在 + if(!optional.isPresent()) { + //查找微信的unionid,如果找到,视为同一个用户 + if(userProfile instanceof WechatUserProfile) { + User user = userRepos.findByUnionId(((WechatUserProfile) userProfile).getUnionId()); + if(Objects.nonNull(user) && Objects.nonNull(user.getId())) { + userDetail.setUserId(user.getId()); + } + } + + //没有找到微信unionid + if(Objects.isNull(userDetail.getUserId())) { + //保存并且获取到保存后的userId + User user = convert(userProfile); + user = userRepos.save(user); + userDetail.setUserId(user.getId()); + } + + userDetail = userDetailRepos.save(userDetail); + } else { + userDetail = optional.get(); + } + + return userDetail.getUser(); + } + + public User loadUser(String clientId, String username) { + UserDetailId entityId = new UserDetailId(clientId, username); + //先从oauth_user_details中查找用户 + User user = userDetailRepos.findById(entityId) + .map(UserDetail::getUser) + //未找到,再从oauth_user中查找用户 + .orElse(userRepos.findByUsername(username)); + + if(Objects.nonNull(user)) { + long userId = Objects.requireNonNull(user.getId()); + user.setAuthorities(userDetailsManager.loadAuthorities(userId)); + } + + return user; + } + + /** + * 将第三方认证的用户和本系统的用户绑定 + */ + @Transactional + public void bindUser(String clientId, String username, User user) { + UserDetailId entityId = new UserDetailId(clientId, username); + UserDetail entity = new UserDetail(entityId); + entity.setUserId(user.getId()); + userDetailRepos.save(entity); + } + + /** + * 为第三方认证的用户创建本系统的 token + */ + public OAuth2AccessToken createToken(String clientId, OAuth2User principal) { + OAuth2Request oAuth2Request = new OAuth2Request(Collections.emptyMap(), clientId, + Collections.emptySet(), true, Collections.emptySet(), + Collections.emptySet(), null, null, null); + + OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(), clientId); + OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); + return tokenServices.createAccessToken(oAuth2Authentication); + } + + /** + * 根据UserProfile创建User + */ + private User convert(UserProfile userProfile) { + User user = new User(); + + user.setName(userProfile.getNickname()); + user.setUsername(userProfile.getUsername()); + user.setEmail(userProfile.getEmail()); + user.setMobile(userProfile.getMobile()); + user.setAvatar(userProfile.getAvatar()); + //第三方首次创建的账号默认是管理员 + user.setAdmin(Boolean.TRUE); + if(userProfile instanceof WechatUserProfile) { + //为微信单独保存 union_id + user.setUnionId(((WechatUserProfile) userProfile).getUnionId()); + } + + return user; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java new file mode 100644 index 0000000..785847d --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -0,0 +1,51 @@ +package cn.seqdata.oauth2.util; + +import java.security.Principal; + +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; + +import cn.seqdata.oauth2.provider.OAuth2Provider; + +/** + * Author: jrxian + * Date: 2020-01-27 01:59 + */ +public final class SecurityUtils { + private SecurityUtils() { + } + + public static String getClientId(Principal principal) { + if(principal instanceof OAuth2Authentication) { + OAuth2Request auth2Request = ((OAuth2Authentication) principal).getOAuth2Request(); + return auth2Request.getClientId(); + } + + return null; + } + + public static String getUsername(Principal principal) { + return principal.getName(); + } + + /** + * 根据用户获取oauth2的token + */ + public static String getAccessToken(Principal principal) { + if(principal instanceof OAuth2Authentication) { + Object details = ((OAuth2Authentication) principal).getDetails(); + if(details instanceof OAuth2AuthenticationDetails) { + return ((OAuth2AuthenticationDetails) details).getTokenValue(); + } + } + + return null; + } + + public static OAuth2Provider getProvider(ClientRegistration registration) { + ClientRegistration.ProviderDetails details = registration.getProviderDetails(); + return OAuth2Provider.matcher(details.getTokenUri()); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java new file mode 100644 index 0000000..ae3187d --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java @@ -0,0 +1,44 @@ +package cn.seqdata.oauth2.wechat; + +import java.net.URI; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.RequestEntity; +import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +/** + * Author: jrxian + * Date: 2020-01-24 21:26 + */ +public class WechatAuthorizationCodeGrantRequestEntityConverter implements Converter> { + + @Override + public RequestEntity convert(OAuth2AuthorizationCodeGrantRequest authorizationCodeGrantRequest) { + ClientRegistration clientRegistration = authorizationCodeGrantRequest.getClientRegistration(); + ClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails(); + AuthorizationGrantType grantType = authorizationCodeGrantRequest.getGrantType(); + OAuth2AuthorizationExchange authorizationExchange = authorizationCodeGrantRequest.getAuthorizationExchange(); + OAuth2AuthorizationRequest authorizationRequest = authorizationExchange.getAuthorizationRequest(); + OAuth2AuthorizationResponse authorizationResponse = authorizationExchange.getAuthorizationResponse(); + + MultiValueMap formParameters = new LinkedMultiValueMap<>(); + formParameters.add("appid", clientRegistration.getClientId()); + formParameters.add("secret", clientRegistration.getClientSecret()); + formParameters.add(OAuth2ParameterNames.GRANT_TYPE, grantType.getValue()); + formParameters.add(OAuth2ParameterNames.CODE, authorizationResponse.getCode()); + + URI uri = URI.create(providerDetails.getTokenUri()); + + return new RequestEntity<>(formParameters, new HttpHeaders(), HttpMethod.POST, uri); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatOAuth2Template.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatOAuth2Template.java new file mode 100644 index 0000000..3ef7b7a --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatOAuth2Template.java @@ -0,0 +1,58 @@ +package cn.seqdata.oauth2.wechat; + +import java.net.URI; +import java.util.Map; + +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.web.util.UriComponentsBuilder; +import com.google.common.collect.Maps; + +import cn.seqdata.oauth2.provider.OAuth2Template; + +/** + * Author: jrxian + * Date: 2020-01-24 21:56 + */ +public class WechatOAuth2Template extends OAuth2Template { + + public WechatOAuth2Template(ClientRegistration registration) { + super(registration, new WechatRestTemplateCustomizer()); + + setAuthorizationCodeEntityConverter(new WechatAuthorizationCodeGrantRequestEntityConverter()); + } + + @Override + public URI buildAuthorizeURI(String state, String redirectURI) { + ClientRegistration.ProviderDetails providerDetails = registration.getProviderDetails(); + + return UriComponentsBuilder.fromUriString(providerDetails.getAuthorizationUri()) + .queryParam(OAuth2ParameterNames.RESPONSE_TYPE, OAuth2ParameterNames.CODE) + .queryParam("appid", registration.getClientId()) + .queryParam(OAuth2ParameterNames.SCOPE, registration.getScopes()) + .queryParam(OAuth2ParameterNames.STATE, state) + .queryParam(OAuth2ParameterNames.REDIRECT_URI, redirectURI) + .build() + .toUri(); + } + + @Override + public OAuth2User loadUser(OAuth2AccessTokenResponse accessTokenResponse) { + DefaultOAuth2UserService client = new DefaultOAuth2UserService(); + client.setRestOperations(restTemplate); + + client.setRequestEntityConverter(new WechatUserRequestConverter()); + + //将openid作为附件请求参数 + OAuth2AccessToken accessToken = accessTokenResponse.getAccessToken(); + Map additionalParameters = Maps.filterKeys(accessTokenResponse.getAdditionalParameters(), "openid"::equals); + + OAuth2UserRequest userRequest = new OAuth2UserRequest(registration, accessToken, additionalParameters); + return client.loadUser(userRequest); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java new file mode 100644 index 0000000..75db1e4 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java @@ -0,0 +1,38 @@ +package cn.seqdata.oauth2.wechat; + +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.http.MediaType; +import org.springframework.http.converter.FormHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +/** + * Author: jrxian + * Date: 2020-01-24 18:56 + */ +public class WechatRestTemplateCustomizer implements RestTemplateCustomizer { + //微信返回格式是text/plain,因此需要替换兼容的mediaType + private static final List supportedMediaTypes = Collections.singletonList(MediaType.TEXT_PLAIN); + + @Override + public void customize(RestTemplate restTemplate) { + + List> messageConverters = restTemplate.getMessageConverters(); + messageConverters.add(new FormHttpMessageConverter()); + + OAuth2AccessTokenResponseHttpMessageConverter accessTokenConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); + accessTokenConverter.setSupportedMediaTypes(supportedMediaTypes); + //微信返回的accessToken和标准不兼容,需要自定义转换函数,在additional中包含openid和unionid + accessTokenConverter.setTokenResponseConverter(new WechatTokenResponseConverter()); + messageConverters.add(accessTokenConverter); + + MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); + jackson2HttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes); + messageConverters.add(jackson2HttpMessageConverter); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatTokenResponseConverter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatTokenResponseConverter.java new file mode 100644 index 0000000..a349c4d --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatTokenResponseConverter.java @@ -0,0 +1,30 @@ +package cn.seqdata.oauth2.wechat; + +import java.util.Map; + +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.util.StringUtils; +import com.google.common.collect.Maps; + +/** + * Author: jrxian + * Date: 2020-01-25 08:26 + */ +public class WechatTokenResponseConverter implements Converter, OAuth2AccessTokenResponse> { + + @Override + public OAuth2AccessTokenResponse convert(Map params) { + return OAuth2AccessTokenResponse + .withToken(params.remove(OAuth2ParameterNames.ACCESS_TOKEN)) + .tokenType(OAuth2AccessToken.TokenType.BEARER) + .expiresIn(NumberUtils.toLong(params.remove(OAuth2ParameterNames.EXPIRES_IN))) + .refreshToken(params.remove(OAuth2ParameterNames.REFRESH_TOKEN)) + .scopes(StringUtils.commaDelimitedListToSet(params.remove(OAuth2ParameterNames.SCOPE))) + .additionalParameters(Maps.transformValues(params, value -> (Object) value)) + .build(); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java new file mode 100644 index 0000000..e9e22f5 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java @@ -0,0 +1,32 @@ +package cn.seqdata.oauth2.wechat; + +import org.springframework.security.oauth2.core.user.OAuth2User; + +import cn.seqdata.oauth2.provider.UserProfile; + +/** + * Author: jrxian + * Date: 2020-01-25 08:41 + */ +@lombok.Getter +@lombok.Setter +public class WechatUserProfile extends UserProfile { + + public WechatUserProfile(OAuth2User delegate) { + super(delegate); + } + + @Override + public String getName() { + return getAttribute("openid"); + } + + @Override + public String getAvatar() { + return getAttribute("headimgurl"); + } + + public String getUnionId() { + return getAttribute("unionid"); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserRequestConverter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserRequestConverter.java new file mode 100644 index 0000000..e7046d3 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserRequestConverter.java @@ -0,0 +1,35 @@ +package cn.seqdata.oauth2.wechat; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.http.HttpMethod; +import org.springframework.http.RequestEntity; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; +import org.springframework.web.util.UriBuilder; +import org.springframework.web.util.UriComponentsBuilder; + +/** + * Author: jrxian + * Date: 2020-01-27 17:52 + */ +public class WechatUserRequestConverter implements Converter> { + + @Override + public RequestEntity convert(OAuth2UserRequest source) { + ClientRegistration registration = source.getClientRegistration(); + ClientRegistration.ProviderDetails providerDetails = registration.getProviderDetails(); + ClientRegistration.ProviderDetails.UserInfoEndpoint endpoint = providerDetails.getUserInfoEndpoint(); + + OAuth2AccessToken accessToken = source.getAccessToken(); + + UriBuilder builder = UriComponentsBuilder.fromUriString(endpoint.getUri()) + .queryParam(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()); + + source.getAdditionalParameters() + .forEach(builder::queryParam); + + return new RequestEntity<>(HttpMethod.GET, builder.build()); + } +} diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml new file mode 100644 index 0000000..115a7bf --- /dev/null +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -0,0 +1,39 @@ +logging: + level: + root: info +# org.springframework.security: debug +spring: + profiles: + active: jackson, jpa + datasource: + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + url: jdbc:sqlserver://cloud.seqdata.cn:1433;DatabaseName=seqdata-cloud + username: seqdata + password: cloud@123 + redis: + host: redis.seqdata.cn + port: 6379 + security: + oauth2: + client: + registration: + github: + provider: github + client-id: f55188e56a9af49701c4 + client-secret: 1968a44d0b5270f65faf854d10d43e9f1dded62e + wechat: + provider: wechat + client-id: wxa71a478c9d3b8826 + client-secret: 6749859cfa2c4d762cf5f47398927f31 + client-name: 微信 + client-authentication-method: post + authorization-grant-type: authorization_code + redirect-uri: "{baseUrl}/{action}/oauth/code/{registrationId}" + scope: snsapi_login + provider: + wechat: + authorization-uri: https://open.weixin.qq.com/connect/qrconnect + token-uri: https://api.weixin.qq.com/sns/oauth2/access_token + user-info-uri: https://api.weixin.qq.com/sns/userinfo + user-info-authentication-method: query + user-name-attribute: openid \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..95a7554 --- /dev/null +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -0,0 +1,9 @@ +spring: + application: + name: authz + cloud: + nacos: + discovery: + server-addr: nacos.seqdata.cn:8848 +server: + port: 30001 \ No newline at end of file diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml new file mode 100644 index 0000000..d2dc74e --- /dev/null +++ b/seqdata-cloud-gateway/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-gateway + 网关,提供路由功能和其它一些通过用功能(防重放攻击、url过滤、统一日志等等) + + + org.springframework.cloud + spring-cloud-starter-gateway + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + cn.seqdata.cloud + seqdata-cloud-log + ${project.version} + + + cn.seqdata.cloud + seqdata-cloud-auth-client + ${project.version} + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + ${nacos.version} + + + + + + diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java new file mode 100644 index 0000000..2a3cc53 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java @@ -0,0 +1,63 @@ +package cn.seqdata.gateway; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.RemoteTokenServices; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; + +import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; +import cn.seqdata.gateway.filter.ResourceServerProperties; +import cn.seqdata.gateway.swagger.OAuth2ClientProperties; +import cn.seqdata.oauth2.client.FeignAuthzClient; +import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; +import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; +import cn.seqdata.oauth2.client.security.PathPermissionEvaluatorImpl; + +/** + * Author: jrxian + * Date: 2020-02-14 01:03 + */ +@Configuration +@EnableFeignClients(basePackageClasses = FeignAuthzClient.class) +@EnableConfigurationProperties({OAuth2ClientProperties.class, ResourceServerProperties.class}) +public class AuthClientConfiguration { + /** + * 连接授权服务器 + */ + @Bean + public RemoteTokenServices remoteTokenServices(OAuth2ClientProperties credentials, ResourceServerProperties resource) { + EnhanceUserAuthenticationConverter authenticationConverter = new EnhanceUserAuthenticationConverter(); + authenticationConverter.setDefaultAuthorities(new String[]{"guest"}); + + DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); + tokenConverter.setUserTokenConverter(authenticationConverter); + + RemoteTokenServices services = new RemoteTokenServices(); + services.setAccessTokenConverter(tokenConverter); + + services.setClientId(credentials.getClientId()); + services.setClientSecret(credentials.getClientSecret()); + + services.setCheckTokenEndpointUrl(resource.getTokenInfoUri()); + + return services; + } + + /** + * 对授权服务器返回的accessToken做短期缓存 + */ + @Bean + @Primary + public ResourceServerTokenServices tokenServices(RemoteTokenServices tokenServices) { + return new CachedResourceServerTokenServices(tokenServices); + } + + @Bean + public PathPermissionEvaluator permissionEvaluator() { + return new PathPermissionEvaluatorImpl("url"); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java new file mode 100644 index 0000000..9e7e76e --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java @@ -0,0 +1,17 @@ +package cn.seqdata.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; + +/** + * Author: jrxian + * Date: 2019-10-02 19:11 + */ +@SpringBootApplication(exclude = ReactiveSecurityAutoConfiguration.class) +public class GatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(GatewayApplication.class, args); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java new file mode 100644 index 0000000..bd272e6 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -0,0 +1,47 @@ +package cn.seqdata.gateway; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import lombok.extern.slf4j.Slf4j; + +import cn.seqdata.gateway.filter.authc.PathMatcherGlobalFilter; +import cn.seqdata.gateway.filter.authc.WhitelistPredicate; +import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; +import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; +import cn.seqdata.log.LogRecorder; +import cn.seqdata.log.url.UrlRecord; +import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; + +/** + * Author: jrxian + * Date: 2020-02-07 21:09 + */ +@Slf4j +@Configuration +public class GatewayConfiguration { + + @Bean + public LogRecorder logRecorder() { + return record -> log.debug(String.valueOf(record)); + } + + @Bean + public LoggingGlobalFilter loggingGlobalFilter(LogRecorder logRecorder, ResourceServerTokenServices tokenServices) { + return new LoggingGlobalFilter(logRecorder, tokenServices); + } + + @Bean + public PathMatcherGlobalFilter pathMatcherGlobalFilter(ResourceServerTokenServices tokenServices, + PathPermissionEvaluator permissionEvaluator) { + return new PathMatcherGlobalFilter(new WhitelistPredicate(), tokenServices, permissionEvaluator); + } + + /** + * 对前端签名进行验证 + */ +// @Bean + public HmacVerifyGlobalFilter hmacVerifyGlobalFilter() { + return new HmacVerifyGlobalFilter(); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java new file mode 100644 index 0000000..f8eb3b4 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java @@ -0,0 +1,57 @@ +package cn.seqdata.gateway; + +import java.io.IOException; +import java.util.Collection; + +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.util.NumberUtils; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +/** + * Author: jrxian + * Date: 2020-02-11 01:20 + */ +@Configuration +public class JacksonConfiguration { + + @Bean + public Jackson2ObjectMapperBuilderCustomizer objectMapperBuilderCustomizer() { + return builder -> { + builder.deserializerByType(long.class, JacksonConfiguration.LongDeserializer.instance); + builder.deserializerByType(Long.class, JacksonConfiguration.LongDeserializer.instance); + }; + } + + @Bean + public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) { + return new MappingJackson2HttpMessageConverter(objectMapper); + } + + @Bean + public HttpMessageConverters httpMessageConverters(Collection> additionalConverters) { + return new HttpMessageConverters(additionalConverters); + } + + static final class LongDeserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + public static JacksonConfiguration.LongDeserializer instance = new JacksonConfiguration.LongDeserializer(); + + private LongDeserializer() { + super(Long.class); + } + + @Override + public Long deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + return NumberUtils.parseNumber(jp.getText(), Long.class); + } + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/BearerTokenResolver.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/BearerTokenResolver.java new file mode 100644 index 0000000..b90210f --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/BearerTokenResolver.java @@ -0,0 +1,11 @@ +package cn.seqdata.gateway.filter; + +import org.springframework.http.server.reactive.ServerHttpRequest; + +/** + * Author: jrxian + * Date: 2020-02-12 23:17 + */ +public interface BearerTokenResolver { + String resolve(ServerHttpRequest request); +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java new file mode 100644 index 0000000..82b61fd --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java @@ -0,0 +1,51 @@ +package cn.seqdata.gateway.filter; + +import java.util.concurrent.TimeUnit; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import lombok.AllArgsConstructor; + +/** + * 缓存token,避免高频访问授权服务器 + */ +@AllArgsConstructor +public class CachedResourceServerTokenServices implements ResourceServerTokenServices { + private final ResourceServerTokenServices delegate; + + private final LoadingCache authenticationCache = CacheBuilder.newBuilder() + .maximumSize(16) + .expireAfterAccess(1, TimeUnit.MINUTES) + .build(new CacheLoader() { + @Override + public OAuth2Authentication load(String key) { + return delegate.loadAuthentication(key); + } + }); + + private final LoadingCache accessTokenCache = CacheBuilder.newBuilder() + .maximumSize(16) + .expireAfterAccess(1, TimeUnit.MINUTES) + .build(new CacheLoader() { + @Override + public OAuth2AccessToken load(String key) { + return delegate.readAccessToken(key); + } + }); + + @Override + public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { + return authenticationCache.getUnchecked(accessToken); + } + + @Override + public OAuth2AccessToken readAccessToken(String accessToken) { + return accessTokenCache.getUnchecked(accessToken); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/DefaultBearerTokenResolver.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/DefaultBearerTokenResolver.java new file mode 100644 index 0000000..4a8b59d --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/DefaultBearerTokenResolver.java @@ -0,0 +1,38 @@ +package cn.seqdata.gateway.filter; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.StringUtils; +import org.springframework.web.server.ResponseStatusException; + +/** + * Author: jrxian + * Date: 2020-02-12 23:19 + */ +public class DefaultBearerTokenResolver implements BearerTokenResolver { + private static final Pattern authorizationPattern = Pattern.compile( + "^Bearer (?[a-zA-Z0-9-._~+/]+)=*$", + Pattern.CASE_INSENSITIVE); + + @Override + public String resolve(ServerHttpRequest request) { + HttpHeaders headers = request.getHeaders(); + String authorization = headers.getFirst(HttpHeaders.AUTHORIZATION); + + if(StringUtils.startsWithIgnoreCase(authorization, "bearer")) { + Matcher matcher = authorizationPattern.matcher(authorization); + + if(!matcher.matches()) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Bearer token is malformed"); + } + + return matcher.group("token"); + } + + return null; + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/ResourceServerProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/ResourceServerProperties.java new file mode 100644 index 0000000..64c9549 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/ResourceServerProperties.java @@ -0,0 +1,18 @@ +package cn.seqdata.gateway.filter; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Author: jrxian + * Date: 2020-02-13 16:41 + */ +@lombok.Getter +@lombok.Setter +@ConfigurationProperties(prefix = "security.oauth2.resource") +public class ResourceServerProperties { + private String userInfoUri; + + private String tokenInfoUri; + + private boolean preferTokenInfo = true; +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java new file mode 100644 index 0000000..2629e1a --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java @@ -0,0 +1,77 @@ +package cn.seqdata.gateway.filter.authc; + +import java.util.Objects; +import java.util.function.Predicate; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import lombok.AllArgsConstructor; +import reactor.core.publisher.Mono; + +import cn.seqdata.gateway.filter.BearerTokenResolver; +import cn.seqdata.gateway.filter.DefaultBearerTokenResolver; +import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; + +/** + * 根据url权限在网关统一拦截 + */ +@AllArgsConstructor +public class PathMatcherGlobalFilter implements GlobalFilter, Ordered { + private final BearerTokenResolver tokenResolver = new DefaultBearerTokenResolver(); + private final Predicate whitelistPredicate; + private final ResourceServerTokenServices tokenServices; + private final PathPermissionEvaluator permissionEvaluator; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + //缓存token,避免高频访问授权服务器 + ServerHttpRequest request = exchange.getRequest(); + ServerHttpResponse response = exchange.getResponse(); + RequestPath requestPath = request.getPath(); + + //白名单和OPTIONS,不需要做权限验证 + if(HttpMethod.OPTIONS.equals(request.getMethod()) || !whitelistPredicate.test(request)) { + //从HttpHeaders获取accessToken + String accessToken = tokenResolver.resolve(request); + if(Objects.isNull(accessToken)) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "To provide a valid accessToken"); + } + + try { + //从授权服务器获取Authentication + OAuth2Authentication auth2Authentication = tokenServices.loadAuthentication(accessToken); + + //由appid发起的,非前端页面访问 + if(!auth2Authentication.isClientOnly()) { + Authentication authentication = auth2Authentication.getUserAuthentication(); + + //没有权限,报403 + if(!permissionEvaluator.hasPermission(authentication, requestPath.value())) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, "URL access verification failed"); + } + } + } catch(InvalidTokenException ex) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, ex.getMessage()); + } + } + + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return -60; + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java new file mode 100644 index 0000000..90deb2e --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java @@ -0,0 +1,53 @@ +package cn.seqdata.gateway.filter.authc; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; + +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +/** + * Author: jrxian + * Date: 2020-02-12 23:49 + */ +public class WhitelistPredicate implements Predicate { + private static final PathMatcher pathMatcher = new AntPathMatcher(); + + private final List whitelists = new ArrayList<>(); + + public WhitelistPredicate() { + //authz: 授权服务器是否需要token由授权服务器决定,网关不做前置拦截 + whitelists.addAll(Arrays.asList("/favicon.ico", "/authz/**", + "/webjars/**", "/plugins/**", + "/swagger-ui.html", "/swagger-resources/**", + "/v2/api-docs/**", "/*/v2/api-docs/**")); + } + + public WhitelistPredicate(Collection whitelists) { + this(); + this.whitelists.addAll(whitelists); + } + + public WhitelistPredicate(String... whitelists) { + this(Arrays.asList(whitelists)); + } + + @Override + public boolean test(ServerHttpRequest request) { + RequestPath requestPath = request.getPath(); + String path = requestPath.value(); + + for(String whitelist : whitelists) { + if(pathMatcher.match(whitelist, path)) { + return true; + } + } + + return false; + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java new file mode 100644 index 0000000..76e2d62 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -0,0 +1,106 @@ +package cn.seqdata.gateway.filter.logging; + +import java.net.InetSocketAddress; +import java.util.Objects; +import java.util.StringJoiner; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +import cn.seqdata.gateway.filter.BearerTokenResolver; +import cn.seqdata.gateway.filter.DefaultBearerTokenResolver; +import cn.seqdata.log.LogRecorder; +import cn.seqdata.log.url.UrlRecord; + +/** + * Author: jrxian + * Date: 2020-02-13 08:44 + */ +@Slf4j +@AllArgsConstructor +public class LoggingGlobalFilter implements GlobalFilter, Ordered { + private final BearerTokenResolver tokenResolver = new DefaultBearerTokenResolver(); + private final LogRecorder logRecorder; + private final ResourceServerTokenServices tokenServices; + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + UrlRecord.UrlRecordBuilder builder = UrlRecord.builder(); + + buildRequst(builder, exchange.getRequest()); + + builder.start(System.currentTimeMillis()); + Mono returnObj = chain.filter(exchange); + builder.end(System.currentTimeMillis()); + + buildResponse(builder, exchange.getResponse()); + + logRecorder.accept(builder.build()); + + return returnObj; + } + + @Override + public int getOrder() { + return -90; + } + + private void buildRequst(UrlRecord.UrlRecordBuilder builder, ServerHttpRequest request) { + RequestPath requestPath = request.getPath(); + builder.method(request.getMethodValue()) + .path(requestPath.value()) + .queryParams(queryParams(request.getQueryParams())); + + InetSocketAddress remoteAddress = request.getRemoteAddress(); + if(Objects.nonNull(remoteAddress)) { + builder.host(remoteAddress.getHostString()) + .port(remoteAddress.getPort()); + } + + String accessToken = tokenResolver.resolve(request); + if(Objects.nonNull(accessToken)) { + builder.token(accessToken); + + try { + OAuth2Authentication authentication = tokenServices.loadAuthentication(accessToken); + Object principal = authentication.getPrincipal(); + builder.principal(String.valueOf(principal)); + } catch(RuntimeException ex) { + log.warn(ex.getMessage()); + } + } + } + + private void buildResponse(UrlRecord.UrlRecordBuilder builder, ServerHttpResponse response) { + HttpStatus statusCode = response.getStatusCode(); + if(Objects.nonNull(statusCode)) { + builder.code(statusCode.value()); + } + } + + private String queryParams(MultiValueMap queryParams) { + StringJoiner joiner = new StringJoiner("&"); + + queryParams.forEach((key, vals) -> { + if(Objects.nonNull(vals)) { + vals.forEach(val -> joiner.add(key + "=" + val)); + } else { + joiner.add(key); + } + }); + + return joiner.toString(); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java new file mode 100644 index 0000000..079fcba --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java @@ -0,0 +1,110 @@ +package cn.seqdata.gateway.filter.verify; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import javax.crypto.Mac; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.HmacAlgorithms; +import org.apache.commons.codec.digest.HmacUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import cn.seqdata.gateway.util.GatewayAssert; +import cn.seqdata.gateway.util.MultiMapUtils; + +/** + * Author: jrxian + * Date: 2020-02-07 15:56 + */ +public class HmacVerifyGlobalFilter implements GlobalFilter, Ordered { + public static final String xCa = "X-Ca-"; + //HMac算法 + public static final String xCaHmacAlgorithms = "Hmac-Algorithms"; + //默认HMac算法 + public static final String defHmacAlgorithms = HmacAlgorithms.HMAC_SHA_256.getName(); + //签名Header + public static final String xCaSignature = xCa + "Signature"; + //签名时间戳 + public static final String xCaTimestamp = xCa + "Timestamp"; + //请求放重放Nonce + public static final String xCaNonce = xCa + "Nonce"; + //APP KEY + public static final String xCaKey = xCa + "Key"; + + private final byte[] secret; + + public HmacVerifyGlobalFilter() { + this("seqdata@13572468".getBytes()); + } + + public HmacVerifyGlobalFilter(byte[] secret) { + this.secret = secret; + } + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + HttpHeaders headers = request.getHeaders(); + + String signature = GatewayAssert.requireNonNull(headers.getFirst(xCaSignature), "没有签名信息:" + xCaSignature); + + long timestamp = MultiMapUtils.getLong(headers, xCaTimestamp); + long delta = Math.abs(timestamp - System.currentTimeMillis()); + GatewayAssert.isTrue(delta < TimeUnit.MINUTES.toMillis(5), xCaTimestamp + "超过5分钟限制"); + + RequestPath requestPath = request.getPath(); + String hmacAlgorithms = MultiMapUtils.getOrDefault(headers, xCaHmacAlgorithms, defHmacAlgorithms); + + String[] xCaHeaderKeys = {xCaTimestamp, xCaNonce, xCaKey, ReplayDigestFunction.xCaDigest}; + SortedMap xCaHeaders = MultiMapUtils.filterKeys(headers, xCaHeaderKeys); + MultiValueMap params = request.getQueryParams(); + + String calcSign = hmac(hmacAlgorithms, secret, requestPath.value(), xCaHeaders, params); + GatewayAssert.isTrue(StringUtils.equalsIgnoreCase(signature, calcSign), "签名不符:" + xCaSignature); + + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return -80; + } + + private static String hmac(String algorithm, byte[] secret, String path, + Map headers, MultiValueMap queries) { + Mac mac = HmacUtils.getInitializedMac(algorithm, secret); + + HmacUtils.updateHmac(mac, path); + updateHmac(mac, headers); + updateHmac(mac, queries); + + return Base64.encodeBase64String(mac.doFinal()); + } + + private static void updateHmac(Mac mac, Collection values) { + new TreeSet<>(values).forEach(v -> HmacUtils.updateHmac(mac, v)); + } + + private static void updateHmac(Mac mac, Map values) { + new TreeMap<>(values).forEach((k, v) -> { + HmacUtils.updateHmac(mac, k); + HmacUtils.updateHmac(mac, v); + }); + } + + private static void updateHmac(Mac mac, MultiValueMap values) { + new TreeMap<>(values).forEach((k, v) -> { + HmacUtils.updateHmac(mac, k); + updateHmac(mac, v); + }); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/ReplayDigestFunction.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/ReplayDigestFunction.java new file mode 100644 index 0000000..1fb9d60 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/ReplayDigestFunction.java @@ -0,0 +1,49 @@ +package cn.seqdata.gateway.filter.verify; + +import java.security.MessageDigest; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.codec.digest.MessageDigestAlgorithms; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.reactivestreams.Publisher; +import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import cn.seqdata.gateway.util.GatewayAssert; + +/** + * Author: jrxian + * Date: 2020-02-08 00:49 + */ +public class ReplayDigestFunction implements RewriteFunction { + //摘要算法 + public static final String xCaDigestAlgorithms = HmacVerifyGlobalFilter.xCa + "Digest-Algorithms"; + //默认摘要算法 + public static final String defDigestAlgorithms = MessageDigestAlgorithms.SHA_256; + //对内容的签名 + public static final String xCaDigest = HmacVerifyGlobalFilter.xCa + "Digest"; + + @Override + public Publisher apply(ServerWebExchange exchange, byte[] bytes) { + ServerHttpRequest request = exchange.getRequest(); + HttpHeaders headers = request.getHeaders(); + + if(headers.getContentLength() > 0 && !MediaType.APPLICATION_FORM_URLENCODED.equals(headers.getContentType())) { + String digest = GatewayAssert.requireNonNull(headers.getFirst(xCaDigest), "没有摘要信息:" + xCaDigest); + + String digestAlgorithms = ObjectUtils.defaultIfNull(headers.getFirst(xCaDigestAlgorithms), defDigestAlgorithms); + MessageDigest messageDigest = DigestUtils.getDigest(digestAlgorithms); + + String calcDigest = Base64.encodeBase64String(messageDigest.digest(bytes)); + GatewayAssert.isTrue(StringUtils.equalsIgnoreCase(digest, calcDigest), "摘要不符:" + xCaDigest); + } + + return Mono.just(bytes); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java new file mode 100644 index 0000000..4b3325a --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java @@ -0,0 +1,18 @@ +package cn.seqdata.gateway.nacos; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; + +/** + * Author: jrxian + * Date: 2020-02-04 00:57 + */ +@Configuration +public class NacosConfiguration { + + @Bean + public NacosReactiveDiscoveryClient reactiveDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { + return new NacosReactiveDiscoveryClient(discoveryProperties); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java new file mode 100644 index 0000000..d3a1fb9 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java @@ -0,0 +1,55 @@ +package cn.seqdata.gateway.nacos; + +import java.util.List; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ListView; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Flux; + +/** + * Author: jrxian + * Date: 2020-02-04 00:52 + */ +@Slf4j +public class NacosReactiveDiscoveryClient implements ReactiveDiscoveryClient { + private final NacosDiscoveryProperties discoveryProperties; + + public NacosReactiveDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { + this.discoveryProperties = discoveryProperties; + } + + @Override + public String description() { + return "Spring Cloud Nacos Discovery Client"; + } + + @Override + public Flux getInstances(String serviceId) { + try { + String group = discoveryProperties.getGroup(); + List instances = discoveryProperties.namingServiceInstance() + .selectInstances(serviceId, group, true); + return Flux.fromIterable(NacosDiscoveryClient.hostToServiceInstanceList(instances, serviceId)); + } catch(Exception e) { + throw new RuntimeException("Can not get hosts from nacos server. serviceId: " + serviceId, e); + } + } + + @Override + public Flux getServices() { + try { + String group = discoveryProperties.getGroup(); + ListView services = discoveryProperties.namingServiceInstance() + .getServicesOfServer(1, Integer.MAX_VALUE, group); + return Flux.fromIterable(services.getData()); + } catch(Exception e) { + log.error("get service name from nacos server fail,", e); + return Flux.empty(); + } + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/OAuth2ClientProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/OAuth2ClientProperties.java new file mode 100644 index 0000000..be5b96f --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/OAuth2ClientProperties.java @@ -0,0 +1,23 @@ +package cn.seqdata.gateway.swagger; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Author: jrxian + * Date: 2020-02-04 09:36 + */ +@lombok.Getter +@lombok.Setter +@ConfigurationProperties(prefix = "security.oauth2.client") +public class OAuth2ClientProperties { + + /** + * OAuth2 client id. + */ + private String clientId; + + /** + * OAuth2 client secret. A random secret is generated by default. + */ + private String clientSecret; +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerController.java new file mode 100644 index 0000000..1462fc1 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerController.java @@ -0,0 +1,19 @@ +package cn.seqdata.gateway.swagger; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.reactive.result.view.RedirectView; + +/** + * 默认首页跳转到swagger调试页面 + */ +@Controller +@RequestMapping +public class SwaggerController { + + @GetMapping("/") + public RedirectView home() { + return new RedirectView("/swagger-ui.html"); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerHandler.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerHandler.java new file mode 100644 index 0000000..bdc860c --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerHandler.java @@ -0,0 +1,64 @@ +package cn.seqdata.gateway.swagger; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; +import springfox.documentation.swagger.web.*; + +/** + * 为适应spring 5.x的reactive模式,原servlet模式的swagger不再有效 + */ +@RestController +@RequestMapping("/swagger-resources") +public class SwaggerHandler { + private final SwaggerResourcesProvider swaggerResources; + private SecurityConfiguration securityConfiguration; + private UiConfiguration uiConfiguration; + + @Value("${spring.application.name:webapp}") + private String appName; + + @Autowired + public SwaggerHandler(OAuth2ClientProperties clientProperties, SwaggerResourcesProvider swaggerResources) { + this.swaggerResources = swaggerResources; + + securityConfiguration = SecurityConfigurationBuilder.builder() + .appName(appName) + .clientId(clientProperties.getClientId()) + .clientSecret(clientProperties.getClientSecret()) + .build(); + + uiConfiguration = UiConfigurationBuilder.builder() + .build(); + } + + @Autowired(required = false) + public void setSecurityConfiguration(SecurityConfiguration securityConfiguration) { + this.securityConfiguration = securityConfiguration; + } + + @Autowired(required = false) + public void setUiConfiguration(UiConfiguration uiConfiguration) { + this.uiConfiguration = uiConfiguration; + } + + @GetMapping("/configuration/security") + public Mono securityConfiguration() { + return Mono.just(securityConfiguration); + } + + @GetMapping("/configuration/ui") + public Mono uiConfiguration() { + return Mono.just(uiConfiguration); + } + + @GetMapping + public Mono> swaggerResources() { + return Mono.just(swaggerResources.get()); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java new file mode 100644 index 0000000..73aa2f9 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java @@ -0,0 +1,88 @@ +package cn.seqdata.gateway.swagger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.cloud.gateway.route.RouteDefinitionLocator; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; +import lombok.SneakyThrows; +import springfox.documentation.swagger.web.SwaggerResource; +import springfox.documentation.swagger.web.SwaggerResourcesProvider; + +/** + * Author: jrxian + * Date: 2020-02-03 22:50 + */ +@Primary +@Component +public class SwaggerProvider implements SwaggerResourcesProvider { + @Value("${spring.application.name:webapp}") + private String serviceId; + + private RouteDefinitionLocator locator; + + @Autowired + public void setLocator(RouteDefinitionLocator locator) { + this.locator = locator; + } + + @SneakyThrows({InterruptedException.class, ExecutionException.class}) + @Override + public List get() { + return CompletableFuture.supplyAsync(() -> { + List swaggerResources = new ArrayList<>(); + + locator.getRouteDefinitions() + .toStream() + //过滤掉网关本身 + .filter(route -> !StringUtils.equals(routeId(route), serviceId)) + .forEach(route -> { + Optional optional = route.getPredicates() + .stream() + .filter(predicateDefinition -> "Path".equals(predicateDefinition.getName())) + .findFirst(); + + if(optional.isPresent()) { + String routeId = routeId(route); + + PredicateDefinition predicate = optional.get(); + Map args = predicate.getArgs(); + String pattern = args.get("pattern"); + String location = StringUtils.replace(pattern, "/**", "/v2/api-docs"); + + swaggerResources.add(swaggerResource(routeId, location)); + } + }); + + return swaggerResources; + }) + .get(); + } + + private String routeId(RouteDefinition routeDefinition) { + return Optional.of(routeDefinition.getId()) + .map(x -> StringUtils.removeStart(x, "CompositeDiscoveryClient_")) + .map(x -> StringUtils.removeStart(x, "ReactiveCompositeDiscoveryClient_")) + .get(); + } + + private SwaggerResource swaggerResource(String name, String location) { + SwaggerResource swaggerResource = new SwaggerResource(); + + swaggerResource.setName(name); + swaggerResource.setSwaggerVersion("2.0"); + swaggerResource.setLocation(location); + + return swaggerResource; + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/GatewayAssert.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/GatewayAssert.java new file mode 100644 index 0000000..4537671 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/GatewayAssert.java @@ -0,0 +1,35 @@ +package cn.seqdata.gateway.util; + +import java.util.Objects; +import java.util.function.Supplier; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +/** + * Author: jrxian + * Date: 2020-02-08 01:56 + */ +public class GatewayAssert { + + public static T requireNonNull(T object, String message) { + if(Objects.isNull(object)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, message); + } + return object; + } + + public static T requireNonNull(T object, Supplier messageSupplier) { + return requireNonNull(object, messageSupplier.get()); + } + + public static void isTrue(boolean expression, String message) { + if(!expression) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, message); + } + } + + public static void isTrue(boolean expression, Supplier messageSupplier) { + isTrue(expression, messageSupplier.get()); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/MultiMapUtils.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/MultiMapUtils.java new file mode 100644 index 0000000..dfa5326 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/util/MultiMapUtils.java @@ -0,0 +1,38 @@ +package cn.seqdata.gateway.util; + +import java.util.Collections; +import java.util.Objects; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.util.MultiValueMap; + +/** + * Author: jrxian + * Date: 2020-02-08 01:32 + */ +public class MultiMapUtils { + + public static String getOrDefault(MultiValueMap multiMap, String key, String defaultValue) { + return CollectionUtils.lastElement(multiMap.getOrDefault(key, Collections.singletonList(defaultValue))); + } + + public static long getLong(MultiValueMap multiMap, String key) { + return NumberUtils.toLong(multiMap.getFirst(key)); + } + + public static SortedMap filterKeys(MultiValueMap multiMap, String... keys) { + SortedMap filterMap = new TreeMap<>(); + + for(String key : keys) { + String value = multiMap.getFirst(key); + if(Objects.nonNull(value)) { + filterMap.put(key, value); + } + } + + return filterMap; + } +} diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml new file mode 100644 index 0000000..6eb4f14 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -0,0 +1,15 @@ +spring: + profiles: + active: jackson +security: + oauth2: + client: + client-id: client + client-secret: secret + resource: + token-info-uri: http://localhost:30001/oauth/check_token +server: + port: 8080 +logging: + level: + root: info \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..f98cd50 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +spring: + application: + name: gateway + cloud: + gateway: + discovery: + locator: + enabled: true + nacos: + config: + server-addr: nacos.seqdata.cn:8848 + discovery: + server-addr: nacos.seqdata.cn:8848 +server: + port: 30000 \ No newline at end of file -- Gitee From cf2c25a66ae23bdf10038240240580532d5bc859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 4 Mar 2020 00:13:19 +0800 Subject: [PATCH 002/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 24 +++++++++++++++++++ .../src/main/docker/Dockerfile | 4 ++++ .../src/main/docker/Dockerfile | 4 ++++ .../src/main/docker/Dockerfile | 4 ++++ 4 files changed, 36 insertions(+) create mode 100644 seqdata-cloud-authc/src/main/docker/Dockerfile create mode 100644 seqdata-cloud-authz/src/main/docker/Dockerfile create mode 100644 seqdata-cloud-gateway/src/main/docker/Dockerfile diff --git a/pom.xml b/pom.xml index 536badf..4a9be1b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,30 @@ org.springframework.boot spring-boot-maven-plugin + + com.spotify + docker-maven-plugin + 1.2.2 + + + package + + build + + + + + ${project.artifactId} + ${project.basedir}/src/main/docker + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + diff --git a/seqdata-cloud-authc/src/main/docker/Dockerfile b/seqdata-cloud-authc/src/main/docker/Dockerfile new file mode 100644 index 0000000..09c1a30 --- /dev/null +++ b/seqdata-cloud-authc/src/main/docker/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ADD seqdata-cloud-authc-2.2.1-SNAPSHOT.jar myapp.jar +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/docker/Dockerfile b/seqdata-cloud-authz/src/main/docker/Dockerfile new file mode 100644 index 0000000..793d223 --- /dev/null +++ b/seqdata-cloud-authz/src/main/docker/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ADD seqdata-cloud-authz-2.2.1-SNAPSHOT.jar myapp.jar +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/docker/Dockerfile b/seqdata-cloud-gateway/src/main/docker/Dockerfile new file mode 100644 index 0000000..4c23386 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/docker/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +ADD seqdata-cloud-gateway-2.2.1-SNAPSHOT.jar myapp.jar +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file -- Gitee From a18910f393a169f5d9328d4ba9c1ac6ef2dc68fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 4 Mar 2020 17:05:51 +0800 Subject: [PATCH 003/228] +docker-compose.yml --- docker-compose.yml | 48 ++++++++++++++++++++++++++++++++++++++++++++++ pom.xml | 1 + 2 files changed, 49 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5741b27 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,48 @@ +version: '3' +services: + nacos: + container_name: nacos + image: nacos/nacos-server:latest + restart: always + environment: + PREFER_HOST_MODE: hostname + MODE: standalone + ports: + - '8848:8848' + redis: + container_name: redis + image: redis:latest + restart: always + expose: + - 6379 + rabbitmq: + container_name: rabbitmq + image: rabbitmq:3-management + restart: always + expose: + - 5672 + ports: + - '15672:15672' + gateway: + container_name: gateway + image: seqdata-cloud-gateway + ports: + - '8080:8080' + links: + - nacos + environment: + - server.port=8080 + - spring.cloud.nacos.config.server-addr=nacos:8848 + - spring.cloud.nacos.discovery.server-addr=nacos:8848 + authz: + container_name: authz + image: seqdata-cloud-authz + expose: + - 8080 + links: + - nacos + - redis + environment: + - server.port=8080 + - spring.cloud.nacos.config.server-addr=nacos:8848 + - spring.cloud.nacos.discovery.server-addr=nacos:8848 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4a9be1b..3d2ce72 100644 --- a/pom.xml +++ b/pom.xml @@ -90,6 +90,7 @@ ${project.artifactId} ${project.basedir}/src/main/docker + http://dev.voltmao.com:14775 / -- Gitee From 65a1bf5ffba1b3a0c36a2b211bec1836f3e87aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 4 Mar 2020 18:37:45 +0800 Subject: [PATCH 004/228] *docker-compose.yml + authc --- docker-compose.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5741b27..849d240 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,8 +28,6 @@ services: image: seqdata-cloud-gateway ports: - '8080:8080' - links: - - nacos environment: - server.port=8080 - spring.cloud.nacos.config.server-addr=nacos:8848 @@ -39,9 +37,15 @@ services: image: seqdata-cloud-authz expose: - 8080 - links: - - nacos - - redis + environment: + - server.port=8080 + - spring.cloud.nacos.config.server-addr=nacos:8848 + - spring.cloud.nacos.discovery.server-addr=nacos:8848 + authc: + container_name: authc + image: seqdata-cloud-authc + expose: + - 8080 environment: - server.port=8080 - spring.cloud.nacos.config.server-addr=nacos:8848 -- Gitee From 4781bc71dea4efa97bd10c9404085b4dec80a23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 5 Mar 2020 10:56:32 +0800 Subject: [PATCH 005/228] +@EnableDiscoveryClient --- pom.xml | 5 +++++ .../main/java/cn/seqdata/oauth2/ResServerApplication.java | 2 ++ seqdata-cloud-authc/src/main/resources/bootstrap.yml | 3 +++ .../java/cn/seqdata/oauth2/AuthzServerApplication.java | 2 ++ seqdata-cloud-authz/src/main/resources/application.yml | 6 +----- seqdata-cloud-authz/src/main/resources/bootstrap.yml | 3 +++ .../main/java/cn/seqdata/gateway/GatewayApplication.java | 2 ++ seqdata-cloud-gateway/src/main/resources/application.yml | 7 ++----- seqdata-cloud-gateway/src/main/resources/bootstrap.yml | 1 + 9 files changed, 21 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index 3d2ce72..e9ec2cd 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,11 @@ spring-cloud-starter-alibaba-nacos-discovery ${nacos.version} + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + ${nacos.version} + io.springfox diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java index b82082e..d037c18 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java @@ -2,6 +2,7 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; import cn.seqdata.oauth2.client.FeignAuthzClient; @@ -11,6 +12,7 @@ import cn.seqdata.oauth2.client.FeignAuthzClient; * Date: 2020-01-20 00:37 */ @SpringBootApplication +@EnableDiscoveryClient @EnableFeignClients(basePackageClasses = FeignAuthzClient.class) public class ResServerApplication { diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml index e8ed434..33272d1 100644 --- a/seqdata-cloud-authc/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authc/src/main/resources/bootstrap.yml @@ -3,6 +3,9 @@ spring: name: authc cloud: nacos: + config: + server-addr: nacos.seqdata.cn:8848 + file-extension: yml discovery: server-addr: nacos.seqdata.cn:8848 server: diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index c9e61fc..03fdde7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -2,6 +2,7 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** @@ -9,6 +10,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; * Date: 2019-10-02 19:11 */ @SpringBootApplication +@EnableDiscoveryClient @EnableSwagger2 public class AuthzServerApplication { diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 115a7bf..51c4b51 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -1,10 +1,6 @@ -logging: - level: - root: info -# org.springframework.security: debug spring: profiles: - active: jackson, jpa + active: jackson, jpa, log datasource: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://cloud.seqdata.cn:1433;DatabaseName=seqdata-cloud diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index 95a7554..b06fcb6 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -3,6 +3,9 @@ spring: name: authz cloud: nacos: + config: + server-addr: nacos.seqdata.cn:8848 + file-extension: yml discovery: server-addr: nacos.seqdata.cn:8848 server: diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java index 9e7e76e..830ee83 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayApplication.java @@ -3,12 +3,14 @@ package cn.seqdata.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * Author: jrxian * Date: 2019-10-02 19:11 */ @SpringBootApplication(exclude = ReactiveSecurityAutoConfiguration.class) +@EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 6eb4f14..5dc8dd5 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: profiles: - active: jackson + active: jackson, log security: oauth2: client: @@ -9,7 +9,4 @@ security: resource: token-info-uri: http://localhost:30001/oauth/check_token server: - port: 8080 -logging: - level: - root: info \ No newline at end of file + port: 8080 \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index f98cd50..2fa5362 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -9,6 +9,7 @@ spring: nacos: config: server-addr: nacos.seqdata.cn:8848 + file-extension: yml discovery: server-addr: nacos.seqdata.cn:8848 server: -- Gitee From bdf7a6f2bd77cbe79f9cff71c3585b9894d9bd32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 5 Mar 2020 23:34:37 +0800 Subject: [PATCH 006/228] Repos ==> Repo --- .../oauth2/AuthzServerConfiguration.java | 6 ++--- .../oauth2/controller/ConnectController.java | 6 ++--- .../seqdata/oauth2/service/UserService.java | 22 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index de3bef5..1021356 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -37,7 +37,7 @@ import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; import cn.seqdata.oauth2.mobile.MobileNonceService; import cn.seqdata.oauth2.mobile.MobileTokenGranter; -import cn.seqdata.oauth2.repos.oauth.ClientDetailRepos; +import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; import cn.seqdata.oauth2.service.UserService; @@ -61,7 +61,7 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt @Autowired private UserService userService; @Autowired - private ClientDetailRepos clientDetailRepos; + private ClientDetailRepo clientDetailRepo; @Autowired(required = false) public MobileNonceService mobileNonceService; @@ -126,7 +126,7 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.withClientDetails(new JpaClientDetailsService(clientDetailRepos)); + clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); // InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); // builder.withClient("client") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 4b1edff..8ccf1dc 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -14,7 +14,7 @@ import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; -import cn.seqdata.oauth2.repos.oauth.UserDetailRepos; +import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.util.SecurityUtils; /** @@ -26,7 +26,7 @@ import cn.seqdata.oauth2.util.SecurityUtils; @AllArgsConstructor public class ConnectController { private final ClientRegistrationRepository repository; - private final UserDetailRepos userDetailRepos; + private final UserDetailRepo userDetailRepo; /** * 向手机发送一次性验证码 @@ -60,6 +60,6 @@ public class ConnectController { @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) { UserDetailId entityId = new UserDetailId(registrationId, principal.getName()); - userDetailRepos.deleteById(entityId); + userDetailRepo.deleteById(entityId); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 6109523..0d514ff 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -20,8 +20,8 @@ import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.provider.UserProfile; -import cn.seqdata.oauth2.repos.oauth.UserDetailRepos; -import cn.seqdata.oauth2.repos.rbac.UserRepos; +import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; +import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.wechat.WechatUserProfile; /** @@ -35,8 +35,8 @@ public class UserService { private final TokenStore tokenStore; private final UserDetailsService userDetailsService; private final JpaUserDetailsManager userDetailsManager; - private final UserRepos userRepos; - private final UserDetailRepos userDetailRepos; + private final UserRepo userRepo; + private final UserDetailRepo userDetailRepo; /** * 将第三方认证获取的用户信息转换为本系统的 User @@ -44,14 +44,14 @@ public class UserService { @Transactional public User createUser(String clientId, UserProfile userProfile) { UserDetailId entityId = new UserDetailId(clientId, userProfile.getName()); - Optional optional = userDetailRepos.findById(entityId); + Optional optional = userDetailRepo.findById(entityId); UserDetail userDetail = new UserDetail(entityId); //用户不存在 if(!optional.isPresent()) { //查找微信的unionid,如果找到,视为同一个用户 if(userProfile instanceof WechatUserProfile) { - User user = userRepos.findByUnionId(((WechatUserProfile) userProfile).getUnionId()); + User user = userRepo.findByUnionId(((WechatUserProfile) userProfile).getUnionId()); if(Objects.nonNull(user) && Objects.nonNull(user.getId())) { userDetail.setUserId(user.getId()); } @@ -61,11 +61,11 @@ public class UserService { if(Objects.isNull(userDetail.getUserId())) { //保存并且获取到保存后的userId User user = convert(userProfile); - user = userRepos.save(user); + user = userRepo.save(user); userDetail.setUserId(user.getId()); } - userDetail = userDetailRepos.save(userDetail); + userDetail = userDetailRepo.save(userDetail); } else { userDetail = optional.get(); } @@ -76,10 +76,10 @@ public class UserService { public User loadUser(String clientId, String username) { UserDetailId entityId = new UserDetailId(clientId, username); //先从oauth_user_details中查找用户 - User user = userDetailRepos.findById(entityId) + User user = userDetailRepo.findById(entityId) .map(UserDetail::getUser) //未找到,再从oauth_user中查找用户 - .orElse(userRepos.findByUsername(username)); + .orElse(userRepo.findByUsername(username)); if(Objects.nonNull(user)) { long userId = Objects.requireNonNull(user.getId()); @@ -97,7 +97,7 @@ public class UserService { UserDetailId entityId = new UserDetailId(clientId, username); UserDetail entity = new UserDetail(entityId); entity.setUserId(user.getId()); - userDetailRepos.save(entity); + userDetailRepo.save(entity); } /** -- Gitee From b37e9c60750a74392076cfb2f3837a02a4c764f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 16 Mar 2020 21:46:03 +0800 Subject: [PATCH 007/228] =?UTF-8?q?=E6=94=AF=E6=8C=81cors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 7 +++++++ seqdata-cloud-gateway/src/main/resources/application.yml | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 849d240..bf2669a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,6 +26,8 @@ services: gateway: container_name: gateway image: seqdata-cloud-gateway + depends_on: + - nacos ports: - '8080:8080' environment: @@ -35,6 +37,9 @@ services: authz: container_name: authz image: seqdata-cloud-authz + depends_on: + - nacos + - redis expose: - 8080 environment: @@ -44,6 +49,8 @@ services: authc: container_name: authc image: seqdata-cloud-authc + depends_on: + - nacos expose: - 8080 environment: diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 5dc8dd5..1849ff0 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -1,6 +1,14 @@ spring: profiles: active: jackson, log + cloud: + gateway: + globalcors: + cors-configurations: + '[/**]': + allowed-origins: "*" + allowed-headers: "*" + allowed-methods: GET, PUT, POST, DELETE, OPTIONS security: oauth2: client: @@ -8,5 +16,6 @@ security: client-secret: secret resource: token-info-uri: http://localhost:30001/oauth/check_token + prefer-token-info: true server: port: 8080 \ No newline at end of file -- Gitee From e7e5ff93223ed798e3ab98c8d25d61cf87752d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 22 Mar 2020 11:57:34 +0800 Subject: [PATCH 008/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0router=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 2 + .../oauth2/AuthzServerConfiguration.java | 4 +- .../oauth2/controller/ConnectController.java | 21 +++- ...Controller.java => CurrentController.java} | 46 ++++---- .../controller/OAuth2CodeController.java | 6 +- .../oauth2/controller/RouterController.java | 107 ++++++++++++++++++ .../cn/seqdata/oauth2/util/SecurityUtils.java | 105 ++++++++++++++--- .../src/main/resources/application.yml | 5 +- 8 files changed, 249 insertions(+), 47 deletions(-) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/{UserInfoController.java => CurrentController.java} (60%) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java diff --git a/docker-compose.yml b/docker-compose.yml index bf2669a..96451fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,6 +44,7 @@ services: - 8080 environment: - server.port=8080 + - spring.redis.host=redis - spring.cloud.nacos.config.server-addr=nacos:8848 - spring.cloud.nacos.discovery.server-addr=nacos:8848 authc: @@ -55,5 +56,6 @@ services: - 8080 environment: - server.port=8080 + - security.oauth2.resource.token-info-uri=http://authz:8080/oauth/check_token - spring.cloud.nacos.config.server-addr=nacos:8848 - spring.cloud.nacos.discovery.server-addr=nacos:8848 \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 1021356..aa64c03 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -101,8 +101,8 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt if(accessToken instanceof DefaultOAuth2AccessToken) { Map attributes = new HashMap<>(); - String clientId = SecurityUtils.getClientId(authentication); - String username = SecurityUtils.getUsername(authentication); + String clientId = SecurityUtils.clientId(authentication); + String username = SecurityUtils.username(authentication); User user = userService.loadUser(clientId, username); if(Objects.nonNull(user)) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 8ccf1dc..faf78b3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -2,6 +2,7 @@ package cn.seqdata.oauth2.controller; import java.net.URI; import java.security.Principal; +import java.util.Objects; import javax.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; @@ -39,22 +40,30 @@ public class ConnectController { @GetMapping(value = "/{registrationId}") public ResponseEntity connect(@PathVariable String registrationId, HttpServletRequest request) { ClientRegistration registration = repository.findByRegistrationId(registrationId); - OAuth2Provider provider = SecurityUtils.getProvider(registration); + OAuth2Provider provider = SecurityUtils.provider(registration); OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); //从 request 中分离出 accessToken - String accessToken = SecurityUtils.getAccessToken(request.getUserPrincipal()); + String accessToken = SecurityUtils.accessToken(request.getUserPrincipal()); //重定向 URI,返回信息由 OAuth2CodeController 负责接收 - String redirectURL = String.valueOf((request.getRequestURL())); - redirectURL = redirectURL.replace("/connect", "/oauth/code"); + String redirectURL = registration.getRedirectUriTemplate(); + if(Objects.isNull(redirectURL)) { + redirectURL = String.valueOf((request.getRequestURL())); + redirectURL = redirectURL.replace("/connect", "/oauth/code"); + } //将 accessToken 以 state 的形式传送出去,返回后用来判断被绑定的用户 URI authorizeURI = oAuth2Template.buildAuthorizeURI(accessToken, redirectURL); return ResponseEntity.status(HttpStatus.SEE_OTHER) - .location(authorizeURI) - .build(); + . + + location(authorizeURI) + . + + build(); + } @DeleteMapping(value = "/{registrationId}") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java similarity index 60% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index fe2c042..55c7996 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -2,13 +2,10 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; -import java.util.Collections; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -16,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController; import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.repos.perm.ActionPermRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; @@ -23,11 +21,12 @@ import cn.seqdata.oauth2.util.SecurityUtils; * Author: jrxian * Date: 2020-01-27 17:16 */ -@Api("授权服务器保持的用户信息") +@Api("当前登录用户信息") @RestController -@RequestMapping("/user") +@RequestMapping("/current") @AllArgsConstructor -public class UserInfoController { +public class CurrentController { + private final ActionPermRepo actionPermRepo; private final TokenStore tokenStore; private final UserService userService; @@ -37,27 +36,34 @@ public class UserInfoController { return principal; } + @ApiOperation("用户信息") + @GetMapping("/user") + public User info(Principal principal) { + String clientId = SecurityUtils.clientId(principal); + String username = SecurityUtils.username(principal); + return userService.loadUser(clientId, username); + } + @ApiOperation("令牌") @GetMapping("/token") public OAuth2AccessToken token(Principal principal) { - String tokenValue = SecurityUtils.getAccessToken(principal); + String tokenValue = SecurityUtils.accessToken(principal); return tokenStore.readAccessToken(tokenValue); } - @ApiOperation("用户信息") - @GetMapping("/info") - public User info(Principal principal) { - String clientId = SecurityUtils.getClientId(principal); - String username = SecurityUtils.getUsername(principal); - return userService.loadUser(clientId, username); - } - @ApiOperation("拥有角色") @GetMapping("/authorities") - public Collection authorities(Principal principal) { - if(principal instanceof OAuth2Authentication) { - return ((OAuth2Authentication) principal).getAuthorities(); - } - return Collections.emptyList(); + public Collection authorities(Principal principal) { + return SecurityUtils.authorities(principal); } + +// @ApiOperation("页面内按钮") +// @GetMapping("/view/{id}/actions") +// public Collection actions(@PathVariable("id") long id, +// Principal principal, @SortDefault("orderNo") Sort sort) { +// return actionPermRepo.findByViewPermId(id, sort) +// .stream() +// .map(JpaIdentifiable::getIdentifier) +// .collect(Collectors.toList()); +// } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 7d99a3b..489af27 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -56,7 +56,7 @@ public class OAuth2CodeController { @RequestParam(OAuth2ParameterNames.CODE) String code, @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { ClientRegistration registration = repository.findByRegistrationId(registrationId); - OAuth2Provider provider = SecurityUtils.getProvider(registration); + OAuth2Provider provider = SecurityUtils.provider(registration); OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); //从远程拉取用户信息,以 registrationId + remoteUser.name 作为唯一识别符 @@ -68,8 +68,8 @@ public class OAuth2CodeController { OAuth2Authentication authentication = tokenStore.readAuthentication(state); if(Objects.nonNull(authentication)) { //获取当前用户身份 - String localClientId = SecurityUtils.getClientId(authentication); - String localUsername = SecurityUtils.getUsername(authentication); + String localClientId = SecurityUtils.clientId(authentication); + String localUsername = SecurityUtils.username(authentication); User user = userService.loadUser(localClientId, localUsername); userService.bindUser(registrationId, authentication.getName(), user); } else { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java new file mode 100644 index 0000000..35425b3 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -0,0 +1,107 @@ +package cn.seqdata.oauth2.controller; + +import java.security.Principal; +import java.util.Collection; +import java.util.Objects; +import java.util.stream.Collectors; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.SortDefault; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import lombok.AllArgsConstructor; + +import cn.seqdata.antd.MenuDataItem; +import cn.seqdata.oauth2.jpa.perm.JpaPermission; +import cn.seqdata.oauth2.jpa.perm.ModulePermission; +import cn.seqdata.oauth2.jpa.perm.SysPermission; +import cn.seqdata.oauth2.jpa.perm.ViewPermission; +import cn.seqdata.oauth2.repos.perm.ModulePermRepo; +import cn.seqdata.oauth2.repos.perm.SysPermRepo; +import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; + +/** + * Author: jrxian + * Date: 2020-03-22 10:04 + */ +@Api("路由信息") +@RestController +@RequestMapping("/router") +@AllArgsConstructor +public class RouterController { + private final AuthorityPermissionRepo permissionRepo; + private final SysPermRepo sysPermRepo; + private final ModulePermRepo modulePermRepo; + + @ApiOperation("全部路由") + @GetMapping("/all") + public Collection allRouters(Principal principal, @SortDefault("orderNo") Sort sort) { + Collection routers = topRouters(principal, sort); + routers.forEach(router -> router.children.addAll(childRouters(toLong(router.key), principal, sort))); + return routers; + } + + @ApiOperation("一级路由") + @GetMapping("/top") + public Collection topRouters(Principal principal, @SortDefault("orderNo") Sort sort) { + return sysPermRepo.findAll(sort) + .stream() + .map(this::toRouter) + .collect(Collectors.toList()); + } + + @ApiOperation("多级路由") + @GetMapping("/{id}/children") + public Collection childRouters(@PathVariable("id") long id, + Principal principal, @SortDefault("orderNo") Sort sort) { + return modulePermRepo.findBySysPermId(id, sort) + .stream() + .map(this::toRouter) + .collect(Collectors.toList()); + } + + private MenuDataItem toRouter(SysPermission sysPerm) { + MenuDataItem item = createRouter(sysPerm); + + item.path = String.format("/%s", sysPerm.getIdentifier()); + + return item; + } + + private MenuDataItem toRouter(ModulePermission modulePerm) { + MenuDataItem item = createRouter(modulePerm); + + item.icon = modulePerm.getIcon(); + ViewPermission viewPerm = modulePerm.getViewPerm(); + if(Objects.nonNull(viewPerm)) { + item.path = viewPerm.getIdentifier(); + item.component = viewPerm.getComponent(); + } else { + SysPermission sysPerm = modulePerm.getSysPerm(); + item.path = String.format("/%s/%s", sysPerm.getIdentifier(), modulePerm.getIdentifier()); + } + + return item; + } + + private MenuDataItem createRouter(JpaPermission permission) { + MenuDataItem item = new MenuDataItem(); + + item.key = permission.getId(); + item.name = permission.getName(); + + return item; + } + + private long toLong(Object key) { + if(key instanceof Number) { + return ((Number) key).longValue(); + } else { + return 0L; + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index 785847d..e8f5f96 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -1,7 +1,12 @@ package cn.seqdata.oauth2.util; import java.security.Principal; +import java.util.*; +import java.util.stream.Collectors; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; @@ -17,7 +22,26 @@ public final class SecurityUtils { private SecurityUtils() { } - public static String getClientId(Principal principal) { + public static OAuth2Provider provider(ClientRegistration registration) { + ClientRegistration.ProviderDetails details = registration.getProviderDetails(); + return OAuth2Provider.matcher(details.getTokenUri()); + } + + /** + * 根据用户获取oauth2的token + */ + public static String accessToken(Principal principal) { + if(principal instanceof OAuth2Authentication) { + Object details = ((OAuth2Authentication) principal).getDetails(); + if(details instanceof OAuth2AuthenticationDetails) { + return ((OAuth2AuthenticationDetails) details).getTokenValue(); + } + } + + return null; + } + + public static String clientId(Principal principal) { if(principal instanceof OAuth2Authentication) { OAuth2Request auth2Request = ((OAuth2Authentication) principal).getOAuth2Request(); return auth2Request.getClientId(); @@ -26,26 +50,79 @@ public final class SecurityUtils { return null; } - public static String getUsername(Principal principal) { + /** + * 用户名 + */ + public static String username(Principal principal) { return principal.getName(); } /** - * 根据用户获取oauth2的token + * 用户编号 */ - public static String getAccessToken(Principal principal) { - if(principal instanceof OAuth2Authentication) { - Object details = ((OAuth2Authentication) principal).getDetails(); - if(details instanceof OAuth2AuthenticationDetails) { - return ((OAuth2AuthenticationDetails) details).getTokenValue(); - } - } + public static Long userId(Principal principal) { + return toLong(detailValue(details(principal), "user_id")); + } - return null; + /** + * 所属部门编号 + */ + public static Long deptId(Principal principal) { + return toLong(detailValue(details(principal), "dept_id")); } - public static OAuth2Provider getProvider(ClientRegistration registration) { - ClientRegistration.ProviderDetails details = registration.getProviderDetails(); - return OAuth2Provider.matcher(details.getTokenUri()); + /** + * 所属单位编号 + */ + public static Long orgId(Principal principal) { + return toLong(detailValue(details(principal), "org_id")); + } + + /** + * 拥有角色 + */ + public static Collection grantedAuthorities(Principal principal) { + return Optional.ofNullable((OAuth2Authentication) principal) + .map(OAuth2Authentication::getUserAuthentication) + .map(Authentication::getAuthorities) + .orElse(Collections.emptySet()); + } + + /** + * 拥有角色名称 + */ + public static Set authorities(Principal principal) { + return mapAuthorities(grantedAuthorities(principal)); + } + + private static Set mapAuthorities(Collection authorities) { + return authorities.stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toSet()); + } + + private static Object details(Principal principal) { + return Optional.ofNullable((OAuth2Authentication) principal) + .map(OAuth2Authentication::getUserAuthentication) + .map(Authentication::getDetails) + .orElse(null); + } + + private static Object detailValue(Object details, String key) { + if(details instanceof Map) { + return ((Map) details).get(key); + } else { + return null; + } + } + + private static Long toLong(Object value) { + if(Objects.isNull(value)) return null; + + if(value instanceof Number) { + return ((Number) value).longValue(); + } else { + return NumberUtils.toLong((String) value); + } } } diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 51c4b51..e66a0cc 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -15,8 +15,9 @@ spring: registration: github: provider: github - client-id: f55188e56a9af49701c4 - client-secret: 1968a44d0b5270f65faf854d10d43e9f1dded62e + client-id: 4c086b8877a0fd770388 + client-secret: 5a5ecca781d83675bacee68a1e3b94262576bdf7 + redirect-uri: http://localhost:8080/authz/oauth/code/github wechat: provider: wechat client-id: wxa71a478c9d3b8826 -- Gitee From 69de70b9a599c45ccf7e343da0d50d2306db9fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 24 Mar 2020 00:15:16 +0800 Subject: [PATCH 009/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9router?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/RouterController.java | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index 35425b3..6597221 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -10,12 +10,11 @@ import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Sort; import org.springframework.data.web.SortDefault; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import lombok.AllArgsConstructor; -import cn.seqdata.antd.MenuDataItem; +import cn.seqdata.antd.Router; import cn.seqdata.oauth2.jpa.perm.JpaPermission; import cn.seqdata.oauth2.jpa.perm.ModulePermission; import cn.seqdata.oauth2.jpa.perm.SysPermission; @@ -37,64 +36,64 @@ public class RouterController { private final SysPermRepo sysPermRepo; private final ModulePermRepo modulePermRepo; - @ApiOperation("全部路由") - @GetMapping("/all") - public Collection allRouters(Principal principal, @SortDefault("orderNo") Sort sort) { - Collection routers = topRouters(principal, sort); - routers.forEach(router -> router.children.addAll(childRouters(toLong(router.key), principal, sort))); + @ApiOperation("路由") + @GetMapping + public Collection routers(Principal principal, @SortDefault("orderNo") Sort sort) { + Collection routers = topRouters(principal, sort); + routers.forEach(router -> router.children.addAll(childRouters(router, principal, sort))); return routers; } - @ApiOperation("一级路由") - @GetMapping("/top") - public Collection topRouters(Principal principal, @SortDefault("orderNo") Sort sort) { + private Collection topRouters(Principal principal, Sort sort) { return sysPermRepo.findAll(sort) .stream() .map(this::toRouter) .collect(Collectors.toList()); } - @ApiOperation("多级路由") - @GetMapping("/{id}/children") - public Collection childRouters(@PathVariable("id") long id, - Principal principal, @SortDefault("orderNo") Sort sort) { - return modulePermRepo.findBySysPermId(id, sort) + private Collection childRouters(Router parent, Principal principal, Sort sort) { + return modulePermRepo.findBySysPermId(toLong(parent.key), sort) .stream() - .map(this::toRouter) + .map(modulePerm -> toRouter(parent, modulePerm)) .collect(Collectors.toList()); } - private MenuDataItem toRouter(SysPermission sysPerm) { - MenuDataItem item = createRouter(sysPerm); + private Router toRouter(SysPermission sysPerm) { + Router router = createRouter(null, sysPerm); - item.path = String.format("/%s", sysPerm.getIdentifier()); + router.component = "RouteView"; + router.meta.authorities.addAll(sysPerm.getScopes()); - return item; + return router; } - private MenuDataItem toRouter(ModulePermission modulePerm) { - MenuDataItem item = createRouter(modulePerm); + private Router toRouter(Router parent, ModulePermission modulePerm) { + Router router = createRouter(parent, modulePerm); + + router.meta.icon = modulePerm.getIcon(); + router.meta.params = modulePerm.getParams(); + router.meta.query = modulePerm.getQuery(); + router.meta.authorities.addAll(modulePerm.getScopes()); - item.icon = modulePerm.getIcon(); ViewPermission viewPerm = modulePerm.getViewPerm(); if(Objects.nonNull(viewPerm)) { - item.path = viewPerm.getIdentifier(); - item.component = viewPerm.getComponent(); + router.component = viewPerm.getComponent(); + router.meta.keepAlive = viewPerm.getKeepAlive(); } else { - SysPermission sysPerm = modulePerm.getSysPerm(); - item.path = String.format("/%s/%s", sysPerm.getIdentifier(), modulePerm.getIdentifier()); + router.redirect = modulePerm.getRedirect(); } - return item; + return router; } - private MenuDataItem createRouter(JpaPermission permission) { - MenuDataItem item = new MenuDataItem(); + private Router createRouter(Router parent, JpaPermission permission) { + Router router = new Router(parent); - item.key = permission.getId(); - item.name = permission.getName(); + router.key = permission.getId(); + router.name = permission.getIdentifier(); + router.meta.title = permission.getName(); - return item; + return router; } private long toLong(Object key) { -- Gitee From b32971917358c53c8a4e120711d93769923fa4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 24 Mar 2020 00:33:40 +0800 Subject: [PATCH 010/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/service/UserService.java | 6 +----- .../gateway/filter/authc/PathMatcherGlobalFilter.java | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 0d514ff..cffe0af 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -4,14 +4,12 @@ import java.util.Collections; import java.util.Objects; import java.util.Optional; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.AllArgsConstructor; @@ -31,10 +29,8 @@ import cn.seqdata.oauth2.wechat.WechatUserProfile; @Service @AllArgsConstructor public class UserService { - private final AuthorizationServerTokenServices tokenServices; - private final TokenStore tokenStore; - private final UserDetailsService userDetailsService; private final JpaUserDetailsManager userDetailsManager; + private final AuthorizationServerTokenServices tokenServices; private final UserRepo userRepo; private final UserDetailRepo userDetailRepo; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java index 2629e1a..f469c41 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java @@ -36,13 +36,12 @@ public class PathMatcherGlobalFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - //缓存token,避免高频访问授权服务器 ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); RequestPath requestPath = request.getPath(); //白名单和OPTIONS,不需要做权限验证 - if(HttpMethod.OPTIONS.equals(request.getMethod()) || !whitelistPredicate.test(request)) { + if(!HttpMethod.OPTIONS.equals(request.getMethod()) || !whitelistPredicate.test(request)) { //从HttpHeaders获取accessToken String accessToken = tokenResolver.resolve(request); if(Objects.isNull(accessToken)) { -- Gitee From e2015a62ffa5315e36fc50327af5370d2930be8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 24 Mar 2020 00:49:10 +0800 Subject: [PATCH 011/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3gateway=E5=89=8D?= =?UTF-8?q?=E7=BD=AEtoken=E6=8B=A6=E6=88=AA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java | 3 +-- .../cn/seqdata/gateway/filter/authc/WhitelistPredicate.java | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java index f469c41..f36dc74 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java @@ -6,7 +6,6 @@ import java.util.function.Predicate; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; -import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -41,7 +40,7 @@ public class PathMatcherGlobalFilter implements GlobalFilter, Ordered { RequestPath requestPath = request.getPath(); //白名单和OPTIONS,不需要做权限验证 - if(!HttpMethod.OPTIONS.equals(request.getMethod()) || !whitelistPredicate.test(request)) { + if(!whitelistPredicate.test(request)) { //从HttpHeaders获取accessToken String accessToken = tokenResolver.resolve(request); if(Objects.isNull(accessToken)) { diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java index 90deb2e..2bea12d 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java @@ -23,9 +23,7 @@ public class WhitelistPredicate implements Predicate { public WhitelistPredicate() { //authz: 授权服务器是否需要token由授权服务器决定,网关不做前置拦截 whitelists.addAll(Arrays.asList("/favicon.ico", "/authz/**", - "/webjars/**", "/plugins/**", - "/swagger-ui.html", "/swagger-resources/**", - "/v2/api-docs/**", "/*/v2/api-docs/**")); + "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/*/v2/api-docs/**")); } public WhitelistPredicate(Collection whitelists) { -- Gitee From b727982baa8431e8754c089c1603eface5542185 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 27 Mar 2020 20:25:02 +0800 Subject: [PATCH 012/228] +JpaEnumerated --- .../java/cn/seqdata/oauth2/ResourceServerConfiguration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java index 13db4e1..c60d8b4 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java @@ -46,7 +46,8 @@ public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter @Override public void configure(HttpSecurity http) throws Exception { - http.csrf(); + http.csrf() + .disable(); http.authorizeRequests() .antMatchers("/v2/api-docs/**", "/swagger-resources/**") -- Gitee From 2eaf4af4e684a9914a75e3be4c401b063197d450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 29 Mar 2020 16:37:09 +0800 Subject: [PATCH 013/228] =?UTF-8?q?=E6=9B=B4=E6=96=B0gateway=E7=9A=84cors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 5 +++-- seqdata-cloud-gateway/src/main/resources/application.yml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 96451fc..bc427b3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,6 +34,7 @@ services: - server.port=8080 - spring.cloud.nacos.config.server-addr=nacos:8848 - spring.cloud.nacos.discovery.server-addr=nacos:8848 + - security.oauth2.resource.token-info-uri=http://authz:8080/oauth/check_token authz: container_name: authz image: seqdata-cloud-authz @@ -56,6 +57,6 @@ services: - 8080 environment: - server.port=8080 - - security.oauth2.resource.token-info-uri=http://authz:8080/oauth/check_token - spring.cloud.nacos.config.server-addr=nacos:8848 - - spring.cloud.nacos.discovery.server-addr=nacos:8848 \ No newline at end of file + - spring.cloud.nacos.discovery.server-addr=nacos:8848 + - security.oauth2.resource.token-info-uri=http://authz:8080/oauth/check_token diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 1849ff0..04c6d6c 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -6,6 +6,7 @@ spring: globalcors: cors-configurations: '[/**]': + allow-credentials: true allowed-origins: "*" allowed-headers: "*" allowed-methods: GET, PUT, POST, DELETE, OPTIONS -- Gitee From 0d6b29a8f4c77d5d2c45cd0a43bc1e0f14ffbc9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 7 Apr 2020 10:19:39 +0800 Subject: [PATCH 014/228] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 50 ++++--------------- .../seqdata/oauth2/MobileConfiguration.java | 27 ++++++++++ .../oauth2/controller/ConnectController.java | 2 +- .../controller/OAuth2CodeController.java | 4 +- .../seqdata/oauth2/service/TokenService.java | 35 +++++++++++++ .../seqdata/oauth2/service/UserService.java | 25 +--------- 6 files changed, 79 insertions(+), 64 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index aa64c03..e911dba 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -2,7 +2,6 @@ package cn.seqdata.oauth2; import java.util.*; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -10,7 +9,6 @@ import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; @@ -31,7 +29,7 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; -import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; @@ -49,35 +47,14 @@ import cn.seqdata.oauth2.util.SecurityUtils; */ @Configuration @EnableAuthorizationServer +@AllArgsConstructor public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { - @Autowired - private ObjectMapper objectMapper; - @Autowired - private RedisConnectionFactory redisConnectionFactory; - @Autowired - private AuthenticationManager authenticationManager; - @Autowired - private InMemoryClientRegistrationRepository registrationRepository; - @Autowired - private UserService userService; - @Autowired - private ClientDetailRepo clientDetailRepo; - @Autowired(required = false) - public MobileNonceService mobileNonceService; - - @Bean - public MobileNonceService mobileNonceService() { - return new MobileNonceService() { - @Override - public void generate(String mobile, int expire) { - } - - @Override - public boolean check(String mobile, String nonce) { - return true; - } - }; - } + private final AuthenticationManager authenticationManager; + private final UserService userService; + private final JpaUserDetailsManager userDetailsService; + private final RedisConnectionFactory redisConnectionFactory; + private final ClientDetailRepo clientDetailRepo; + private final MobileNonceService mobileNonceService; @Bean public PasswordEncoder passwordEncoder() { @@ -85,11 +62,6 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt // return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } - @Bean - public JpaUserDetailsManager userDetailsManager() { - return new JpaUserDetailsManager(); - } - @Bean public TokenStore tokenStore() { return new RedisTokenStore(redisConnectionFactory); @@ -148,11 +120,10 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt endpoints .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) .authenticationManager(authenticationManager) - .userDetailsService(userDetailsManager()) + .userDetailsService(userDetailsService) .tokenStore(tokenStore()) .tokenEnhancer(tokenEnhancer()) - .tokenGranter(tokenGranter(endpoints)) - ; + .tokenGranter(tokenGranter(endpoints)); } /** @@ -169,6 +140,7 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + //用户名密码验证 if(Objects.nonNull(authenticationManager)) { tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java new file mode 100644 index 0000000..4ce7ae8 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java @@ -0,0 +1,27 @@ +package cn.seqdata.oauth2; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import cn.seqdata.oauth2.mobile.MobileNonceService; + +/** + * Author: jrxian + * Date: 2020-04-07 10:17 + */ +@Configuration +public class MobileConfiguration { + @Bean + public MobileNonceService mobileNonceService() { + return new MobileNonceService() { + @Override + public void generate(String mobile, int expire) { + } + + @Override + public boolean check(String mobile, String nonce) { + return true; + } + }; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index faf78b3..5b3724f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -33,7 +33,7 @@ public class ConnectController { * 向手机发送一次性验证码 */ @GetMapping(value = "/mobile") - public ResponseEntity mobile(@RequestParam String mobile, HttpServletRequest request) { + public ResponseEntity mobile(String mobile, HttpServletRequest request) { return ResponseEntity.ok("已经成功的将验证码发送到您的手机,请注意查收!"); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 489af27..cf1866f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -21,6 +21,7 @@ import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.provider.UserProfile; +import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; @@ -35,6 +36,7 @@ public class OAuth2CodeController { private final ClientRegistrationRepository repository; private final TokenStore tokenStore; private final UserService userService; + private final TokenService tokenService; /** * 修改用户名和密码 @@ -77,7 +79,7 @@ public class OAuth2CodeController { userService.createUser(registrationId, userProfile); } - return userService.createToken(registrationId, remoteUser); + return tokenService.createToken(registrationId, remoteUser); } @ApiOperation("authorization_code的错误回调函数,处理error, error_description, error_uri") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java new file mode 100644 index 0000000..58c28b1 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java @@ -0,0 +1,35 @@ +package cn.seqdata.oauth2.service; + +import java.util.Collections; + +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.stereotype.Service; +import lombok.AllArgsConstructor; + +/** + * Author: jrxian + * Date: 2020-04-07 10:11 + */ +@Service +@AllArgsConstructor +public class TokenService { + private final AuthorizationServerTokenServices tokenServices; + + /** + * 为第三方认证的用户创建本系统的 token + */ + public OAuth2AccessToken createToken(String clientId, OAuth2User principal) { + OAuth2Request oAuth2Request = new OAuth2Request(Collections.emptyMap(), clientId, + Collections.emptySet(), true, Collections.emptySet(), + Collections.emptySet(), null, null, null); + + OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(), clientId); + OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); + return tokenServices.createAccessToken(oAuth2Authentication); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index cffe0af..ea924ff 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -1,15 +1,8 @@ package cn.seqdata.oauth2.service; -import java.util.Collections; import java.util.Objects; import java.util.Optional; -import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import lombok.AllArgsConstructor; @@ -29,8 +22,7 @@ import cn.seqdata.oauth2.wechat.WechatUserProfile; @Service @AllArgsConstructor public class UserService { - private final JpaUserDetailsManager userDetailsManager; - private final AuthorizationServerTokenServices tokenServices; + private final AuthorityService authorityService; private final UserRepo userRepo; private final UserDetailRepo userDetailRepo; @@ -79,7 +71,7 @@ public class UserService { if(Objects.nonNull(user)) { long userId = Objects.requireNonNull(user.getId()); - user.setAuthorities(userDetailsManager.loadAuthorities(userId)); + user.setAuthorities(authorityService.loadAuthorities(userId)); } return user; @@ -96,19 +88,6 @@ public class UserService { userDetailRepo.save(entity); } - /** - * 为第三方认证的用户创建本系统的 token - */ - public OAuth2AccessToken createToken(String clientId, OAuth2User principal) { - OAuth2Request oAuth2Request = new OAuth2Request(Collections.emptyMap(), clientId, - Collections.emptySet(), true, Collections.emptySet(), - Collections.emptySet(), null, null, null); - - OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(), clientId); - OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); - return tokenServices.createAccessToken(oAuth2Authentication); - } - /** * 根据UserProfile创建User */ -- Gitee From dea9bbad70fcef8b269c6f2264d2b4a7bb10c73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 7 Apr 2020 11:16:13 +0800 Subject: [PATCH 015/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=B3=A8=E9=94=80?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/controller/CurrentController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 55c7996..b175c9e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -7,6 +7,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -57,6 +58,12 @@ public class CurrentController { return SecurityUtils.authorities(principal); } + @ApiOperation("注销") + @DeleteMapping + public void logout(Principal principal) { + tokenStore.removeAccessToken(token(principal)); + } + // @ApiOperation("页面内按钮") // @GetMapping("/view/{id}/actions") // public Collection actions(@PathVariable("id") long id, -- Gitee From 70e5d003338f07f6a8a3ea78e183185b0bddb0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 20 Apr 2020 08:29:56 +0800 Subject: [PATCH 016/228] /perm/{xxx}/types ==> /type/perm/{xxx} --- .../oauth2/controller/OAuth2CodeController.java | 10 ++++++++-- .../src/main/resources/application.yml | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index cf1866f..9b1c5e2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -5,6 +5,7 @@ import java.util.Objects; import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.common.OAuth2AccessToken; @@ -15,6 +16,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; +import com.google.common.net.HttpHeaders; import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; @@ -54,7 +56,7 @@ public class OAuth2CodeController { @ApiOperation("authorization_code的回调函数,处理code和state") @GetMapping(value = "/{registrationId}", params = OAuth2ParameterNames.CODE) - public OAuth2AccessToken bind(@PathVariable String registrationId, + public ResponseEntity bind(@PathVariable String registrationId, @RequestParam(OAuth2ParameterNames.CODE) String code, @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { ClientRegistration registration = repository.findByRegistrationId(registrationId); @@ -79,7 +81,11 @@ public class OAuth2CodeController { userService.createUser(registrationId, userProfile); } - return tokenService.createToken(registrationId, remoteUser); + //要求提供跨域信息 + return ResponseEntity.status(HttpStatus.OK) + .header(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .header(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString()) + .body(tokenService.createToken(registrationId, remoteUser)); } @ApiOperation("authorization_code的错误回调函数,处理error, error_description, error_uri") diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 04c6d6c..c0b127d 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -9,7 +9,7 @@ spring: allow-credentials: true allowed-origins: "*" allowed-headers: "*" - allowed-methods: GET, PUT, POST, DELETE, OPTIONS + allowed-methods: GET, PUT, POST, DELETE security: oauth2: client: -- Gitee From e97e12020ff61f327ddc0f3a8b0c4f39e64826e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 8 May 2020 20:45:40 +0800 Subject: [PATCH 017/228] =?UTF-8?q?Router=E5=A2=9E=E5=8A=A0=E6=9D=83?= =?UTF-8?q?=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../oauth2/controller/RouterController.java | 56 +++--------------- .../oauth2/service/PermissionPredicate.java | 46 +++++++++++++++ .../cn/seqdata/oauth2/util/RouterUtils.java | 57 +++++++++++++++++++ 4 files changed, 112 insertions(+), 49 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java diff --git a/pom.xml b/pom.xml index e9ec2cd..207bd27 100644 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ 1.2.2 - package + install build diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index 6597221..ec4dead 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -2,11 +2,9 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; -import java.util.Objects; import java.util.stream.Collectors; import io.swagger.annotations.Api; -import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Sort; import org.springframework.data.web.SortDefault; import org.springframework.web.bind.annotation.GetMapping; @@ -15,13 +13,12 @@ import org.springframework.web.bind.annotation.RestController; import lombok.AllArgsConstructor; import cn.seqdata.antd.Router; -import cn.seqdata.oauth2.jpa.perm.JpaPermission; -import cn.seqdata.oauth2.jpa.perm.ModulePermission; -import cn.seqdata.oauth2.jpa.perm.SysPermission; -import cn.seqdata.oauth2.jpa.perm.ViewPermission; +import cn.seqdata.oauth2.domain.PermissionType; import cn.seqdata.oauth2.repos.perm.ModulePermRepo; import cn.seqdata.oauth2.repos.perm.SysPermRepo; import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; +import cn.seqdata.oauth2.service.PermissionPredicate; +import cn.seqdata.oauth2.util.RouterUtils; /** * Author: jrxian @@ -32,11 +29,10 @@ import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; @RequestMapping("/router") @AllArgsConstructor public class RouterController { - private final AuthorityPermissionRepo permissionRepo; private final SysPermRepo sysPermRepo; private final ModulePermRepo modulePermRepo; + private final AuthorityPermissionRepo permissionRepo; - @ApiOperation("路由") @GetMapping public Collection routers(Principal principal, @SortDefault("orderNo") Sort sort) { Collection routers = topRouters(principal, sort); @@ -47,55 +43,19 @@ public class RouterController { private Collection topRouters(Principal principal, Sort sort) { return sysPermRepo.findAll(sort) .stream() - .map(this::toRouter) + .filter(new PermissionPredicate(permissionRepo, PermissionType.sys, principal)) + .map(RouterUtils::toRouter) .collect(Collectors.toList()); } private Collection childRouters(Router parent, Principal principal, Sort sort) { return modulePermRepo.findBySysPermId(toLong(parent.key), sort) .stream() - .map(modulePerm -> toRouter(parent, modulePerm)) + .filter(new PermissionPredicate(permissionRepo, PermissionType.module, principal)) + .map(modulePerm -> RouterUtils.toRouter(parent, modulePerm)) .collect(Collectors.toList()); } - private Router toRouter(SysPermission sysPerm) { - Router router = createRouter(null, sysPerm); - - router.component = "RouteView"; - router.meta.authorities.addAll(sysPerm.getScopes()); - - return router; - } - - private Router toRouter(Router parent, ModulePermission modulePerm) { - Router router = createRouter(parent, modulePerm); - - router.meta.icon = modulePerm.getIcon(); - router.meta.params = modulePerm.getParams(); - router.meta.query = modulePerm.getQuery(); - router.meta.authorities.addAll(modulePerm.getScopes()); - - ViewPermission viewPerm = modulePerm.getViewPerm(); - if(Objects.nonNull(viewPerm)) { - router.component = viewPerm.getComponent(); - router.meta.keepAlive = viewPerm.getKeepAlive(); - } else { - router.redirect = modulePerm.getRedirect(); - } - - return router; - } - - private Router createRouter(Router parent, JpaPermission permission) { - Router router = new Router(parent); - - router.key = permission.getId(); - router.name = permission.getIdentifier(); - router.meta.title = permission.getName(); - - return router; - } - private long toLong(Object key) { if(key instanceof Number) { return ((Number) key).longValue(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java new file mode 100644 index 0000000..f2431dc --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java @@ -0,0 +1,46 @@ +package cn.seqdata.oauth2.service; + +import java.security.Principal; +import java.util.Collections; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import cn.seqdata.core.jpa.JpaIdGenerated; +import cn.seqdata.oauth2.domain.PermissionType; +import cn.seqdata.oauth2.jpa.perm.JpaPermission; +import cn.seqdata.oauth2.jpa.rbac.AuthorityPermission; +import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * @author jrxian + * @date 2020-05-08 20:21 + */ +public class PermissionPredicate implements Predicate { + private final boolean sysAdmin; + private final Set permissions; + + public PermissionPredicate(AuthorityPermissionRepo permissionRepo, PermissionType permType, Principal principal) { + Set authorities = SecurityUtils.authorities(principal); + sysAdmin = authorities.contains("sysAdmin"); + if(sysAdmin) { + permissions = Collections.emptySet(); + } else { + permissions = fetchPermissions(permissionRepo, permType, authorities); + } + } + + @Override + public boolean test(JpaPermission permission) { + return sysAdmin || permissions.contains(permission.getId()); + } + + private Set fetchPermissions(AuthorityPermissionRepo permissionRepo, PermissionType permType, Set authorities) { + return permissionRepo.findByAuthorityIdentifierInAndPermissionType(authorities, permType) + .stream() + .map(AuthorityPermission::getPermission) + .map(JpaIdGenerated::getId) + .collect(Collectors.toSet()); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java new file mode 100644 index 0000000..4cfcfa3 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java @@ -0,0 +1,57 @@ +package cn.seqdata.oauth2.util; + +import java.util.Objects; + +import cn.seqdata.antd.Router; +import cn.seqdata.oauth2.jpa.perm.JpaPermission; +import cn.seqdata.oauth2.jpa.perm.ModulePermission; +import cn.seqdata.oauth2.jpa.perm.SysPermission; +import cn.seqdata.oauth2.jpa.perm.ViewPermission; + +/** + * @author jrxian + * @date 2020-05-08 20:17 + */ + +public final class RouterUtils { + private RouterUtils() { + } + + public static Router toRouter(SysPermission sysPerm) { + Router router = createRouter(null, sysPerm); + + router.component = "RouteView"; + router.meta.authorities.addAll(sysPerm.getScopes()); + + return router; + } + + public static Router toRouter(Router parent, ModulePermission modulePerm) { + Router router = createRouter(parent, modulePerm); + + router.meta.icon = modulePerm.getIcon(); + router.meta.params = modulePerm.getParams(); + router.meta.query = modulePerm.getQuery(); + router.meta.authorities.addAll(modulePerm.getScopes()); + + ViewPermission viewPerm = modulePerm.getViewPerm(); + if(Objects.nonNull(viewPerm)) { + router.component = viewPerm.getComponent(); + router.meta.keepAlive = viewPerm.getKeepAlive(); + } else { + router.redirect = modulePerm.getRedirect(); + } + + return router; + } + + public static Router createRouter(Router parent, JpaPermission permission) { + Router router = new Router(parent); + + router.key = permission.getId(); + router.name = permission.getIdentifier(); + router.meta.title = permission.getName(); + + return router; + } +} -- Gitee From 22ff27a08dab20acf1f3707e8bb391c5432d4ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 10 May 2020 21:08:00 +0800 Subject: [PATCH 018/228] =?UTF-8?q?=E5=8D=87=E7=BA=A7nacos=E5=88=B02.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../gateway/nacos/NacosConfiguration.java | 18 ------ .../nacos/NacosReactiveDiscoveryClient.java | 55 ------------------- .../src/main/resources/bootstrap.yml | 4 +- 4 files changed, 2 insertions(+), 77 deletions(-) delete mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java delete mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java diff --git a/pom.xml b/pom.xml index 207bd27..30723fc 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ 1.8 Hoxton.SR1 2.9.2 - 2.1.1.RELEASE + 2.2.1.RELEASE seqdata-cloud-gateway diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java deleted file mode 100644 index 4b3325a..0000000 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.seqdata.gateway.nacos; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import com.alibaba.cloud.nacos.NacosDiscoveryProperties; - -/** - * Author: jrxian - * Date: 2020-02-04 00:57 - */ -@Configuration -public class NacosConfiguration { - - @Bean - public NacosReactiveDiscoveryClient reactiveDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { - return new NacosReactiveDiscoveryClient(discoveryProperties); - } -} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java deleted file mode 100644 index d3a1fb9..0000000 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/nacos/NacosReactiveDiscoveryClient.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.seqdata.gateway.nacos; - -import java.util.List; - -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient; -import com.alibaba.cloud.nacos.NacosDiscoveryProperties; -import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ListView; -import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Flux; - -/** - * Author: jrxian - * Date: 2020-02-04 00:52 - */ -@Slf4j -public class NacosReactiveDiscoveryClient implements ReactiveDiscoveryClient { - private final NacosDiscoveryProperties discoveryProperties; - - public NacosReactiveDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { - this.discoveryProperties = discoveryProperties; - } - - @Override - public String description() { - return "Spring Cloud Nacos Discovery Client"; - } - - @Override - public Flux getInstances(String serviceId) { - try { - String group = discoveryProperties.getGroup(); - List instances = discoveryProperties.namingServiceInstance() - .selectInstances(serviceId, group, true); - return Flux.fromIterable(NacosDiscoveryClient.hostToServiceInstanceList(instances, serviceId)); - } catch(Exception e) { - throw new RuntimeException("Can not get hosts from nacos server. serviceId: " + serviceId, e); - } - } - - @Override - public Flux getServices() { - try { - String group = discoveryProperties.getGroup(); - ListView services = discoveryProperties.namingServiceInstance() - .getServicesOfServer(1, Integer.MAX_VALUE, group); - return Flux.fromIterable(services.getData()); - } catch(Exception e) { - log.error("get service name from nacos server fail,", e); - return Flux.empty(); - } - } -} diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index 2fa5362..02780cf 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -11,6 +11,4 @@ spring: server-addr: nacos.seqdata.cn:8848 file-extension: yml discovery: - server-addr: nacos.seqdata.cn:8848 -server: - port: 30000 \ No newline at end of file + server-addr: nacos.seqdata.cn:8848 \ No newline at end of file -- Gitee From efb583b00b2ec0fdfb328de56965e7a034707af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 25 May 2020 08:10:04 +0800 Subject: [PATCH 019/228] =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=8E=92=E5=BA=8F?= =?UTF-8?q?=E8=A7=84=E5=88=99=EF=BC=8C=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- seqdata-cloud-authc/pom.xml | 2 +- .../cn/seqdata/oauth2/GlobalCorsConfig.java | 2 +- .../oauth2/MethodSecurityConfiguration.java | 6 +- seqdata-cloud-authz/pom.xml | 2 +- .../oauth2/provider/OAuth2Provider.java | 12 ++-- seqdata-cloud-gateway/pom.xml | 2 +- .../filter/verify/HmacVerifyGlobalFilter.java | 58 +++++++++---------- 8 files changed, 43 insertions(+), 43 deletions(-) diff --git a/pom.xml b/pom.xml index 30723fc..c8aa125 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,5 @@ - 4.0.0 diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml index 43baf1a..ccc8453 100644 --- a/seqdata-cloud-authc/pom.xml +++ b/seqdata-cloud-authc/pom.xml @@ -1,5 +1,5 @@ - 4.0.0 diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java index 6a347da..f77b098 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java @@ -11,7 +11,7 @@ import org.springframework.web.filter.CorsFilter; * Date: 2020-02-04 12:53 */ @Configuration -public class GlobalCorsConfig { +public class GlobalCorsConfig { @Bean public CorsFilter corsFilter() { diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java index 649a0cc..5c69acf 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java @@ -27,6 +27,9 @@ import cn.seqdata.oauth2.client.security.*; @EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration { + @Autowired + private ControllerMethodVoter controllerMethodVoter; + @Bean @Primary public MethodSecurityMetadataSource methodSecurityMetadataSource() { @@ -54,9 +57,6 @@ public class MethodSecurityConfiguration extends GlobalMethodSecurityConfigurati return new ControllerMethodVoter(permissionEvaluator); } - @Autowired - private ControllerMethodVoter controllerMethodVoter; - @Override protected AccessDecisionManager accessDecisionManager() { AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager(); diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 2464294..15ee45f 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -1,5 +1,5 @@ - 4.0.0 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java index 9b650e1..75cd5d2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java @@ -72,12 +72,6 @@ public enum OAuth2Provider { this.profiler = profiler; } - public abstract ClientRegistration.Builder getBuilder(String registrationId); - - public OAuth2Template createOAuth2Template(ClientRegistration registration) { - return new OAuth2Template(registration); - } - public static OAuth2Provider fromString(String providerId) { for(OAuth2Provider value : values()) { if(0 == StringUtils.compareIgnoreCase(value.name(), providerId)) { @@ -97,4 +91,10 @@ public enum OAuth2Provider { return DEFAULT; } + + public abstract ClientRegistration.Builder getBuilder(String registrationId); + + public OAuth2Template createOAuth2Template(ClientRegistration registration) { + return new OAuth2Template(registration); + } } diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index d2dc74e..db3c0ca 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -1,5 +1,5 @@ - 4.0.0 diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java index 079fcba..3eece88 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java @@ -50,35 +50,6 @@ public class HmacVerifyGlobalFilter implements GlobalFilter, Ordered { this.secret = secret; } - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - ServerHttpRequest request = exchange.getRequest(); - HttpHeaders headers = request.getHeaders(); - - String signature = GatewayAssert.requireNonNull(headers.getFirst(xCaSignature), "没有签名信息:" + xCaSignature); - - long timestamp = MultiMapUtils.getLong(headers, xCaTimestamp); - long delta = Math.abs(timestamp - System.currentTimeMillis()); - GatewayAssert.isTrue(delta < TimeUnit.MINUTES.toMillis(5), xCaTimestamp + "超过5分钟限制"); - - RequestPath requestPath = request.getPath(); - String hmacAlgorithms = MultiMapUtils.getOrDefault(headers, xCaHmacAlgorithms, defHmacAlgorithms); - - String[] xCaHeaderKeys = {xCaTimestamp, xCaNonce, xCaKey, ReplayDigestFunction.xCaDigest}; - SortedMap xCaHeaders = MultiMapUtils.filterKeys(headers, xCaHeaderKeys); - MultiValueMap params = request.getQueryParams(); - - String calcSign = hmac(hmacAlgorithms, secret, requestPath.value(), xCaHeaders, params); - GatewayAssert.isTrue(StringUtils.equalsIgnoreCase(signature, calcSign), "签名不符:" + xCaSignature); - - return chain.filter(exchange); - } - - @Override - public int getOrder() { - return -80; - } - private static String hmac(String algorithm, byte[] secret, String path, Map headers, MultiValueMap queries) { Mac mac = HmacUtils.getInitializedMac(algorithm, secret); @@ -107,4 +78,33 @@ public class HmacVerifyGlobalFilter implements GlobalFilter, Ordered { updateHmac(mac, v); }); } + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + HttpHeaders headers = request.getHeaders(); + + String signature = GatewayAssert.requireNonNull(headers.getFirst(xCaSignature), "没有签名信息:" + xCaSignature); + + long timestamp = MultiMapUtils.getLong(headers, xCaTimestamp); + long delta = Math.abs(timestamp - System.currentTimeMillis()); + GatewayAssert.isTrue(delta < TimeUnit.MINUTES.toMillis(5), xCaTimestamp + "超过5分钟限制"); + + RequestPath requestPath = request.getPath(); + String hmacAlgorithms = MultiMapUtils.getOrDefault(headers, xCaHmacAlgorithms, defHmacAlgorithms); + + String[] xCaHeaderKeys = {xCaTimestamp, xCaNonce, xCaKey, ReplayDigestFunction.xCaDigest}; + SortedMap xCaHeaders = MultiMapUtils.filterKeys(headers, xCaHeaderKeys); + MultiValueMap params = request.getQueryParams(); + + String calcSign = hmac(hmacAlgorithms, secret, requestPath.value(), xCaHeaders, params); + GatewayAssert.isTrue(StringUtils.equalsIgnoreCase(signature, calcSign), "签名不符:" + xCaSignature); + + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return -80; + } } -- Gitee From cf8de44af6635ce61db45d71ec7256a6b0e99e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 26 May 2020 08:44:19 +0800 Subject: [PATCH 020/228] =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=9D=83=E9=99=90?= =?UTF-8?q?=E7=9A=84=E6=97=B6=E5=80=99=E6=8A=8Avisible=E7=94=A8=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/service/PermissionPredicate.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java index f2431dc..f77eb72 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java @@ -6,6 +6,8 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.apache.commons.lang3.BooleanUtils; + import cn.seqdata.core.jpa.JpaIdGenerated; import cn.seqdata.oauth2.domain.PermissionType; import cn.seqdata.oauth2.jpa.perm.JpaPermission; @@ -33,9 +35,13 @@ public class PermissionPredicate implements Predicate { @Override public boolean test(JpaPermission permission) { - return sysAdmin || permissions.contains(permission.getId()); + //系统管理员,模块不需要权限,是否包含所需权限 + return sysAdmin || BooleanUtils.isTrue(permission.getVisible()) || permissions.contains(permission.getId()); } + /** + * 根据角色列表获取所有可操作的权限 + */ private Set fetchPermissions(AuthorityPermissionRepo permissionRepo, PermissionType permType, Set authorities) { return permissionRepo.findByAuthorityIdentifierInAndPermissionType(authorities, permType) .stream() -- Gitee From 1762d41621131031e3c378dabda7e8dc0b59f278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 6 Jun 2020 20:47:31 +0800 Subject: [PATCH 021/228] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E5=88=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 11 ++--- .../oauth2/controller/CurrentController.java | 45 ++++++++----------- .../controller/OAuth2CodeController.java | 15 ------- .../controller/OAuth2TokenController.java | 36 +++++++++++++++ seqdata-cloud-gateway/pom.xml | 4 ++ .../seqdata/gateway/GatewayConfiguration.java | 11 +++++ .../CachedResourceServerTokenServices.java | 4 +- 7 files changed, 75 insertions(+), 51 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 5b3724f..925e902 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -34,7 +34,7 @@ public class ConnectController { */ @GetMapping(value = "/mobile") public ResponseEntity mobile(String mobile, HttpServletRequest request) { - return ResponseEntity.ok("已经成功的将验证码发送到您的手机,请注意查收!"); + return ResponseEntity.ok("已经将验证码发送到您的手机,请注意查收!"); } @GetMapping(value = "/{registrationId}") @@ -57,13 +57,8 @@ public class ConnectController { URI authorizeURI = oAuth2Template.buildAuthorizeURI(accessToken, redirectURL); return ResponseEntity.status(HttpStatus.SEE_OTHER) - . - - location(authorizeURI) - . - - build(); - + .location(authorizeURI) + .build(); } @DeleteMapping(value = "/{registrationId}") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index b175c9e..a0b5aaa 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -5,12 +5,8 @@ import java.util.Collection; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; +import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; @@ -28,8 +24,8 @@ import cn.seqdata.oauth2.util.SecurityUtils; @AllArgsConstructor public class CurrentController { private final ActionPermRepo actionPermRepo; - private final TokenStore tokenStore; private final UserService userService; + private final ConsumerTokenServices tokenServices; @ApiOperation("通行证") @GetMapping("/principal") @@ -45,32 +41,29 @@ public class CurrentController { return userService.loadUser(clientId, username); } - @ApiOperation("令牌") - @GetMapping("/token") - public OAuth2AccessToken token(Principal principal) { - String tokenValue = SecurityUtils.accessToken(principal); - return tokenStore.readAccessToken(tokenValue); - } - @ApiOperation("拥有角色") @GetMapping("/authorities") public Collection authorities(Principal principal) { return SecurityUtils.authorities(principal); } + /** + * 修改用户名和密码 + */ + @GetMapping(value = "/password") + public void password(Principal principal, @RequestParam String username, @RequestParam String password) { + } + + /** + * 更换手机号 + */ + @GetMapping(value = "/mobile") + public void mobile(Principal principal, @RequestParam String mobile, @RequestParam String nonce) { + } + @ApiOperation("注销") @DeleteMapping - public void logout(Principal principal) { - tokenStore.removeAccessToken(token(principal)); + public void revokeToken(Principal principal) { + tokenServices.revokeToken(SecurityUtils.accessToken(principal)); } - -// @ApiOperation("页面内按钮") -// @GetMapping("/view/{id}/actions") -// public Collection actions(@PathVariable("id") long id, -// Principal principal, @SortDefault("orderNo") Sort sort) { -// return actionPermRepo.findByViewPermId(id, sort) -// .stream() -// .map(JpaIdentifiable::getIdentifier) -// .collect(Collectors.toList()); -// } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 9b1c5e2..7087030 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -1,6 +1,5 @@ package cn.seqdata.oauth2.controller; -import java.security.Principal; import java.util.Objects; import io.swagger.annotations.ApiOperation; @@ -40,20 +39,6 @@ public class OAuth2CodeController { private final UserService userService; private final TokenService tokenService; - /** - * 修改用户名和密码 - */ - @GetMapping(value = "/password") - public void password(Principal principal, @RequestParam String username, @RequestParam String password) { - } - - /** - * 更换手机号 - */ - @GetMapping(value = "/mobile") - public void mobile(Principal principal, @RequestParam String mobile, @RequestParam String nonce) { - } - @ApiOperation("authorization_code的回调函数,处理code和state") @GetMapping(value = "/{registrationId}", params = OAuth2ParameterNames.CODE) public ResponseEntity bind(@PathVariable String registrationId, diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java new file mode 100644 index 0000000..b2125aa --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java @@ -0,0 +1,36 @@ +package cn.seqdata.oauth2.controller; + +import java.security.Principal; + +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * @author jrxian + * @date 2020-06-06 20:38 + */ +@RestController +@RequestMapping("/oauth/token") +@AllArgsConstructor +public class OAuth2TokenController { + private final TokenStore tokenStore; + private final ConsumerTokenServices tokenServices; + + @GetMapping + public OAuth2AccessToken readAccessToken(Principal principal) { + return tokenStore.readAccessToken(SecurityUtils.accessToken(principal)); + } + + @DeleteMapping + public void revokeToken(Principal principal) { + tokenServices.revokeToken(SecurityUtils.accessToken(principal)); + } +} diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index db3c0ca..fe26424 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -43,5 +43,9 @@ org.springframework.boot spring-boot-starter-jdbc --> + + org.springframework.boot + spring-boot-starter-amqp + diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index bd272e6..abac55b 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -1,5 +1,8 @@ package cn.seqdata.gateway; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; @@ -10,6 +13,7 @@ import cn.seqdata.gateway.filter.authc.WhitelistPredicate; import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; import cn.seqdata.log.LogRecorder; +import cn.seqdata.log.RabbitRecorder; import cn.seqdata.log.url.UrlRecord; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; @@ -20,8 +24,15 @@ import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; @Slf4j @Configuration public class GatewayConfiguration { + + @Bean + @ConditionalOnProperty("spring.rabbitmq.host") + public RabbitRecorder rabbitLogRecorder(RabbitTemplate rabbitTemplate) { + return new RabbitRecorder<>(rabbitTemplate); + } @Bean + @ConditionalOnMissingBean(RabbitRecorder.class) public LogRecorder logRecorder() { return record -> log.debug(String.valueOf(record)); } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java index 82b61fd..b6f40aa 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java @@ -21,7 +21,7 @@ public class CachedResourceServerTokenServices implements ResourceServerTokenSer private final LoadingCache authenticationCache = CacheBuilder.newBuilder() .maximumSize(16) - .expireAfterAccess(1, TimeUnit.MINUTES) + .expireAfterAccess(10, TimeUnit.SECONDS) .build(new CacheLoader() { @Override public OAuth2Authentication load(String key) { @@ -31,7 +31,7 @@ public class CachedResourceServerTokenServices implements ResourceServerTokenSer private final LoadingCache accessTokenCache = CacheBuilder.newBuilder() .maximumSize(16) - .expireAfterAccess(1, TimeUnit.MINUTES) + .expireAfterAccess(10, TimeUnit.SECONDS) .build(new CacheLoader() { @Override public OAuth2AccessToken load(String key) { -- Gitee From 8977508b0e7ca2af0a038d43b42a9de5fb7a57b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 6 Jun 2020 22:08:49 +0800 Subject: [PATCH 022/228] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E5=88=86del?= =?UTF-8?q?ete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/ConnectController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 925e902..085c85d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -62,7 +62,7 @@ public class ConnectController { } @DeleteMapping(value = "/{registrationId}") - public void unbind(@PathVariable String registrationId, Principal principal) { + public void disconnect(@PathVariable String registrationId, Principal principal) { UserDetailId entityId = new UserDetailId(registrationId, principal.getName()); userDetailRepo.deleteById(entityId); } -- Gitee From 3de5f5cf4a4dda60c9c39b98f6a987897c8cfc88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 6 Jun 2020 22:38:33 +0800 Subject: [PATCH 023/228] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E5=88=86del?= =?UTF-8?q?ete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 40 ++++++++++++++++--- .../controller/OAuth2TokenController.java | 36 ----------------- 2 files changed, 34 insertions(+), 42 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index a0b5aaa..cf00c53 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -2,15 +2,20 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; +import java.util.Objects; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ServerWebInputException; import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.repos.perm.ActionPermRepo; +import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; @@ -23,8 +28,10 @@ import cn.seqdata.oauth2.util.SecurityUtils; @RequestMapping("/current") @AllArgsConstructor public class CurrentController { + private final UserRepo userRepo; private final ActionPermRepo actionPermRepo; private final UserService userService; + private final TokenStore tokenStore; private final ConsumerTokenServices tokenServices; @ApiOperation("通行证") @@ -41,6 +48,18 @@ public class CurrentController { return userService.loadUser(clientId, username); } + @ApiOperation("令牌信息") + @GetMapping("/token") + public OAuth2AccessToken token(Principal principal) { + return tokenStore.readAccessToken(SecurityUtils.accessToken(principal)); + } + + @ApiOperation("注销") + @DeleteMapping + public void logout(Principal principal) { + tokenServices.revokeToken(SecurityUtils.accessToken(principal)); + } + @ApiOperation("拥有角色") @GetMapping("/authorities") public Collection authorities(Principal principal) { @@ -52,6 +71,18 @@ public class CurrentController { */ @GetMapping(value = "/password") public void password(Principal principal, @RequestParam String username, @RequestParam String password) { + User entity = userRepo.getOne(SecurityUtils.userId(principal)); + + User user = userRepo.findByUsername(username); + if(Objects.nonNull(user)) { + if(!Objects.equals(entity.getId(), user.getId())) { + throw new ServerWebInputException("用户名已经被使用!"); + } + } + + entity.setUsername(username); + entity.setPassword(password); + userRepo.save(entity); } /** @@ -59,11 +90,8 @@ public class CurrentController { */ @GetMapping(value = "/mobile") public void mobile(Principal principal, @RequestParam String mobile, @RequestParam String nonce) { - } - - @ApiOperation("注销") - @DeleteMapping - public void revokeToken(Principal principal) { - tokenServices.revokeToken(SecurityUtils.accessToken(principal)); + User entity = userRepo.getOne(SecurityUtils.userId(principal)); + entity.setMobile(mobile); + userRepo.save(entity); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java deleted file mode 100644 index b2125aa..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2TokenController.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.seqdata.oauth2.controller; - -import java.security.Principal; - -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; -import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import lombok.AllArgsConstructor; - -import cn.seqdata.oauth2.util.SecurityUtils; - -/** - * @author jrxian - * @date 2020-06-06 20:38 - */ -@RestController -@RequestMapping("/oauth/token") -@AllArgsConstructor -public class OAuth2TokenController { - private final TokenStore tokenStore; - private final ConsumerTokenServices tokenServices; - - @GetMapping - public OAuth2AccessToken readAccessToken(Principal principal) { - return tokenStore.readAccessToken(SecurityUtils.accessToken(principal)); - } - - @DeleteMapping - public void revokeToken(Principal principal) { - tokenServices.revokeToken(SecurityUtils.accessToken(principal)); - } -} -- Gitee From c9b881e2dcf5ed2848e104e39967f6a934e0132a Mon Sep 17 00:00:00 2001 From: me <791216569@qq.com> Date: Fri, 12 Jun 2020 09:47:37 +0800 Subject: [PATCH 024/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3token=E6=AF=8F?= =?UTF-8?q?=E6=AC=A1=E7=94=9F=E6=88=90=E4=B8=80=E6=A0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 201 +++++++++--------- 1 file changed, 101 insertions(+), 100 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index e911dba..5828f2e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -1,7 +1,15 @@ package cn.seqdata.oauth2; -import java.util.*; - +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; +import cn.seqdata.oauth2.mobile.MobileNonceService; +import cn.seqdata.oauth2.mobile.MobileTokenGranter; +import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; +import cn.seqdata.oauth2.service.JpaClientDetailsService; +import cn.seqdata.oauth2.service.JpaUserDetailsManager; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -29,17 +37,8 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; -import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.jpa.rbac.User; -import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; -import cn.seqdata.oauth2.mobile.MobileNonceService; -import cn.seqdata.oauth2.mobile.MobileTokenGranter; -import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; -import cn.seqdata.oauth2.service.JpaClientDetailsService; -import cn.seqdata.oauth2.service.JpaUserDetailsManager; -import cn.seqdata.oauth2.service.UserService; -import cn.seqdata.oauth2.util.SecurityUtils; +import java.util.*; /** * Author: jrxian @@ -49,56 +48,58 @@ import cn.seqdata.oauth2.util.SecurityUtils; @EnableAuthorizationServer @AllArgsConstructor public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { - private final AuthenticationManager authenticationManager; - private final UserService userService; - private final JpaUserDetailsManager userDetailsService; - private final RedisConnectionFactory redisConnectionFactory; - private final ClientDetailRepo clientDetailRepo; - private final MobileNonceService mobileNonceService; - - @Bean - public PasswordEncoder passwordEncoder() { - return NoOpPasswordEncoder.getInstance(); + private final AuthenticationManager authenticationManager; + private final UserService userService; + private final JpaUserDetailsManager userDetailsService; + private final RedisConnectionFactory redisConnectionFactory; + private final ClientDetailRepo clientDetailRepo; + private final MobileNonceService mobileNonceService; + + @Bean + public PasswordEncoder passwordEncoder() { + return NoOpPasswordEncoder.getInstance(); // return PasswordEncoderFactories.createDelegatingPasswordEncoder(); - } - - @Bean - public TokenStore tokenStore() { - return new RedisTokenStore(redisConnectionFactory); - } - - @Bean - public TokenEnhancer tokenEnhancer() { - return (accessToken, authentication) -> { - if(accessToken instanceof DefaultOAuth2AccessToken) { - Map attributes = new HashMap<>(); - - String clientId = SecurityUtils.clientId(authentication); - String username = SecurityUtils.username(authentication); - User user = userService.loadUser(clientId, username); - - if(Objects.nonNull(user)) { - attributes.put("user_id", user.getId()); - attributes.put("org_id", user.getOrgId()); - } - - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); - } - - return accessToken; - }; - } - - @Override - public void configure(AuthorizationServerSecurityConfigurer security) { - security - .tokenKeyAccess("isAuthenticated()") - .checkTokenAccess("isAuthenticated()"); - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); + } + + @Bean + public TokenStore tokenStore() { + RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); + redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString()); + return redisTokenStore; + } + + @Bean + public TokenEnhancer tokenEnhancer() { + return (accessToken, authentication) -> { + if (accessToken instanceof DefaultOAuth2AccessToken) { + Map attributes = new HashMap<>(); + + String clientId = SecurityUtils.clientId(authentication); + String username = SecurityUtils.username(authentication); + User user = userService.loadUser(clientId, username); + + if (Objects.nonNull(user)) { + attributes.put("user_id", user.getId()); + attributes.put("org_id", user.getOrgId()); + } + + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); + } + + return accessToken; + }; + } + + @Override + public void configure(AuthorizationServerSecurityConfigurer security) { + security + .tokenKeyAccess("isAuthenticated()") + .checkTokenAccess("isAuthenticated()"); + } + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); // InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); // builder.withClient("client") @@ -113,43 +114,43 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt // registrationRepository.forEach(clientRegistration -> builder // .withClient(clientRegistration.getRegistrationId()) // .authorizedGrantTypes(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())); - } - - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) { - endpoints - .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) - .authenticationManager(authenticationManager) - .userDetailsService(userDetailsService) - .tokenStore(tokenStore()) - .tokenEnhancer(tokenEnhancer()) - .tokenGranter(tokenGranter(endpoints)); - } - - /** - * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter - */ - private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { - ClientDetailsService clientDetails = endpoints.getClientDetailsService(); - AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); - AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); - OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); - - List tokenGranters = new ArrayList<>(); - tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); - tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); - //用户名密码验证 - if(Objects.nonNull(authenticationManager)) { - tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); - } - //自定义的手机验证码登录 - if(Objects.nonNull(mobileNonceService)) { - MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); - tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); - } - - return new CompositeTokenGranter(tokenGranters); - } + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + endpoints + .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsService) + .tokenStore(tokenStore()) + .tokenEnhancer(tokenEnhancer()) + .tokenGranter(tokenGranter(endpoints)); + } + + /** + * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter + */ + private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { + ClientDetailsService clientDetails = endpoints.getClientDetailsService(); + AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); + AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); + OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); + + List tokenGranters = new ArrayList<>(); + tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); + tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + //用户名密码验证 + if (Objects.nonNull(authenticationManager)) { + tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + } + //自定义的手机验证码登录 + if (Objects.nonNull(mobileNonceService)) { + MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); + tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); + } + + return new CompositeTokenGranter(tokenGranters); + } } -- Gitee From 313e953dea7a9970301d9306c5b54bda679bf385 Mon Sep 17 00:00:00 2001 From: me <791216569@qq.com> Date: Fri, 12 Jun 2020 11:05:30 +0800 Subject: [PATCH 025/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9bootstrap.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-gateway/src/main/resources/bootstrap.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index 02780cf..387a9c0 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -1,14 +1,16 @@ spring: application: - name: gateway + name: ${APPLICATION_NAME:gateway} cloud: gateway: discovery: locator: enabled: true nacos: + discovery: + server-addr: ${NACOS_ADDR:192.168.1.241}:${NACOS_PORT:8848} config: - server-addr: nacos.seqdata.cn:8848 + server-addr: ${spring.cloud.nacos.discovery.server-addr} file-extension: yml - discovery: - server-addr: nacos.seqdata.cn:8848 \ No newline at end of file + # 公共 文件 + shared-dataids: application.${spring.cloud.nacos.config.file-extension} \ No newline at end of file -- Gitee From 5d5ad74b88007981ab2493ae756c31d449cbe951 Mon Sep 17 00:00:00 2001 From: me <791216569@qq.com> Date: Fri, 12 Jun 2020 11:32:20 +0800 Subject: [PATCH 026/228] =?UTF-8?q?bootstrap=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/bootstrap.yml | 21 ++++++++++++------- .../src/main/resources/bootstrap.yml | 11 ++++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index b06fcb6..620f47e 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -1,12 +1,19 @@ spring: application: - name: authz + name: ${APPLICATION_NAME:authz} cloud: + gateway: + discovery: + locator: + enabled: true nacos: - config: - server-addr: nacos.seqdata.cn:8848 - file-extension: yml discovery: - server-addr: nacos.seqdata.cn:8848 -server: - port: 30001 \ No newline at end of file + server-addr: ${NACOS_SERVICE_HOST:192.168.1.241}:${NACOS_SERVICE_PORT:8848} + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: ${CONFIG_FORMAT:yml} + # 公共文件 + shared-configs[0]: + data-id: ${SHARE_CONFIG_PREFIX:application}.${spring.cloud.nacos.config.file-extension} + # 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false + refresh: false \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index 387a9c0..9ff0490 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -8,9 +8,12 @@ spring: enabled: true nacos: discovery: - server-addr: ${NACOS_ADDR:192.168.1.241}:${NACOS_PORT:8848} + server-addr: ${NACOS_SERVICE_HOST:192.168.1.241}:${NACOS_SERVICE_PORT:8848} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: yml - # 公共 文件 - shared-dataids: application.${spring.cloud.nacos.config.file-extension} \ No newline at end of file + file-extension: ${CONFIG_FORMAT:yml} + # 公共文件 + shared-configs[0]: + data-id: ${SHARE_CONFIG_PREFIX:application}.${spring.cloud.nacos.config.file-extension} + # 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false + refresh: false \ No newline at end of file -- Gitee From a36190ef3a03610eb6d20a277ff0d80aaf05b767 Mon Sep 17 00:00:00 2001 From: me <791216569@qq.com> Date: Fri, 12 Jun 2020 11:34:12 +0800 Subject: [PATCH 027/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=AF=8F=E6=AC=A1?= =?UTF-8?q?=E7=94=9F=E6=88=90=E7=9A=84token=E9=83=BD=E4=B8=80=E6=A0=B7?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 202 +++++++++--------- 1 file changed, 102 insertions(+), 100 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index e911dba..aef6a63 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -1,7 +1,15 @@ package cn.seqdata.oauth2; -import java.util.*; - +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; +import cn.seqdata.oauth2.mobile.MobileNonceService; +import cn.seqdata.oauth2.mobile.MobileTokenGranter; +import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; +import cn.seqdata.oauth2.service.JpaClientDetailsService; +import cn.seqdata.oauth2.service.JpaUserDetailsManager; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -29,17 +37,8 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; -import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.jpa.rbac.User; -import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; -import cn.seqdata.oauth2.mobile.MobileNonceService; -import cn.seqdata.oauth2.mobile.MobileTokenGranter; -import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; -import cn.seqdata.oauth2.service.JpaClientDetailsService; -import cn.seqdata.oauth2.service.JpaUserDetailsManager; -import cn.seqdata.oauth2.service.UserService; -import cn.seqdata.oauth2.util.SecurityUtils; +import java.util.*; /** * Author: jrxian @@ -49,56 +48,59 @@ import cn.seqdata.oauth2.util.SecurityUtils; @EnableAuthorizationServer @AllArgsConstructor public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { - private final AuthenticationManager authenticationManager; - private final UserService userService; - private final JpaUserDetailsManager userDetailsService; - private final RedisConnectionFactory redisConnectionFactory; - private final ClientDetailRepo clientDetailRepo; - private final MobileNonceService mobileNonceService; - - @Bean - public PasswordEncoder passwordEncoder() { - return NoOpPasswordEncoder.getInstance(); + private final AuthenticationManager authenticationManager; + private final UserService userService; + private final JpaUserDetailsManager userDetailsService; + private final RedisConnectionFactory redisConnectionFactory; + private final ClientDetailRepo clientDetailRepo; + private final MobileNonceService mobileNonceService; + + @Bean + public PasswordEncoder passwordEncoder() { + return NoOpPasswordEncoder.getInstance(); // return PasswordEncoderFactories.createDelegatingPasswordEncoder(); - } - - @Bean - public TokenStore tokenStore() { - return new RedisTokenStore(redisConnectionFactory); - } - - @Bean - public TokenEnhancer tokenEnhancer() { - return (accessToken, authentication) -> { - if(accessToken instanceof DefaultOAuth2AccessToken) { - Map attributes = new HashMap<>(); - - String clientId = SecurityUtils.clientId(authentication); - String username = SecurityUtils.username(authentication); - User user = userService.loadUser(clientId, username); - - if(Objects.nonNull(user)) { - attributes.put("user_id", user.getId()); - attributes.put("org_id", user.getOrgId()); - } - - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); - } - - return accessToken; - }; - } - - @Override - public void configure(AuthorizationServerSecurityConfigurer security) { - security - .tokenKeyAccess("isAuthenticated()") - .checkTokenAccess("isAuthenticated()"); - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); + } + + @Bean + public TokenStore tokenStore() { + RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); + // 解决每次生成的token都一样的问题 + redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString()); + return redisTokenStore; + } + + @Bean + public TokenEnhancer tokenEnhancer() { + return (accessToken, authentication) -> { + if (accessToken instanceof DefaultOAuth2AccessToken) { + Map attributes = new HashMap<>(); + + String clientId = SecurityUtils.clientId(authentication); + String username = SecurityUtils.username(authentication); + User user = userService.loadUser(clientId, username); + + if (Objects.nonNull(user)) { + attributes.put("user_id", user.getId()); + attributes.put("org_id", user.getOrgId()); + } + + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); + } + + return accessToken; + }; + } + + @Override + public void configure(AuthorizationServerSecurityConfigurer security) { + security + .tokenKeyAccess("isAuthenticated()") + .checkTokenAccess("isAuthenticated()"); + } + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); // InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); // builder.withClient("client") @@ -113,43 +115,43 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt // registrationRepository.forEach(clientRegistration -> builder // .withClient(clientRegistration.getRegistrationId()) // .authorizedGrantTypes(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())); - } - - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) { - endpoints - .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) - .authenticationManager(authenticationManager) - .userDetailsService(userDetailsService) - .tokenStore(tokenStore()) - .tokenEnhancer(tokenEnhancer()) - .tokenGranter(tokenGranter(endpoints)); - } - - /** - * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter - */ - private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { - ClientDetailsService clientDetails = endpoints.getClientDetailsService(); - AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); - AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); - OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); - - List tokenGranters = new ArrayList<>(); - tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); - tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); - //用户名密码验证 - if(Objects.nonNull(authenticationManager)) { - tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); - } - //自定义的手机验证码登录 - if(Objects.nonNull(mobileNonceService)) { - MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); - tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); - } - - return new CompositeTokenGranter(tokenGranters); - } + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + endpoints + .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsService) + .tokenStore(tokenStore()) + .tokenEnhancer(tokenEnhancer()) + .tokenGranter(tokenGranter(endpoints)); + } + + /** + * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter + */ + private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { + ClientDetailsService clientDetails = endpoints.getClientDetailsService(); + AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); + AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); + OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); + + List tokenGranters = new ArrayList<>(); + tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); + tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + //用户名密码验证 + if (Objects.nonNull(authenticationManager)) { + tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + } + //自定义的手机验证码登录 + if (Objects.nonNull(mobileNonceService)) { + MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); + tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); + } + + return new CompositeTokenGranter(tokenGranters); + } } -- Gitee From c90a77499bb3c676ce63c9564731531ba080d0b0 Mon Sep 17 00:00:00 2001 From: me <791216569@qq.com> Date: Fri, 12 Jun 2020 11:50:40 +0800 Subject: [PATCH 028/228] =?UTF-8?q?=E5=BC=95=E5=85=A5mysql8.0,alibaba?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 15ee45f..254dc74 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -47,5 +47,16 @@ mssql-jdbc runtime + + + mysql + mysql-connector-java + runtime + + + com.alibaba + druid-spring-boot-starter + 1.1.20 + -- Gitee From 3a5dddeb288f2a5343c40e8a066f2b167f071a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 21 Jun 2020 23:39:51 +0800 Subject: [PATCH 029/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E7=99=BB=E5=BD=95=EF=BC=8C=E7=AC=AC=E4=B8=89=E6=96=B9=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 11 + .../cn/seqdata/oauth2/AliyunProperties.java | 15 ++ .../seqdata/oauth2/AliyunSmsProperties.java | 16 ++ .../oauth2/AuthzServerApplication.java | 2 + .../oauth2/AuthzServerConfiguration.java | 201 +++++++++--------- .../cn/seqdata/oauth2/HomeProperties.java | 14 ++ .../seqdata/oauth2/MobileConfiguration.java | 36 +++- .../oauth2/controller/ConnectController.java | 12 +- .../oauth2/controller/CurrentController.java | 6 +- .../controller/OAuth2CodeController.java | 39 +++- .../MobileNonceAuthenticationProvider.java | 16 +- .../oauth2/mobile/MobileNonceHandler.java | 19 ++ .../oauth2/mobile/MobileNonceService.java | 11 - .../oauth2/service/AliyunSmsService.java | 66 ++++++ .../oauth2/service/MobileNonceService.java | 56 +++++ .../seqdata/oauth2/service/UserService.java | 21 +- .../src/main/resources/application.yml | 1 + 17 files changed, 392 insertions(+), 150 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunProperties.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 15ee45f..b510dd9 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -26,6 +26,11 @@ org.springframework.boot spring-boot-starter-data-redis + + org.springframework.boot + spring-boot-configuration-processor + true + org.springframework.security @@ -36,6 +41,12 @@ spring-security-oauth2-autoconfigure + + com.aliyun + aliyun-java-sdk-core + 4.5.0 + + cn.seqdata.cloud seqdata-cloud-oauth2 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunProperties.java new file mode 100644 index 0000000..4d4d7c9 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunProperties.java @@ -0,0 +1,15 @@ +package cn.seqdata.oauth2; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author jrxian + * @date 2020-06-21 19:02 + */ +@lombok.Getter +@lombok.Setter +@ConfigurationProperties("aliyun") +public class AliyunProperties { + private String accessKey; + private String accessSecret; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java new file mode 100644 index 0000000..b377302 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java @@ -0,0 +1,16 @@ +package cn.seqdata.oauth2; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author jrxian + * @date 2020-06-21 19:01 + */ +@lombok.Getter +@lombok.Setter +@ConfigurationProperties("aliyun.sms") +public class AliyunSmsProperties { + private Boolean enabled; + private String regionId; + private String templateCode; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index 03fdde7..09a3af5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -2,6 +2,7 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import springfox.documentation.swagger2.annotations.EnableSwagger2; @@ -11,6 +12,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; */ @SpringBootApplication @EnableDiscoveryClient +@EnableConfigurationProperties(HomeProperties.class) @EnableSwagger2 public class AuthzServerApplication { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 5828f2e..4b5bc27 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -1,15 +1,7 @@ package cn.seqdata.oauth2; -import cn.seqdata.oauth2.jpa.rbac.User; -import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; -import cn.seqdata.oauth2.mobile.MobileNonceService; -import cn.seqdata.oauth2.mobile.MobileTokenGranter; -import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; -import cn.seqdata.oauth2.service.JpaClientDetailsService; -import cn.seqdata.oauth2.service.JpaUserDetailsManager; -import cn.seqdata.oauth2.service.UserService; -import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; +import java.util.*; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -37,8 +29,17 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; +import lombok.AllArgsConstructor; -import java.util.*; +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; +import cn.seqdata.oauth2.mobile.MobileNonceHandler; +import cn.seqdata.oauth2.mobile.MobileTokenGranter; +import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; +import cn.seqdata.oauth2.service.JpaClientDetailsService; +import cn.seqdata.oauth2.service.JpaUserDetailsManager; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.util.SecurityUtils; /** * Author: jrxian @@ -48,58 +49,56 @@ import java.util.*; @EnableAuthorizationServer @AllArgsConstructor public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { - private final AuthenticationManager authenticationManager; - private final UserService userService; - private final JpaUserDetailsManager userDetailsService; - private final RedisConnectionFactory redisConnectionFactory; - private final ClientDetailRepo clientDetailRepo; - private final MobileNonceService mobileNonceService; - - @Bean - public PasswordEncoder passwordEncoder() { - return NoOpPasswordEncoder.getInstance(); + private final AuthenticationManager authenticationManager; + private final UserService userService; + private final JpaUserDetailsManager userDetailsService; + private final RedisConnectionFactory redisConnectionFactory; + private final ClientDetailRepo clientDetailRepo; + private final MobileNonceHandler mobileNonceService; + + @Bean + public PasswordEncoder passwordEncoder() { + return NoOpPasswordEncoder.getInstance(); // return PasswordEncoderFactories.createDelegatingPasswordEncoder(); - } - - @Bean - public TokenStore tokenStore() { - RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); - redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString()); - return redisTokenStore; - } - - @Bean - public TokenEnhancer tokenEnhancer() { - return (accessToken, authentication) -> { - if (accessToken instanceof DefaultOAuth2AccessToken) { - Map attributes = new HashMap<>(); - - String clientId = SecurityUtils.clientId(authentication); - String username = SecurityUtils.username(authentication); - User user = userService.loadUser(clientId, username); - - if (Objects.nonNull(user)) { - attributes.put("user_id", user.getId()); - attributes.put("org_id", user.getOrgId()); - } - - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); - } - - return accessToken; - }; - } - - @Override - public void configure(AuthorizationServerSecurityConfigurer security) { - security - .tokenKeyAccess("isAuthenticated()") - .checkTokenAccess("isAuthenticated()"); - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); + } + + @Bean + public TokenStore tokenStore() { + return new RedisTokenStore(redisConnectionFactory); + } + + @Bean + public TokenEnhancer tokenEnhancer() { + return (accessToken, authentication) -> { + if(accessToken instanceof DefaultOAuth2AccessToken) { + Map attributes = new HashMap<>(); + + String clientId = SecurityUtils.clientId(authentication); + String username = SecurityUtils.username(authentication); + User user = userService.loadUser(clientId, username); + + if(Objects.nonNull(user)) { + attributes.put("user_id", user.getId()); + attributes.put("org_id", user.getOrgId()); + } + + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); + } + + return accessToken; + }; + } + + @Override + public void configure(AuthorizationServerSecurityConfigurer security) { + security + .tokenKeyAccess("isAuthenticated()") + .checkTokenAccess("isAuthenticated()"); + } + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); // InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); // builder.withClient("client") @@ -114,43 +113,43 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt // registrationRepository.forEach(clientRegistration -> builder // .withClient(clientRegistration.getRegistrationId()) // .authorizedGrantTypes(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())); - } - - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) { - endpoints - .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) - .authenticationManager(authenticationManager) - .userDetailsService(userDetailsService) - .tokenStore(tokenStore()) - .tokenEnhancer(tokenEnhancer()) - .tokenGranter(tokenGranter(endpoints)); - } - - /** - * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter - */ - private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { - ClientDetailsService clientDetails = endpoints.getClientDetailsService(); - AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); - AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); - OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); - - List tokenGranters = new ArrayList<>(); - tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); - tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); - //用户名密码验证 - if (Objects.nonNull(authenticationManager)) { - tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); - } - //自定义的手机验证码登录 - if (Objects.nonNull(mobileNonceService)) { - MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); - tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); - } - - return new CompositeTokenGranter(tokenGranters); - } + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + endpoints + .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsService) + .tokenStore(tokenStore()) + .tokenEnhancer(tokenEnhancer()) + .tokenGranter(tokenGranter(endpoints)); + } + + /** + * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter + */ + private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { + ClientDetailsService clientDetails = endpoints.getClientDetailsService(); + AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); + AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); + OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); + + List tokenGranters = new ArrayList<>(); + tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); + tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + //用户名密码验证 + if(Objects.nonNull(authenticationManager)) { + tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + } + //自定义的手机验证码登录 + if(Objects.nonNull(mobileNonceService)) { + MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); + tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); + } + + return new CompositeTokenGranter(tokenGranters); + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java new file mode 100644 index 0000000..60ba5a2 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java @@ -0,0 +1,14 @@ +package cn.seqdata.oauth2; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author jrxian + * @date 2020-06-21 23:35 + */ +@lombok.Getter +@lombok.Setter +@ConfigurationProperties("spring.security.oauth2") +public class HomeProperties { + private String homeUri; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java index 4ce7ae8..bdf5eca 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java @@ -1,26 +1,50 @@ package cn.seqdata.oauth2; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +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.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.core.AuthenticationException; -import cn.seqdata.oauth2.mobile.MobileNonceService; +import cn.seqdata.oauth2.mobile.MobileNonceHandler; +import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.service.AliyunSmsService; +import cn.seqdata.oauth2.service.MobileNonceService; /** * Author: jrxian * Date: 2020-04-07 10:17 */ @Configuration +@EnableConfigurationProperties({AliyunProperties.class, AliyunSmsProperties.class}) public class MobileConfiguration { + + @Bean + @ConditionalOnProperty(value = "aliyun.sms.enabled", havingValue = "true") + public AliyunSmsService smsService(AliyunProperties properties, AliyunSmsProperties smsProperties) { + return new AliyunSmsService(properties, smsProperties); + } + + @Bean + @ConditionalOnBean(AliyunSmsService.class) + public MobileNonceHandler aliyunMobileNonceService(UserRepo userRepo, AliyunSmsService smsService) { + return new MobileNonceService(userRepo, smsService); + } + @Bean - public MobileNonceService mobileNonceService() { - return new MobileNonceService() { + @ConditionalOnMissingBean(AliyunSmsService.class) + public MobileNonceHandler anonymousMobileNonceHandler() { + return new MobileNonceHandler() { @Override - public void generate(String mobile, int expire) { + public void nonce(String mobile, int expire) { } @Override - public boolean check(String mobile, String nonce) { - return true; + public void check(String mobile, int nonce) throws AuthenticationException { + throw new AuthenticationServiceException("未实现短信验证码登录功能"); } }; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 085c85d..122a9f7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -47,17 +47,17 @@ public class ConnectController { String accessToken = SecurityUtils.accessToken(request.getUserPrincipal()); //重定向 URI,返回信息由 OAuth2CodeController 负责接收 - String redirectURL = registration.getRedirectUriTemplate(); - if(Objects.isNull(redirectURL)) { - redirectURL = String.valueOf((request.getRequestURL())); - redirectURL = redirectURL.replace("/connect", "/oauth/code"); + String redirectUri = registration.getRedirectUriTemplate(); + if(Objects.isNull(redirectUri)) { + redirectUri = String.valueOf((request.getRequestURL())); + redirectUri = redirectUri.replace("/connect", "/oauth/code"); } //将 accessToken 以 state 的形式传送出去,返回后用来判断被绑定的用户 - URI authorizeURI = oAuth2Template.buildAuthorizeURI(accessToken, redirectURL); + URI authorizeUri = oAuth2Template.buildAuthorizeURI(accessToken, redirectUri); return ResponseEntity.status(HttpStatus.SEE_OTHER) - .location(authorizeURI) + .location(authorizeUri) .build(); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index cf00c53..3e32d3e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; import java.util.Objects; +import java.util.Optional; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -73,8 +74,9 @@ public class CurrentController { public void password(Principal principal, @RequestParam String username, @RequestParam String password) { User entity = userRepo.getOne(SecurityUtils.userId(principal)); - User user = userRepo.findByUsername(username); - if(Objects.nonNull(user)) { + Optional optional = userRepo.findByUsername(username); + if(optional.isPresent()) { + User user = optional.get(); if(!Objects.equals(entity.getId(), user.getId())) { throw new ServerWebInputException("用户名已经被使用!"); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 7087030..c4ee154 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -1,8 +1,11 @@ package cn.seqdata.oauth2.controller; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -15,9 +18,10 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; -import com.google.common.net.HttpHeaders; +import org.springframework.web.util.UriComponentsBuilder; import lombok.AllArgsConstructor; +import cn.seqdata.oauth2.HomeProperties; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; @@ -38,10 +42,35 @@ public class OAuth2CodeController { private final TokenStore tokenStore; private final UserService userService; private final TokenService tokenService; + private final HomeProperties homeProperties; @ApiOperation("authorization_code的回调函数,处理code和state") @GetMapping(value = "/{registrationId}", params = OAuth2ParameterNames.CODE) - public ResponseEntity bind(@PathVariable String registrationId, + public ResponseEntity bind(@PathVariable String registrationId, + @RequestParam(OAuth2ParameterNames.CODE) String code, + @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { + OAuth2AccessToken accessToken = bindForPost(registrationId, code, state); + + Map params = new HashMap<>(); + params.put(OAuth2AccessToken.ACCESS_TOKEN, accessToken.getValue()); + params.put(OAuth2AccessToken.TOKEN_TYPE, accessToken.getTokenType()); + params.put(OAuth2AccessToken.EXPIRES_IN, accessToken.getExpiresIn()); + params.putAll(accessToken.getAdditionalInformation()); + + //将 accessToken 以 state 的形式传送出去,返回后用来判断被绑定的用户 + if(StringUtils.isNotBlank(homeProperties.getHomeUri())) { + return ResponseEntity.status(HttpStatus.SEE_OTHER) + .location(UriComponentsBuilder.fromUriString(homeProperties.getHomeUri()) + .build(params)) + .build(); + } else { + return ResponseEntity.ok(accessToken); + } + } + + @ApiOperation("用于小程序直接获取code后绑定用户信息") + @PostMapping(value = "/{registrationId}") + public OAuth2AccessToken bindForPost(@PathVariable String registrationId, @RequestParam(OAuth2ParameterNames.CODE) String code, @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { ClientRegistration registration = repository.findByRegistrationId(registrationId); @@ -66,11 +95,7 @@ public class OAuth2CodeController { userService.createUser(registrationId, userProfile); } - //要求提供跨域信息 - return ResponseEntity.status(HttpStatus.OK) - .header(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .header(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString()) - .body(tokenService.createToken(registrationId, remoteUser)); + return tokenService.createToken(registrationId, remoteUser); } @ApiOperation("authorization_code的错误回调函数,处理error, error_description, error_uri") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java index 8a1acc2..1b488f1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java @@ -2,8 +2,8 @@ package cn.seqdata.oauth2.mobile; import java.util.Collections; +import org.apache.commons.lang3.math.NumberUtils; import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -13,19 +13,17 @@ import org.springframework.security.core.AuthenticationException; * Date: 2020-01-26 00:52 */ public class MobileNonceAuthenticationProvider implements AuthenticationProvider { - private final MobileNonceService mobileNonceService; + private final MobileNonceHandler mobileNonceHandler; - public MobileNonceAuthenticationProvider(MobileNonceService mobileNonceService) { - this.mobileNonceService = mobileNonceService; + public MobileNonceAuthenticationProvider(MobileNonceHandler mobileNonceHandler) { + this.mobileNonceHandler = mobileNonceHandler; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - Object mobile = authentication.getPrincipal(); - Object nonce = authentication.getCredentials(); - if(!mobileNonceService.check(String.valueOf(mobile), String.valueOf(nonce))) { - throw new BadCredentialsException("Invalid Credentials"); - } + String mobile = String.valueOf(authentication.getPrincipal()); + int nonce = NumberUtils.toInt(String.valueOf(authentication.getCredentials()), -1); + mobileNonceHandler.check(mobile, nonce); return new UsernamePasswordAuthenticationToken(mobile, null, Collections.emptyList()); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java new file mode 100644 index 0000000..9feaf46 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java @@ -0,0 +1,19 @@ +package cn.seqdata.oauth2.mobile; + +import org.springframework.security.core.AuthenticationException; + +/** + * Author: jrxian + * Date: 2020-01-26 10:28 + */ +public interface MobileNonceHandler { + /** + * 生成一次性验证码 + */ + void nonce(String mobile, int expire); + + /** + * 检查一次性验证码 + */ + void check(String mobile, int nonce) throws AuthenticationException; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java deleted file mode 100644 index 7fbae74..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceService.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.seqdata.oauth2.mobile; - -/** - * Author: jrxian - * Date: 2020-01-26 10:28 - */ -public interface MobileNonceService { - void generate(String mobile, int expire); - - boolean check(String mobile, String nonce); -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java new file mode 100644 index 0000000..27cb56f --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java @@ -0,0 +1,66 @@ +package cn.seqdata.oauth2.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; +import com.alibaba.fastjson.JSON; +import com.aliyuncs.CommonRequest; +import com.aliyuncs.CommonResponse; +import com.aliyuncs.DefaultAcsClient; +import com.aliyuncs.IAcsClient; +import com.aliyuncs.exceptions.ClientException; +import com.aliyuncs.exceptions.ServerException; +import com.aliyuncs.http.MethodType; +import com.aliyuncs.profile.DefaultProfile; +import lombok.extern.slf4j.Slf4j; + +import cn.seqdata.oauth2.AliyunProperties; +import cn.seqdata.oauth2.AliyunSmsProperties; + +/** + * @author jrxian + * @date 2020-06-21 19:00 + */ +@Slf4j +public class AliyunSmsService implements BiConsumer { + private final IAcsClient acsClient; + private final AliyunSmsProperties properties; + + public AliyunSmsService(AliyunProperties properties, AliyunSmsProperties smsProperties) { + this.properties = smsProperties; + DefaultProfile profile = DefaultProfile.getProfile(smsProperties.getRegionId(), properties.getAccessKey(), properties.getAccessSecret()); + acsClient = new DefaultAcsClient(profile); + } + + @Override + public void accept(String mobile, Integer nonce) { + CommonRequest request = new CommonRequest(); + request.setSysMethod(MethodType.POST); + request.setSysDomain("dysmsapi.aliyuncs.com"); + request.setSysVersion("2017-05-25"); + request.setSysAction("SendSms"); + + request.putQueryParameter("RegionId", properties.getRegionId()); + request.putQueryParameter("TemplateCode", properties.getTemplateCode()); + + request.putQueryParameter("PhoneNumbers", mobile); + + Map templateParams = new HashMap<>(); + templateParams.put("code", Integer.toString(nonce)); + request.putQueryParameter("TemplateParam", JSON.toJSONString(templateParams)); + + try { + CommonResponse response = acsClient.getCommonResponse(request); + } catch(ClientException ex) { + log.warn(ex.getMessage()); + if(ex instanceof ServerException) { + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, ex.getErrMsg()); + } else { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getErrMsg()); + } + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java new file mode 100644 index 0000000..c538f2e --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java @@ -0,0 +1,56 @@ +package cn.seqdata.oauth2.service; + +import java.util.Objects; + +import org.apache.commons.lang3.RandomUtils; +import org.joda.time.DateTime; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.authentication.www.NonceExpiredException; +import org.springframework.web.server.ServerWebInputException; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.mobile.MobileNonceHandler; +import cn.seqdata.oauth2.repos.rbac.UserRepo; + +/** + * @author jrxian + * @date 2020-06-21 21:26 + */ +@AllArgsConstructor +public class MobileNonceService implements MobileNonceHandler { + private final UserRepo userRepo; + private final AliyunSmsService smsService; + + @Override + public void nonce(String mobile, int expire) { + User user = userRepo.findByMobile(mobile) + .orElseThrow(() -> new ServerWebInputException("无效的手机号")); + + int nonce = RandomUtils.nextInt(100000, 999999); + DateTime now = DateTime.now(); + user.setNonceCode(nonce); + user.setNonceExpire(now.plusSeconds(expire)); + userRepo.save(user); + + smsService.accept(mobile, nonce); + } + + @Override + public void check(String mobile, int nonce) throws AuthenticationException { + User user = userRepo.findByMobile(mobile) + .orElseThrow(() -> new UsernameNotFoundException("无效的手机号")); + + DateTime expire = user.getNonceExpire(); + if(Objects.isNull(expire) || expire.isBeforeNow()) { + throw new NonceExpiredException("验证码已过期"); + } + + Integer code = user.getNonceCode(); + if(Objects.isNull(code) || nonce != code) { + throw new BadCredentialsException("无效的验证码"); + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index ea924ff..82c7b6c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -39,8 +39,9 @@ public class UserService { if(!optional.isPresent()) { //查找微信的unionid,如果找到,视为同一个用户 if(userProfile instanceof WechatUserProfile) { - User user = userRepo.findByUnionId(((WechatUserProfile) userProfile).getUnionId()); - if(Objects.nonNull(user) && Objects.nonNull(user.getId())) { + Optional userOptional = userRepo.findByUnionId(((WechatUserProfile) userProfile).getUnionId()); + if(userOptional.isPresent()) { + User user = userOptional.get(); userDetail.setUserId(user.getId()); } } @@ -64,17 +65,21 @@ public class UserService { public User loadUser(String clientId, String username) { UserDetailId entityId = new UserDetailId(clientId, username); //先从oauth_user_details中查找用户 - User user = userDetailRepo.findById(entityId) - .map(UserDetail::getUser) - //未找到,再从oauth_user中查找用户 - .orElse(userRepo.findByUsername(username)); + Optional optional = userDetailRepo.findById(entityId) + .map(UserDetail::getUser); - if(Objects.nonNull(user)) { + //未找到,再从oauth_user中查找用户 + if(!optional.isPresent()) { + optional = userRepo.findByUsername(username); + } + + if(optional.isPresent()) { + User user = optional.get(); long userId = Objects.requireNonNull(user.getId()); user.setAuthorities(authorityService.loadAuthorities(userId)); } - return user; + return optional.orElse(null); } /** diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index e66a0cc..4261469 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -11,6 +11,7 @@ spring: port: 6379 security: oauth2: + home-uri: http://localhost/#/user/login?token={access_token} client: registration: github: -- Gitee From da045d2e7c3824055e36ab5dbf9676844f5fc9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 21 Jun 2020 23:45:40 +0800 Subject: [PATCH 030/228] mobileNonceService ==> mobileNonceHandler --- .../cn/seqdata/oauth2/AuthzServerConfiguration.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 4b5bc27..159bd9c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -54,7 +54,7 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt private final JpaUserDetailsManager userDetailsService; private final RedisConnectionFactory redisConnectionFactory; private final ClientDetailRepo clientDetailRepo; - private final MobileNonceHandler mobileNonceService; + private final MobileNonceHandler mobileNonceHandler; @Bean public PasswordEncoder passwordEncoder() { @@ -64,7 +64,10 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt @Bean public TokenStore tokenStore() { - return new RedisTokenStore(redisConnectionFactory); +// return new RedisTokenStore(redisConnectionFactory); + RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); + redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> String.valueOf(UUID.randomUUID())); + return redisTokenStore; } @Bean @@ -145,8 +148,8 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); } //自定义的手机验证码登录 - if(Objects.nonNull(mobileNonceService)) { - MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); + if(Objects.nonNull(mobileNonceHandler)) { + MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceHandler); tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); } -- Gitee From 1684fc327f52206857702377c550a201c7b12b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 28 Jun 2020 10:44:21 +0800 Subject: [PATCH 031/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 5 +++++ .../oauth2/controller/WxAppController.java | 13 +++++++++++++ .../seqdata/oauth2/wxapp/Code2SessionRequest.java | 14 ++++++++++++++ .../oauth2/wxapp/Code2SessionResponse.java | 15 +++++++++++++++ .../cn/seqdata/oauth2/wxapp/WxAppFeignClient.java | 15 +++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 533b225..1e29f0e 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -41,6 +41,11 @@ spring-security-oauth2-autoconfigure + + org.springframework.cloud + spring-cloud-starter-openfeign + + com.aliyun aliyun-java-sdk-core diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java new file mode 100644 index 0000000..bd66184 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -0,0 +1,13 @@ +package cn.seqdata.oauth2.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author jrxian + * @date 2020-06-22 09:17 + */ +@RestController +@RequestMapping("/wxapp") +public class WxAppController { +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java new file mode 100644 index 0000000..1008f44 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java @@ -0,0 +1,14 @@ +package cn.seqdata.oauth2.wxapp; + +/** + * @author jrxian + * @date 2020-06-22 09:27 + */ +@lombok.Getter +@lombok.Setter +public class Code2SessionRequest { + private String appid; + private String secret; + private String js_code; + private String grant_type; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java new file mode 100644 index 0000000..b546352 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java @@ -0,0 +1,15 @@ +package cn.seqdata.oauth2.wxapp; + +/** + * @author jrxian + * @date 2020-06-22 09:28 + */ +@lombok.Getter +@lombok.Setter +public class Code2SessionResponse { + private String openid; + private String session_key; + private String unionid; + private String errcode; + private String errmsg; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java new file mode 100644 index 0000000..46c59aa --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java @@ -0,0 +1,15 @@ +package cn.seqdata.oauth2.wxapp; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * @author jrxian + * @date 2020-06-22 09:21 + */ +@FeignClient(url = "https://api.weixin.qq.com") +public interface WxAppFeignClient { + @GetMapping("/sns/jscode2session") + Code2SessionResponse code2session(@RequestBody Code2SessionRequest request); +} -- Gitee From c967d400d3278b48961ad68950a6470defcf8a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 28 Jun 2020 10:51:19 +0800 Subject: [PATCH 032/228] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 145 +++++------------- 1 file changed, 42 insertions(+), 103 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 8aa2bf4..159bd9c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -1,15 +1,7 @@ package cn.seqdata.oauth2; -import cn.seqdata.oauth2.jpa.rbac.User; -import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; -import cn.seqdata.oauth2.mobile.MobileNonceService; -import cn.seqdata.oauth2.mobile.MobileTokenGranter; -import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; -import cn.seqdata.oauth2.service.JpaClientDetailsService; -import cn.seqdata.oauth2.service.JpaUserDetailsManager; -import cn.seqdata.oauth2.service.UserService; -import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; +import java.util.*; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -37,8 +29,8 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; +import lombok.AllArgsConstructor; -import java.util.*; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; import cn.seqdata.oauth2.mobile.MobileNonceHandler; @@ -57,59 +49,6 @@ import cn.seqdata.oauth2.util.SecurityUtils; @EnableAuthorizationServer @AllArgsConstructor public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { - private final AuthenticationManager authenticationManager; - private final UserService userService; - private final JpaUserDetailsManager userDetailsService; - private final RedisConnectionFactory redisConnectionFactory; - private final ClientDetailRepo clientDetailRepo; - private final MobileNonceService mobileNonceService; - - @Bean - public PasswordEncoder passwordEncoder() { - return NoOpPasswordEncoder.getInstance(); -// return PasswordEncoderFactories.createDelegatingPasswordEncoder(); - } - - @Bean - public TokenStore tokenStore() { - RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); - // 解决每次生成的token都一样的问题 - redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> UUID.randomUUID().toString()); - return redisTokenStore; - } - - @Bean - public TokenEnhancer tokenEnhancer() { - return (accessToken, authentication) -> { - if (accessToken instanceof DefaultOAuth2AccessToken) { - Map attributes = new HashMap<>(); - - String clientId = SecurityUtils.clientId(authentication); - String username = SecurityUtils.username(authentication); - User user = userService.loadUser(clientId, username); - - if (Objects.nonNull(user)) { - attributes.put("user_id", user.getId()); - attributes.put("org_id", user.getOrgId()); - } - - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); - } - - return accessToken; - }; - } - - @Override - public void configure(AuthorizationServerSecurityConfigurer security) { - security - .tokenKeyAccess("isAuthenticated()") - .checkTokenAccess("isAuthenticated()"); - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); private final AuthenticationManager authenticationManager; private final UserService userService; private final JpaUserDetailsManager userDetailsService; @@ -177,43 +116,43 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt // registrationRepository.forEach(clientRegistration -> builder // .withClient(clientRegistration.getRegistrationId()) // .authorizedGrantTypes(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())); - } - - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) { - endpoints - .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) - .authenticationManager(authenticationManager) - .userDetailsService(userDetailsService) - .tokenStore(tokenStore()) - .tokenEnhancer(tokenEnhancer()) - .tokenGranter(tokenGranter(endpoints)); - } - - /** - * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter - */ - private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { - ClientDetailsService clientDetails = endpoints.getClientDetailsService(); - AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); - AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); - OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); - - List tokenGranters = new ArrayList<>(); - tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); - tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); - //用户名密码验证 - if (Objects.nonNull(authenticationManager)) { - tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); - } - //自定义的手机验证码登录 - if (Objects.nonNull(mobileNonceService)) { - MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceService); - tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); - } - - return new CompositeTokenGranter(tokenGranters); - } + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + endpoints + .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsService) + .tokenStore(tokenStore()) + .tokenEnhancer(tokenEnhancer()) + .tokenGranter(tokenGranter(endpoints)); + } + + /** + * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter + */ + private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { + ClientDetailsService clientDetails = endpoints.getClientDetailsService(); + AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); + AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); + OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); + + List tokenGranters = new ArrayList<>(); + tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); + tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + //用户名密码验证 + if(Objects.nonNull(authenticationManager)) { + tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + } + //自定义的手机验证码登录 + if(Objects.nonNull(mobileNonceHandler)) { + MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceHandler); + tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); + } + + return new CompositeTokenGranter(tokenGranters); + } } -- Gitee From 99495e745cab5305d30c4102237e750ce9a6aafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 28 Jun 2020 14:37:24 +0800 Subject: [PATCH 033/228] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 ++++---- seqdata-cloud-authz/pom.xml | 6 ------ .../java/cn/seqdata/oauth2/MobileConfiguration.java | 1 + .../seqdata/oauth2/controller/ConnectController.java | 6 +++++- .../oauth2/controller/OAuth2CodeController.java | 2 +- .../cn/seqdata/oauth2/service/AliyunSmsService.java | 10 ++++++++-- seqdata-cloud-authz/src/main/resources/bootstrap.yml | 9 ++------- seqdata-cloud-gateway/src/main/resources/bootstrap.yml | 9 ++------- 8 files changed, 23 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index c8aa125..bc023cc 100644 --- a/pom.xml +++ b/pom.xml @@ -41,16 +41,16 @@ 21.0 - + + io.springfox diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 1e29f0e..a9282ac 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -63,16 +63,10 @@ mssql-jdbc runtime - mysql mysql-connector-java runtime - - com.alibaba - druid-spring-boot-starter - 1.1.20 - diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java index bdf5eca..90469d1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java @@ -40,6 +40,7 @@ public class MobileConfiguration { return new MobileNonceHandler() { @Override public void nonce(String mobile, int expire) { + throw new AuthenticationServiceException("未实现短信验证码登录功能"); } @Override diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 122a9f7..cc077d5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -5,6 +5,7 @@ import java.security.Principal; import java.util.Objects; import javax.servlet.http.HttpServletRequest; +import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; +import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; @@ -28,13 +30,15 @@ import cn.seqdata.oauth2.util.SecurityUtils; public class ConnectController { private final ClientRegistrationRepository repository; private final UserDetailRepo userDetailRepo; + private final MobileNonceHandler mobileNonceHandler; /** * 向手机发送一次性验证码 */ @GetMapping(value = "/mobile") public ResponseEntity mobile(String mobile, HttpServletRequest request) { - return ResponseEntity.ok("已经将验证码发送到您的手机,请注意查收!"); + mobileNonceHandler.nonce(mobile, 5 * DateTimeConstants.SECONDS_PER_MINUTE); + return ResponseEntity.ok("验证码已经发送到您手机,5分钟内输入有效!"); } @GetMapping(value = "/{registrationId}") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index c4ee154..eee454e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -5,7 +5,7 @@ import java.util.Map; import java.util.Objects; import io.swagger.annotations.ApiOperation; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java index 27cb56f..c2f1971 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java @@ -6,7 +6,6 @@ import java.util.function.BiConsumer; import org.springframework.http.HttpStatus; import org.springframework.web.server.ResponseStatusException; -import com.alibaba.fastjson.JSON; import com.aliyuncs.CommonRequest; import com.aliyuncs.CommonResponse; import com.aliyuncs.DefaultAcsClient; @@ -15,6 +14,8 @@ import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ServerException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import cn.seqdata.oauth2.AliyunProperties; @@ -50,7 +51,7 @@ public class AliyunSmsService implements BiConsumer { Map templateParams = new HashMap<>(); templateParams.put("code", Integer.toString(nonce)); - request.putQueryParameter("TemplateParam", JSON.toJSONString(templateParams)); + request.putQueryParameter("TemplateParam", toJSONString(templateParams)); try { CommonResponse response = acsClient.getCommonResponse(request); @@ -63,4 +64,9 @@ public class AliyunSmsService implements BiConsumer { } } } + + @SneakyThrows + private static String toJSONString(Object value) { + return new ObjectMapper().writeValueAsString(value); + } } diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index 620f47e..ab4ffe2 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -8,12 +8,7 @@ spring: enabled: true nacos: discovery: - server-addr: ${NACOS_SERVICE_HOST:192.168.1.241}:${NACOS_SERVICE_PORT:8848} + server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: ${CONFIG_FORMAT:yml} - # 公共文件 - shared-configs[0]: - data-id: ${SHARE_CONFIG_PREFIX:application}.${spring.cloud.nacos.config.file-extension} - # 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false - refresh: false \ No newline at end of file + file-extension: ${CONFIG_FORMAT:yml} \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index 9ff0490..ad4e0c6 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -8,12 +8,7 @@ spring: enabled: true nacos: discovery: - server-addr: ${NACOS_SERVICE_HOST:192.168.1.241}:${NACOS_SERVICE_PORT:8848} + server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: ${CONFIG_FORMAT:yml} - # 公共文件 - shared-configs[0]: - data-id: ${SHARE_CONFIG_PREFIX:application}.${spring.cloud.nacos.config.file-extension} - # 配置Data Id 在配置变更时,是否动态刷新,缺省默认 false - refresh: false \ No newline at end of file + file-extension: ${CONFIG_FORMAT:yml} \ No newline at end of file -- Gitee From 415d9ad9de200b42b7bdcfc03e5ab4ac86612e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 28 Jun 2020 14:43:07 +0800 Subject: [PATCH 034/228] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index bc023cc..c8aa125 100644 --- a/pom.xml +++ b/pom.xml @@ -41,16 +41,16 @@ 21.0 - - + io.springfox -- Gitee From deb00f742a686a8a43dec467ad88fdfbef188a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 00:36:56 +0800 Subject: [PATCH 035/228] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=9F=AD=E4=BF=A1?= =?UTF-8?q?=E5=8F=91=E9=80=81jar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 7 ++++- .../seqdata/oauth2/AliyunSmsProperties.java | 1 + .../oauth2/service/AliyunSmsService.java | 28 +++++++++---------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index a9282ac..90704f3 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -49,7 +49,12 @@ com.aliyun aliyun-java-sdk-core - 4.5.0 + 4.5.2 + + + com.aliyun + aliyun-java-sdk-dysmsapi + 2.1.0 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java index b377302..9b371f1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AliyunSmsProperties.java @@ -13,4 +13,5 @@ public class AliyunSmsProperties { private Boolean enabled; private String regionId; private String templateCode; + private String signName; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java index c2f1971..7692093 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java @@ -4,15 +4,15 @@ import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; +import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.web.server.ResponseStatusException; -import com.aliyuncs.CommonRequest; -import com.aliyuncs.CommonResponse; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ServerException; -import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.SneakyThrows; @@ -38,23 +38,21 @@ public class AliyunSmsService implements BiConsumer { @Override public void accept(String mobile, Integer nonce) { - CommonRequest request = new CommonRequest(); - request.setSysMethod(MethodType.POST); - request.setSysDomain("dysmsapi.aliyuncs.com"); - request.setSysVersion("2017-05-25"); - request.setSysAction("SendSms"); + SendSmsRequest request = new SendSmsRequest(); - request.putQueryParameter("RegionId", properties.getRegionId()); - request.putQueryParameter("TemplateCode", properties.getTemplateCode()); - - request.putQueryParameter("PhoneNumbers", mobile); + request.setPhoneNumbers(mobile); + request.setTemplateCode(properties.getTemplateCode()); + request.setSignName(properties.getSignName()); Map templateParams = new HashMap<>(); templateParams.put("code", Integer.toString(nonce)); - request.putQueryParameter("TemplateParam", toJSONString(templateParams)); - + request.setTemplateParam(toJSONString(templateParams)); try { - CommonResponse response = acsClient.getCommonResponse(request); + SendSmsResponse response = acsClient.getAcsResponse(request); + if(!StringUtils.equals("OK", response.getCode())) { + log.warn(response.getMessage()); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, response.getMessage()); + } } catch(ClientException ex) { log.warn(ex.getMessage()); if(ex instanceof ServerException) { -- Gitee From 42acf7d4b7fa9228393f962aec9feda782889516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 15:14:07 +0800 Subject: [PATCH 036/228] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E7=9A=84=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authc/src/main/resources/bootstrap.yml | 8 ++++---- seqdata-cloud-authz/src/main/resources/bootstrap.yml | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml index 33272d1..79b91a4 100644 --- a/seqdata-cloud-authc/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authc/src/main/resources/bootstrap.yml @@ -3,10 +3,10 @@ spring: name: authc cloud: nacos: - config: - server-addr: nacos.seqdata.cn:8848 - file-extension: yml discovery: - server-addr: nacos.seqdata.cn:8848 + server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + file-extension: ${CONFIG_FORMAT:yml} server: port: 30002 \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index ab4ffe2..a811d81 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -11,4 +11,6 @@ spring: server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: ${CONFIG_FORMAT:yml} \ No newline at end of file + file-extension: ${CONFIG_FORMAT:yml} +server: + port: 30001 \ No newline at end of file -- Gitee From ae3e985cea0ed6689b8dab5c3fea1daced1a13d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 16:24:25 +0800 Subject: [PATCH 037/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0connect/list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index cc077d5..19be265 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -2,6 +2,8 @@ package cn.seqdata.oauth2.controller; import java.net.URI; import java.security.Principal; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import javax.servlet.http.HttpServletRequest; @@ -13,11 +15,14 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; +import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; +import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; +import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.util.SecurityUtils; /** @@ -29,9 +34,36 @@ import cn.seqdata.oauth2.util.SecurityUtils; @AllArgsConstructor public class ConnectController { private final ClientRegistrationRepository repository; + private final UserRepo userRepo; private final UserDetailRepo userDetailRepo; private final MobileNonceHandler mobileNonceHandler; + @GetMapping(value = "/list") + public Map list(Principal principal) { + Map principals = new HashMap<>(); + + Long userId = SecurityUtils.userId(principal); + if(Objects.nonNull(userId)) { + User user = userRepo.getOne(userId); + if(Objects.nonNull(user.getUsername())) { + principals.put("username", user.getUsername()); + } + if(Objects.nonNull(user.getMobile())) { + principals.put("mobile", user.getMobile()); + } + if(Objects.nonNull(user.getEmail())) { + principals.put("email", user.getEmail()); + } + + userDetailRepo.findByUserId(userId) + .stream() + .map(UserDetail::getId) + .filter(Objects::nonNull) + .forEach(x -> principals.put(x.getClient(), x.getPrincipal())); + } + return principals; + } + /** * 向手机发送一次性验证码 */ -- Gitee From 86c5f335bd2756ddac15efe524766b7b5ced832c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 18:14:57 +0800 Subject: [PATCH 038/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0connect/list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 19be265..de94943 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -38,29 +38,31 @@ public class ConnectController { private final UserDetailRepo userDetailRepo; private final MobileNonceHandler mobileNonceHandler; - @GetMapping(value = "/list") + @GetMapping public Map list(Principal principal) { Map principals = new HashMap<>(); - Long userId = SecurityUtils.userId(principal); - if(Objects.nonNull(userId)) { - User user = userRepo.getOne(userId); - if(Objects.nonNull(user.getUsername())) { - principals.put("username", user.getUsername()); - } - if(Objects.nonNull(user.getMobile())) { - principals.put("mobile", user.getMobile()); - } - if(Objects.nonNull(user.getEmail())) { - principals.put("email", user.getEmail()); - } - - userDetailRepo.findByUserId(userId) - .stream() - .map(UserDetail::getId) - .filter(Objects::nonNull) - .forEach(x -> principals.put(x.getClient(), x.getPrincipal())); + String clientId = SecurityUtils.clientId(principal); + String username = SecurityUtils.username(principal); + UserDetail userDetail = userDetailRepo.getOne(new UserDetailId(clientId, username)); + User user = userDetail.getUser(); + + if(Objects.nonNull(user.getUsername())) { + principals.put("username", user.getUsername()); + } + if(Objects.nonNull(user.getMobile())) { + principals.put("mobile", user.getMobile()); + } + if(Objects.nonNull(user.getEmail())) { + principals.put("email", user.getEmail()); } + + userDetailRepo.findByUserId(Objects.requireNonNull(user.getId())) + .stream() + .map(UserDetail::getId) + .filter(Objects::nonNull) + .forEach(x -> principals.put(x.getClient(), x.getPrincipal())); + return principals; } -- Gitee From 51262cbd24dffc0ca014489d91ee645d460023c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 20:33:49 +0800 Subject: [PATCH 039/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0connect/list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index de94943..6808aee 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -5,8 +5,10 @@ import java.security.Principal; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Optional; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -40,30 +42,33 @@ public class ConnectController { @GetMapping public Map list(Principal principal) { - Map principals = new HashMap<>(); + Map connectors = new HashMap<>(); + Optional optional; String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); - UserDetail userDetail = userDetailRepo.getOne(new UserDetailId(clientId, username)); - User user = userDetail.getUser(); - - if(Objects.nonNull(user.getUsername())) { - principals.put("username", user.getUsername()); - } - if(Objects.nonNull(user.getMobile())) { - principals.put("mobile", user.getMobile()); - } - if(Objects.nonNull(user.getEmail())) { - principals.put("email", user.getEmail()); + if(StringUtils.equals("client", clientId)) { + optional = userRepo.findByUsernameOrMobileOrEmail(username, username, username); + } else { + UserDetail userDetail = userDetailRepo.getOne(new UserDetailId(clientId, username)); + optional = Optional.ofNullable(userDetail.getUser()); } - userDetailRepo.findByUserId(Objects.requireNonNull(user.getId())) - .stream() - .map(UserDetail::getId) - .filter(Objects::nonNull) - .forEach(x -> principals.put(x.getClient(), x.getPrincipal())); + if(optional.isPresent()) { + User user = optional.get(); + + connectors.put("username", user.getUsername()); + connectors.put("mobile", user.getMobile()); + connectors.put("email", user.getEmail()); + + userDetailRepo.findByUserId(Objects.requireNonNull(user.getId())) + .stream() + .map(UserDetail::getId) + .filter(Objects::nonNull) + .forEach(x -> connectors.put(x.getClient(), x.getPrincipal())); + } - return principals; + return connectors; } /** -- Gitee From d7355175946c040b2f68d31d0b5f8e71740f33b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 20:39:26 +0800 Subject: [PATCH 040/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0connect/list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/service/MobileNonceService.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java index c538f2e..ef16898 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java @@ -27,7 +27,7 @@ public class MobileNonceService implements MobileNonceHandler { @Override public void nonce(String mobile, int expire) { User user = userRepo.findByMobile(mobile) - .orElseThrow(() -> new ServerWebInputException("无效的手机号")); + .orElseThrow(() -> new ServerWebInputException("无法用此手机号登录")); int nonce = RandomUtils.nextInt(100000, 999999); DateTime now = DateTime.now(); @@ -41,15 +41,19 @@ public class MobileNonceService implements MobileNonceHandler { @Override public void check(String mobile, int nonce) throws AuthenticationException { User user = userRepo.findByMobile(mobile) - .orElseThrow(() -> new UsernameNotFoundException("无效的手机号")); + .orElseThrow(() -> new UsernameNotFoundException("无法用此手机号登录")); DateTime expire = user.getNonceExpire(); - if(Objects.isNull(expire) || expire.isBeforeNow()) { + Integer code = user.getNonceCode(); + if(Objects.isNull(expire) || Objects.isNull(code)) { + throw new BadCredentialsException("未获取验证码"); + } + + if(expire.isBeforeNow()) { throw new NonceExpiredException("验证码已过期"); } - Integer code = user.getNonceCode(); - if(Objects.isNull(code) || nonce != code) { + if(nonce != code) { throw new BadCredentialsException("无效的验证码"); } } -- Gitee From 8338cc606a9c01a1bafbeb701344d7bf78e35a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 21:35:13 +0800 Subject: [PATCH 041/228] =?UTF-8?q?=E5=88=86=E7=A6=BBUser=E5=92=8CAccount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/MobileConfiguration.java | 6 +- .../oauth2/controller/ConnectController.java | 22 ++++--- .../oauth2/controller/CurrentController.java | 19 +++--- .../oauth2/service/MobileNonceService.java | 20 +++--- .../seqdata/oauth2/service/UserService.java | 62 +++++++++---------- 5 files changed, 63 insertions(+), 66 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java index 90469d1..9ce5ac9 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java @@ -10,7 +10,7 @@ import org.springframework.security.authentication.AuthenticationServiceExceptio import org.springframework.security.core.AuthenticationException; import cn.seqdata.oauth2.mobile.MobileNonceHandler; -import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.AliyunSmsService; import cn.seqdata.oauth2.service.MobileNonceService; @@ -30,8 +30,8 @@ public class MobileConfiguration { @Bean @ConditionalOnBean(AliyunSmsService.class) - public MobileNonceHandler aliyunMobileNonceService(UserRepo userRepo, AliyunSmsService smsService) { - return new MobileNonceService(userRepo, smsService); + public MobileNonceHandler aliyunMobileNonceService(UserAccountRepo accountRepo, AliyunSmsService smsService) { + return new MobileNonceService(accountRepo, smsService); } @Bean diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 6808aee..9475585 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -20,11 +20,12 @@ import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; -import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.util.SecurityUtils; /** @@ -36,7 +37,7 @@ import cn.seqdata.oauth2.util.SecurityUtils; @AllArgsConstructor public class ConnectController { private final ClientRegistrationRepository repository; - private final UserRepo userRepo; + private final UserAccountRepo accountRepo; private final UserDetailRepo userDetailRepo; private final MobileNonceHandler mobileNonceHandler; @@ -44,24 +45,25 @@ public class ConnectController { public Map list(Principal principal) { Map connectors = new HashMap<>(); - Optional optional; + Optional optional; String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); if(StringUtils.equals("client", clientId)) { - optional = userRepo.findByUsernameOrMobileOrEmail(username, username, username); + optional = accountRepo.findByUsernameOrMobileOrEmail(username, username, username); } else { UserDetail userDetail = userDetailRepo.getOne(new UserDetailId(clientId, username)); - optional = Optional.ofNullable(userDetail.getUser()); + User user = userDetail.getUser(); + optional = Optional.of(accountRepo.getOne(Objects.requireNonNull(user.getId()))); } if(optional.isPresent()) { - User user = optional.get(); + UserAccount account = optional.get(); - connectors.put("username", user.getUsername()); - connectors.put("mobile", user.getMobile()); - connectors.put("email", user.getEmail()); + connectors.put("username", account.getUsername()); + connectors.put("mobile", account.getMobile()); + connectors.put("email", account.getEmail()); - userDetailRepo.findByUserId(Objects.requireNonNull(user.getId())) + userDetailRepo.findByUserId(Objects.requireNonNull(account.getId())) .stream() .map(UserDetail::getId) .filter(Objects::nonNull) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 3e32d3e..6bdb86f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -15,8 +15,9 @@ import org.springframework.web.server.ServerWebInputException; import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.perm.ActionPermRepo; -import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; @@ -29,7 +30,7 @@ import cn.seqdata.oauth2.util.SecurityUtils; @RequestMapping("/current") @AllArgsConstructor public class CurrentController { - private final UserRepo userRepo; + private final UserAccountRepo accountRepo; private final ActionPermRepo actionPermRepo; private final UserService userService; private final TokenStore tokenStore; @@ -72,19 +73,19 @@ public class CurrentController { */ @GetMapping(value = "/password") public void password(Principal principal, @RequestParam String username, @RequestParam String password) { - User entity = userRepo.getOne(SecurityUtils.userId(principal)); + UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); - Optional optional = userRepo.findByUsername(username); + Optional optional = accountRepo.findByUsername(username); if(optional.isPresent()) { - User user = optional.get(); - if(!Objects.equals(entity.getId(), user.getId())) { + UserAccount account = optional.get(); + if(!Objects.equals(entity.getId(), account.getId())) { throw new ServerWebInputException("用户名已经被使用!"); } } entity.setUsername(username); entity.setPassword(password); - userRepo.save(entity); + accountRepo.save(entity); } /** @@ -92,8 +93,8 @@ public class CurrentController { */ @GetMapping(value = "/mobile") public void mobile(Principal principal, @RequestParam String mobile, @RequestParam String nonce) { - User entity = userRepo.getOne(SecurityUtils.userId(principal)); + UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); entity.setMobile(mobile); - userRepo.save(entity); + accountRepo.save(entity); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java index ef16898..b3996d0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java @@ -11,9 +11,9 @@ import org.springframework.security.web.authentication.www.NonceExpiredException import org.springframework.web.server.ServerWebInputException; import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.mobile.MobileNonceHandler; -import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; /** * @author jrxian @@ -21,30 +21,30 @@ import cn.seqdata.oauth2.repos.rbac.UserRepo; */ @AllArgsConstructor public class MobileNonceService implements MobileNonceHandler { - private final UserRepo userRepo; + private final UserAccountRepo accountRepo; private final AliyunSmsService smsService; @Override public void nonce(String mobile, int expire) { - User user = userRepo.findByMobile(mobile) + UserAccount account = accountRepo.findByMobile(mobile) .orElseThrow(() -> new ServerWebInputException("无法用此手机号登录")); int nonce = RandomUtils.nextInt(100000, 999999); DateTime now = DateTime.now(); - user.setNonceCode(nonce); - user.setNonceExpire(now.plusSeconds(expire)); - userRepo.save(user); + account.setNonceCode(nonce); + account.setNonceExpire(now.plusSeconds(expire)); + accountRepo.save(account); smsService.accept(mobile, nonce); } @Override public void check(String mobile, int nonce) throws AuthenticationException { - User user = userRepo.findByMobile(mobile) + UserAccount account = accountRepo.findByMobile(mobile) .orElseThrow(() -> new UsernameNotFoundException("无法用此手机号登录")); - DateTime expire = user.getNonceExpire(); - Integer code = user.getNonceCode(); + DateTime expire = account.getNonceExpire(); + Integer code = account.getNonceCode(); if(Objects.isNull(expire) || Objects.isNull(code)) { throw new BadCredentialsException("未获取验证码"); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 82c7b6c..f614bc5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -10,8 +10,10 @@ import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.provider.UserProfile; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.wechat.WechatUserProfile; @@ -24,42 +26,55 @@ import cn.seqdata.oauth2.wechat.WechatUserProfile; public class UserService { private final AuthorityService authorityService; private final UserRepo userRepo; + private final UserAccountRepo accountRepo; private final UserDetailRepo userDetailRepo; /** * 将第三方认证获取的用户信息转换为本系统的 User */ @Transactional - public User createUser(String clientId, UserProfile userProfile) { + public void createUser(String clientId, UserProfile userProfile) { UserDetailId entityId = new UserDetailId(clientId, userProfile.getName()); Optional optional = userDetailRepo.findById(entityId); UserDetail userDetail = new UserDetail(entityId); //用户不存在 if(!optional.isPresent()) { - //查找微信的unionid,如果找到,视为同一个用户 if(userProfile instanceof WechatUserProfile) { - Optional userOptional = userRepo.findByUnionId(((WechatUserProfile) userProfile).getUnionId()); - if(userOptional.isPresent()) { - User user = userOptional.get(); - userDetail.setUserId(user.getId()); + //查找微信的unionid,如果找到,视为同一个用户 + String unionId = ((WechatUserProfile) userProfile).getUnionId(); + Optional accountOptional = accountRepo.findByUnionId(unionId); + if(accountOptional.isPresent()) { + UserAccount account = accountOptional.get(); + userDetail.setUserId(account.getId()); } } //没有找到微信unionid if(Objects.isNull(userDetail.getUserId())) { - //保存并且获取到保存后的userId - User user = convert(userProfile); + User user = new User(); + user.setName(userProfile.getNickname()); + user.setAvatar(userProfile.getAvatar()); user = userRepo.save(user); + + //保存并且获取到保存后的userId userDetail.setUserId(user.getId()); + + UserAccount account = new UserAccount(user.getId()); + account.setUsername(userProfile.getUsername()); + account.setEmail(userProfile.getEmail()); + account.setMobile(userProfile.getMobile()); + //第三方首次创建的账号默认是管理员 + account.setAdmin(Boolean.TRUE); + if(userProfile instanceof WechatUserProfile) { + //为微信单独保存 union_id + account.setUnionId(((WechatUserProfile) userProfile).getUnionId()); + } + accountRepo.save(account); } - userDetail = userDetailRepo.save(userDetail); - } else { - userDetail = optional.get(); + userDetailRepo.save(userDetail); } - - return userDetail.getUser(); } public User loadUser(String clientId, String username) { @@ -92,25 +107,4 @@ public class UserService { entity.setUserId(user.getId()); userDetailRepo.save(entity); } - - /** - * 根据UserProfile创建User - */ - private User convert(UserProfile userProfile) { - User user = new User(); - - user.setName(userProfile.getNickname()); - user.setUsername(userProfile.getUsername()); - user.setEmail(userProfile.getEmail()); - user.setMobile(userProfile.getMobile()); - user.setAvatar(userProfile.getAvatar()); - //第三方首次创建的账号默认是管理员 - user.setAdmin(Boolean.TRUE); - if(userProfile instanceof WechatUserProfile) { - //为微信单独保存 union_id - user.setUnionId(((WechatUserProfile) userProfile).getUnionId()); - } - - return user; - } } -- Gitee From 7da6ba08586102d5e05e24cbb8cd8110fb298e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 29 Jun 2020 21:53:10 +0800 Subject: [PATCH 042/228] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=BC=96=E7=A0=81=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/AuthzServerConfiguration.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 159bd9c..47b895e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -7,6 +7,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; @@ -58,15 +60,18 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt @Bean public PasswordEncoder passwordEncoder() { - return NoOpPasswordEncoder.getInstance(); -// return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); + if(encoder instanceof DelegatingPasswordEncoder) { + DelegatingPasswordEncoder delegating = (DelegatingPasswordEncoder) encoder; + delegating.setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance()); + } + return encoder; } @Bean public TokenStore tokenStore() { -// return new RedisTokenStore(redisConnectionFactory); RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); - redisTokenStore.setAuthenticationKeyGenerator(oAuth2Authentication -> String.valueOf(UUID.randomUUID())); + redisTokenStore.setAuthenticationKeyGenerator(x -> String.valueOf(UUID.randomUUID())); return redisTokenStore; } -- Gitee From cf95716bd5644f14316f56d8f9d20f85a85016ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 3 Jul 2020 10:00:18 +0800 Subject: [PATCH 043/228] =?UTF-8?q?=E6=81=A2=E5=A4=8Dcode=EF=BC=8C?= =?UTF-8?q?=E5=8F=96=E6=B6=88=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerApplication.java | 2 -- .../cn/seqdata/oauth2/HomeProperties.java | 14 -------- .../controller/OAuth2CodeController.java | 33 +------------------ .../src/main/resources/application.yml | 1 - 4 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index 09a3af5..03fdde7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -2,7 +2,6 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import springfox.documentation.swagger2.annotations.EnableSwagger2; @@ -12,7 +11,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; */ @SpringBootApplication @EnableDiscoveryClient -@EnableConfigurationProperties(HomeProperties.class) @EnableSwagger2 public class AuthzServerApplication { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java deleted file mode 100644 index 60ba5a2..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/HomeProperties.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.seqdata.oauth2; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * @author jrxian - * @date 2020-06-21 23:35 - */ -@lombok.Getter -@lombok.Setter -@ConfigurationProperties("spring.security.oauth2") -public class HomeProperties { - private String homeUri; -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index eee454e..eb89723 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -1,13 +1,9 @@ package cn.seqdata.oauth2.controller; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import io.swagger.annotations.ApiOperation; -import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.common.OAuth2AccessToken; @@ -18,10 +14,8 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; -import org.springframework.web.util.UriComponentsBuilder; import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.HomeProperties; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; @@ -42,35 +36,10 @@ public class OAuth2CodeController { private final TokenStore tokenStore; private final UserService userService; private final TokenService tokenService; - private final HomeProperties homeProperties; @ApiOperation("authorization_code的回调函数,处理code和state") @GetMapping(value = "/{registrationId}", params = OAuth2ParameterNames.CODE) - public ResponseEntity bind(@PathVariable String registrationId, - @RequestParam(OAuth2ParameterNames.CODE) String code, - @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { - OAuth2AccessToken accessToken = bindForPost(registrationId, code, state); - - Map params = new HashMap<>(); - params.put(OAuth2AccessToken.ACCESS_TOKEN, accessToken.getValue()); - params.put(OAuth2AccessToken.TOKEN_TYPE, accessToken.getTokenType()); - params.put(OAuth2AccessToken.EXPIRES_IN, accessToken.getExpiresIn()); - params.putAll(accessToken.getAdditionalInformation()); - - //将 accessToken 以 state 的形式传送出去,返回后用来判断被绑定的用户 - if(StringUtils.isNotBlank(homeProperties.getHomeUri())) { - return ResponseEntity.status(HttpStatus.SEE_OTHER) - .location(UriComponentsBuilder.fromUriString(homeProperties.getHomeUri()) - .build(params)) - .build(); - } else { - return ResponseEntity.ok(accessToken); - } - } - - @ApiOperation("用于小程序直接获取code后绑定用户信息") - @PostMapping(value = "/{registrationId}") - public OAuth2AccessToken bindForPost(@PathVariable String registrationId, + public OAuth2AccessToken bind(@PathVariable String registrationId, @RequestParam(OAuth2ParameterNames.CODE) String code, @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { ClientRegistration registration = repository.findByRegistrationId(registrationId); diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 4261469..e66a0cc 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -11,7 +11,6 @@ spring: port: 6379 security: oauth2: - home-uri: http://localhost/#/user/login?token={access_token} client: registration: github: -- Gitee From b8406cfb7b6994bd3ad130829e5084f62d590256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 15 Jul 2020 18:03:18 +0800 Subject: [PATCH 044/228] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/GlobalCorsConfig.java | 27 ------ .../controller/OAuth2CodeController.java | 6 +- .../seqdata/oauth2/service/UserService.java | 82 +++++++++++-------- 3 files changed, 50 insertions(+), 65 deletions(-) delete mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java deleted file mode 100644 index f77b098..0000000 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/GlobalCorsConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.seqdata.oauth2; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -/** - * Author: jrxian - * Date: 2020-02-04 12:53 - */ -@Configuration -public class GlobalCorsConfig { - - @Bean - public CorsFilter corsFilter() { - CorsConfiguration corsConfiguration = new CorsConfiguration(); - corsConfiguration.setAllowCredentials(true); - corsConfiguration.addAllowedOrigin("*"); - corsConfiguration.addAllowedHeader("*"); - corsConfiguration.addAllowedMethod("*"); - UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); - urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); - return new CorsFilter(urlBasedCorsConfigurationSource); - } -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index eb89723..4b31f64 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -49,7 +49,7 @@ public class OAuth2CodeController { //从远程拉取用户信息,以 registrationId + remoteUser.name 作为唯一识别符 OAuth2AccessTokenResponse tokenResponse = oAuth2Template.exchangeForAccess(code, state); OAuth2User remoteUser = oAuth2Template.loadUser(tokenResponse); - UserProfile userProfile = provider.profiler.apply(remoteUser); + UserProfile profile = provider.profiler.apply(remoteUser); //这里的 state 是 accessToken OAuth2Authentication authentication = tokenStore.readAuthentication(state); @@ -58,10 +58,10 @@ public class OAuth2CodeController { String localClientId = SecurityUtils.clientId(authentication); String localUsername = SecurityUtils.username(authentication); User user = userService.loadUser(localClientId, localUsername); - userService.bindUser(registrationId, authentication.getName(), user); + userService.bindUser(user.getId(), registrationId, profile); } else { //当用户不存在时自动创建 - userService.createUser(registrationId, userProfile); + userService.createUser(registrationId, profile); } return tokenService.createToken(registrationId, remoteUser); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index f614bc5..510c5f7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -4,7 +4,6 @@ import java.util.Objects; import java.util.Optional; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetail; @@ -32,18 +31,18 @@ public class UserService { /** * 将第三方认证获取的用户信息转换为本系统的 User */ - @Transactional - public void createUser(String clientId, UserProfile userProfile) { - UserDetailId entityId = new UserDetailId(clientId, userProfile.getName()); + //@Transactional //不要加事务,不然User和UserAccount不能保存 + public void createUser(String clientId, UserProfile profile) { + UserDetailId entityId = new UserDetailId(clientId, profile.getName()); Optional optional = userDetailRepo.findById(entityId); UserDetail userDetail = new UserDetail(entityId); //用户不存在 if(!optional.isPresent()) { - if(userProfile instanceof WechatUserProfile) { + if(profile instanceof WechatUserProfile) { //查找微信的unionid,如果找到,视为同一个用户 - String unionId = ((WechatUserProfile) userProfile).getUnionId(); - Optional accountOptional = accountRepo.findByUnionId(unionId); + String unionId = ((WechatUserProfile) profile).getUnionId(); + Optional accountOptional = accountRepo.findFirstByUnionId(unionId); if(accountOptional.isPresent()) { UserAccount account = accountOptional.get(); userDetail.setUserId(account.getId()); @@ -52,31 +51,33 @@ public class UserService { //没有找到微信unionid if(Objects.isNull(userDetail.getUserId())) { - User user = new User(); - user.setName(userProfile.getNickname()); - user.setAvatar(userProfile.getAvatar()); - user = userRepo.save(user); - - //保存并且获取到保存后的userId - userDetail.setUserId(user.getId()); - - UserAccount account = new UserAccount(user.getId()); - account.setUsername(userProfile.getUsername()); - account.setEmail(userProfile.getEmail()); - account.setMobile(userProfile.getMobile()); - //第三方首次创建的账号默认是管理员 - account.setAdmin(Boolean.TRUE); - if(userProfile instanceof WechatUserProfile) { - //为微信单独保存 union_id - account.setUnionId(((WechatUserProfile) userProfile).getUnionId()); - } - accountRepo.save(account); + Long userId = createUser(profile); + createUserAccount(userId, profile); + userDetail.setUserId(userId); } userDetailRepo.save(userDetail); } } + /** + * 将第三方认证的用户和本系统的用户绑定 + */ + //@Transactional //不要加事务 + public void bindUser(Long userId, String clientId, UserProfile profile) { + //如果是微信绑定,更新account的union_id + if(profile instanceof WechatUserProfile) { + UserAccount account = accountRepo.getOne(Objects.requireNonNull(userId)); + account.setUnionId(((WechatUserProfile) profile).getUnionId()); + accountRepo.save(account); + } + + UserDetailId entityId = new UserDetailId(clientId, profile.getName()); + UserDetail entity = new UserDetail(entityId); + entity.setUserId(userId); + userDetailRepo.save(entity); + } + public User loadUser(String clientId, String username) { UserDetailId entityId = new UserDetailId(clientId, username); //先从oauth_user_details中查找用户 @@ -97,14 +98,25 @@ public class UserService { return optional.orElse(null); } - /** - * 将第三方认证的用户和本系统的用户绑定 - */ - @Transactional - public void bindUser(String clientId, String username, User user) { - UserDetailId entityId = new UserDetailId(clientId, username); - UserDetail entity = new UserDetail(entityId); - entity.setUserId(user.getId()); - userDetailRepo.save(entity); + private Long createUser(UserProfile profile) { + User user = new User(); + user.setName(profile.getNickname()); + user.setAvatar(profile.getAvatar()); + User entity = userRepo.save(user); + return entity.getId(); + } + + private void createUserAccount(Long entityId, UserProfile profile) { + UserAccount account = new UserAccount(entityId); + account.setUsername(profile.getUsername()); + account.setEmail(profile.getEmail()); + account.setMobile(profile.getMobile()); + //第三方首次创建的账号默认是管理员 + account.setAdmin(Boolean.TRUE); + if(profile instanceof WechatUserProfile) { + //为微信单独保存 union_id + account.setUnionId(((WechatUserProfile) profile).getUnionId()); + } + accountRepo.save(account); } } -- Gitee From 3d78565767355f2c7420e776cc5575303b73f3ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 16 Jul 2020 16:39:29 +0800 Subject: [PATCH 045/228] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/AuthzServerConfiguration.java | 6 ++++-- .../oauth2/controller/CurrentController.java | 2 +- .../oauth2/controller/OAuth2CodeController.java | 8 ++++++-- .../cn/seqdata/oauth2/service/TokenService.java | 13 +++++++++++-- .../java/cn/seqdata/oauth2/service/UserService.java | 4 ++-- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 47b895e..885bf2f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -83,11 +83,13 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt String clientId = SecurityUtils.clientId(authentication); String username = SecurityUtils.username(authentication); - User user = userService.loadUser(clientId, username); + Optional userOptional = userService.loadUser(clientId, username); - if(Objects.nonNull(user)) { + if(userOptional.isPresent()) { + User user = userOptional.get(); attributes.put("user_id", user.getId()); attributes.put("org_id", user.getOrgId()); + attributes.put("dept_id", user.getDeptId()); } ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 6bdb86f..b80fe30 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -44,7 +44,7 @@ public class CurrentController { @ApiOperation("用户信息") @GetMapping("/user") - public User info(Principal principal) { + public Optional info(Principal principal) { String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); return userService.loadUser(clientId, username); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 4b31f64..9ba27e2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -1,6 +1,7 @@ package cn.seqdata.oauth2.controller; import java.util.Objects; +import java.util.Optional; import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; @@ -57,8 +58,11 @@ public class OAuth2CodeController { //获取当前用户身份 String localClientId = SecurityUtils.clientId(authentication); String localUsername = SecurityUtils.username(authentication); - User user = userService.loadUser(localClientId, localUsername); - userService.bindUser(user.getId(), registrationId, profile); + Optional userOptional = userService.loadUser(localClientId, localUsername); + if(userOptional.isPresent()) { + User user = userOptional.get(); + userService.bindUser(user.getId(), registrationId, profile); + } } else { //当用户不存在时自动创建 userService.createUser(registrationId, profile); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java index 58c28b1..9d72b5d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java @@ -1,7 +1,10 @@ package cn.seqdata.oauth2.service; import java.util.Collections; +import java.util.Objects; +import java.util.Set; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -19,16 +22,22 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public class TokenService { private final AuthorizationServerTokenServices tokenServices; + private final UserService userService; + private final AuthorityService authorityService; /** * 为第三方认证的用户创建本系统的 token */ public OAuth2AccessToken createToken(String clientId, OAuth2User principal) { + Set authorities = userService.loadUser(clientId, principal.getName()) + .map(user -> authorityService.loadAuthorities(Objects.requireNonNull(user.getId()))) + .orElse(Collections.emptySet()); + OAuth2Request oAuth2Request = new OAuth2Request(Collections.emptyMap(), clientId, - Collections.emptySet(), true, Collections.emptySet(), + authorities, true, Collections.emptySet(), Collections.emptySet(), null, null, null); - OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, Collections.emptyList(), clientId); + OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, authorities, clientId); OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); return tokenServices.createAccessToken(oAuth2Authentication); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 510c5f7..3be7742 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -78,7 +78,7 @@ public class UserService { userDetailRepo.save(entity); } - public User loadUser(String clientId, String username) { + public Optional loadUser(String clientId, String username) { UserDetailId entityId = new UserDetailId(clientId, username); //先从oauth_user_details中查找用户 Optional optional = userDetailRepo.findById(entityId) @@ -95,7 +95,7 @@ public class UserService { user.setAuthorities(authorityService.loadAuthorities(userId)); } - return optional.orElse(null); + return optional; } private Long createUser(UserProfile profile) { -- Gitee From cb4839fdae7e3c2ee247c5bb9c60926c5609bcca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 17 Jul 2020 11:27:22 +0800 Subject: [PATCH 046/228] =?UTF-8?q?OPTIONS=E4=B8=8D=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java index f36dc74..cdea8bf 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java @@ -6,6 +6,7 @@ import java.util.function.Predicate; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -37,10 +38,11 @@ public class PathMatcherGlobalFilter implements GlobalFilter, Ordered { public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); + HttpMethod httpMethod = request.getMethod(); RequestPath requestPath = request.getPath(); //白名单和OPTIONS,不需要做权限验证 - if(!whitelistPredicate.test(request)) { + if(HttpMethod.OPTIONS.equals(httpMethod) || !whitelistPredicate.test(request)) { //从HttpHeaders获取accessToken String accessToken = tokenResolver.resolve(request); if(Objects.isNull(accessToken)) { -- Gitee From b37775341350eb0862bf198fe7e553be1274ef97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 31 Jul 2020 19:27:19 +0800 Subject: [PATCH 047/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9A=E7=BA=A7?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/RouterController.java | 41 ++++++++++++++++++- .../oauth2/service/PermissionPredicate.java | 2 +- .../cn/seqdata/oauth2/util/RouterUtils.java | 1 + .../cn/seqdata/oauth2/util/SecurityUtils.java | 6 +++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index ec4dead..e6b7c1e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -2,6 +2,7 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; +import java.util.Objects; import java.util.stream.Collectors; import io.swagger.annotations.Api; @@ -13,7 +14,9 @@ import org.springframework.web.bind.annotation.RestController; import lombok.AllArgsConstructor; import cn.seqdata.antd.Router; +import cn.seqdata.core.domain.Named; import cn.seqdata.oauth2.domain.PermissionType; +import cn.seqdata.oauth2.jpa.perm.ModulePermission; import cn.seqdata.oauth2.repos.perm.ModulePermRepo; import cn.seqdata.oauth2.repos.perm.SysPermRepo; import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; @@ -35,8 +38,11 @@ public class RouterController { @GetMapping public Collection routers(Principal principal, @SortDefault("orderNo") Sort sort) { + Collection modules = modules(principal, sort); + Collection routers = topRouters(principal, sort); - routers.forEach(router -> router.children.addAll(childRouters(router, principal, sort))); + routers.forEach(router -> doRouter(router, modules)); + return routers; } @@ -56,7 +62,38 @@ public class RouterController { .collect(Collectors.toList()); } - private long toLong(Object key) { + private Collection modules(Principal principal, Sort sort) { + return modulePermRepo.findAll(sort) + .stream() + .filter(new PermissionPredicate(permissionRepo, PermissionType.module, principal)) + .collect(Collectors.toList()); + } + + private void doRouter(Router router, Collection modules) { + modules.stream() + .filter(module -> Objects.equals(toLong(router.key), getId(module.getSysPerm()))) + .forEach(module -> { + Router childRouter = RouterUtils.toRouter(router, module); + router.children.add(childRouter); + doChildRouter(childRouter, modules); + }); + } + + private void doChildRouter(Router router, Collection modules) { + modules.stream() + .filter(module -> Objects.equals(toLong(router.key), module.getParentId())) + .forEach(module -> { + Router childRouter = RouterUtils.toRouter(router, module); + router.children.add(childRouter); + doChildRouter(childRouter, modules); + }); + } + + private static Long getId(Named entity) { + return entity.getId(); + } + + private static Long toLong(Object key) { if(key instanceof Number) { return ((Number) key).longValue(); } else { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java index f77eb72..e2fe5f7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java @@ -25,7 +25,7 @@ public class PermissionPredicate implements Predicate { public PermissionPredicate(AuthorityPermissionRepo permissionRepo, PermissionType permType, Principal principal) { Set authorities = SecurityUtils.authorities(principal); - sysAdmin = authorities.contains("sysAdmin"); + sysAdmin = authorities.contains(SecurityUtils.ROLE_SYS_ADMIN); if(sysAdmin) { permissions = Collections.emptySet(); } else { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java index 4cfcfa3..cad98a5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java @@ -29,6 +29,7 @@ public final class RouterUtils { public static Router toRouter(Router parent, ModulePermission modulePerm) { Router router = createRouter(parent, modulePerm); + router.meta.group = modulePerm.getGroup(); router.meta.icon = modulePerm.getIcon(); router.meta.params = modulePerm.getParams(); router.meta.query = modulePerm.getQuery(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index e8f5f96..dbee1f5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -19,6 +19,8 @@ import cn.seqdata.oauth2.provider.OAuth2Provider; * Date: 2020-01-27 01:59 */ public final class SecurityUtils { + public static final String ROLE_SYS_ADMIN = "sysAdmin"; + private SecurityUtils() { } @@ -78,6 +80,10 @@ public final class SecurityUtils { return toLong(detailValue(details(principal), "org_id")); } + public static boolean isSysAdmin(Principal principal) { + return authorities(principal).contains(ROLE_SYS_ADMIN); + } + /** * 拥有角色 */ -- Gitee From ba64e3358880c1dac861be7624c18274da989173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 2 Aug 2020 11:09:52 +0800 Subject: [PATCH 048/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9A=E7=BA=A7?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/RouterController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index e6b7c1e..38f40a3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -71,7 +71,8 @@ public class RouterController { private void doRouter(Router router, Collection modules) { modules.stream() - .filter(module -> Objects.equals(toLong(router.key), getId(module.getSysPerm()))) + .filter(module -> Objects.isNull(module.getParentId()) + && Objects.equals(toLong(router.key), getId(module.getSysPerm()))) .forEach(module -> { Router childRouter = RouterUtils.toRouter(router, module); router.children.add(childRouter); -- Gitee From 24ddf44a85e254e3a0855bf762354571db7a2b22 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Mon, 10 Aug 2020 12:44:40 +0800 Subject: [PATCH 049/228] =?UTF-8?q?gateway:=20=E6=B7=BB=E5=8A=A0=E6=97=A0t?= =?UTF-8?q?oken=20=E7=99=BD=E5=90=8D=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/gateway/filter/authc/WhitelistPredicate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java index 2bea12d..88262e7 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/WhitelistPredicate.java @@ -23,7 +23,7 @@ public class WhitelistPredicate implements Predicate { public WhitelistPredicate() { //authz: 授权服务器是否需要token由授权服务器决定,网关不做前置拦截 whitelists.addAll(Arrays.asList("/favicon.ico", "/authz/**", - "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/*/v2/api-docs/**")); + "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/*/v2/api-docs/**", "/**/anonymous/**")); } public WhitelistPredicate(Collection whitelists) { -- Gitee From a688084b1a7c9f664025b11183855ac109a16fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 20 Aug 2020 19:30:58 +0800 Subject: [PATCH 050/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0joda=E5=92=8Cspring.d?= =?UTF-8?q?ata=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authc/pom.xml | 8 ++++ .../seqdata/oauth2/SwaggerConfiguration.java | 3 +- .../oauth2/WebSecurityConfiguration.java | 6 +-- .../oauth2/controller/JodaController.java | 38 +++++++++++++++++++ .../controller/SpringDataController.java | 27 +++++++++++++ .../oauth2/controller/UserInfoController.java | 2 - .../oauth2/controller/ConnectController.java | 4 +- 7 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml index ccc8453..2786289 100644 --- a/seqdata-cloud-authc/pom.xml +++ b/seqdata-cloud-authc/pom.xml @@ -10,6 +10,14 @@ seqdata-cloud-authc 资源服务器,所有的业务模块应该继承自资源服务器,自动和授权服务器交互实现认证和授权 + + joda-time + joda-time + + + org.springframework.data + spring-data-commons + org.springframework.boot spring-boot-starter-web diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java index 900563a..a72e92b 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java @@ -29,7 +29,8 @@ public class SwaggerConfiguration { public Docket apiDocker() { return new Docket(DocumentationType.SWAGGER_2) // .securitySchemes(Collections.singletonList(password())) - .globalOperationParameters(operationParameters()); +// .globalOperationParameters(operationParameters()) + ; } private SecurityScheme password() { diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java index f1bcec7..0bffd00 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -20,9 +20,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { super.configure(web); web.ignoring() - .antMatchers("/favicon.ico", - "/resources/**", "/css/**", "/js/**", - "/swagger-ui.html", "/webjars/**", "/plugins/**" + .antMatchers("/favicon.ico" + , "/resources/**", "/css/**", "/js/**" + , "/swagger-ui.html", "/webjars/**", "/plugins/**" ); } diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java new file mode 100644 index 0000000..120f303 --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java @@ -0,0 +1,38 @@ +package cn.seqdata.oauth2.controller; + +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author jrxian + * @date 2020-08-20 16:42 + */ +@RestController +@RequestMapping("/joda") +public class JodaController { + @GetMapping("/date") + public LocalDate date(@RequestParam(required = false) LocalDate key) { + return key; + } + + @GetMapping("/time") + public LocalTime time(@RequestParam(required = false) LocalTime key) { + return key; + } + + @GetMapping("/datetime") + public DateTime datetime(@RequestParam(required = false) DateTime key) { + return key; + } + + @GetMapping("/localdatetime") + public LocalDateTime localdatetime(@RequestParam(required = false) LocalDateTime key) { + return key; + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java new file mode 100644 index 0000000..3b818af --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java @@ -0,0 +1,27 @@ +package cn.seqdata.oauth2.controller; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.data.web.SortDefault; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author jrxian + * @date 2020-08-20 16:43 + */ +@RestController +@RequestMapping("/spring/data") +public class SpringDataController { + @GetMapping("/sort") + public Sort sort(String key, @SortDefault("id") Sort sort) { + return sort; + } + + @GetMapping("/pageable") + public Pageable pageable(String key, @PageableDefault(sort = "id") Pageable pageable) { + return pageable; + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java index 5db2c80..7153cd9 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java @@ -7,7 +7,6 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import lombok.AllArgsConstructor; /** * Author: jrxian @@ -15,7 +14,6 @@ import lombok.AllArgsConstructor; */ @RestController @RequestMapping("/user") -@AllArgsConstructor public class UserInfoController { @ApiOperation("通行证") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 9475585..798c0a7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -83,7 +83,7 @@ public class ConnectController { } @GetMapping(value = "/{registrationId}") - public ResponseEntity connect(@PathVariable String registrationId, HttpServletRequest request) { + public ResponseEntity bind(@PathVariable String registrationId, HttpServletRequest request) { ClientRegistration registration = repository.findByRegistrationId(registrationId); OAuth2Provider provider = SecurityUtils.provider(registration); OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); @@ -107,7 +107,7 @@ public class ConnectController { } @DeleteMapping(value = "/{registrationId}") - public void disconnect(@PathVariable String registrationId, Principal principal) { + public void unbind(@PathVariable String registrationId, Principal principal) { UserDetailId entityId = new UserDetailId(registrationId, principal.getName()); userDetailRepo.deleteById(entityId); } -- Gitee From 7e685d1208092a19e03d4eba2d9a610a37edb355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 29 Aug 2020 13:30:57 +0800 Subject: [PATCH 051/228] =?UTF-8?q?router=E6=8C=89sys=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E4=B8=94=E6=94=AF=E6=8C=81actions=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E5=88=B0=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/RouterController.java | 91 +++---------- .../oauth2/service/PermissionPredicate.java | 4 +- .../seqdata/oauth2/service/RouterService.java | 126 ++++++++++++++++++ .../cn/seqdata/oauth2/util/RouterUtils.java | 1 + 4 files changed, 150 insertions(+), 72 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index 38f40a3..63dfa5a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -3,24 +3,22 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; import java.util.Objects; -import java.util.stream.Collectors; import io.swagger.annotations.Api; import org.springframework.data.domain.Sort; import org.springframework.data.web.SortDefault; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.google.common.collect.LinkedListMultimap; +import com.google.common.collect.Multimap; import lombok.AllArgsConstructor; import cn.seqdata.antd.Router; -import cn.seqdata.core.domain.Named; -import cn.seqdata.oauth2.domain.PermissionType; import cn.seqdata.oauth2.jpa.perm.ModulePermission; -import cn.seqdata.oauth2.repos.perm.ModulePermRepo; import cn.seqdata.oauth2.repos.perm.SysPermRepo; -import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; -import cn.seqdata.oauth2.service.PermissionPredicate; +import cn.seqdata.oauth2.service.RouterService; import cn.seqdata.oauth2.util.RouterUtils; /** @@ -33,72 +31,27 @@ import cn.seqdata.oauth2.util.RouterUtils; @AllArgsConstructor public class RouterController { private final SysPermRepo sysPermRepo; - private final ModulePermRepo modulePermRepo; - private final AuthorityPermissionRepo permissionRepo; + private final RouterService service; @GetMapping - public Collection routers(Principal principal, @SortDefault("orderNo") Sort sort) { - Collection modules = modules(principal, sort); - - Collection routers = topRouters(principal, sort); - routers.forEach(router -> doRouter(router, modules)); - - return routers; - } - - private Collection topRouters(Principal principal, Sort sort) { - return sysPermRepo.findAll(sort) - .stream() - .filter(new PermissionPredicate(permissionRepo, PermissionType.sys, principal)) - .map(RouterUtils::toRouter) - .collect(Collectors.toList()); - } - - private Collection childRouters(Router parent, Principal principal, Sort sort) { - return modulePermRepo.findBySysPermId(toLong(parent.key), sort) - .stream() - .filter(new PermissionPredicate(permissionRepo, PermissionType.module, principal)) - .map(modulePerm -> RouterUtils.toRouter(parent, modulePerm)) - .collect(Collectors.toList()); - } - - private Collection modules(Principal principal, Sort sort) { - return modulePermRepo.findAll(sort) - .stream() - .filter(new PermissionPredicate(permissionRepo, PermissionType.module, principal)) - .collect(Collectors.toList()); - } - - private void doRouter(Router router, Collection modules) { - modules.stream() - .filter(module -> Objects.isNull(module.getParentId()) - && Objects.equals(toLong(router.key), getId(module.getSysPerm()))) - .forEach(module -> { - Router childRouter = RouterUtils.toRouter(router, module); - router.children.add(childRouter); - doChildRouter(childRouter, modules); - }); - } - - private void doChildRouter(Router router, Collection modules) { - modules.stream() - .filter(module -> Objects.equals(toLong(router.key), module.getParentId())) - .forEach(module -> { - Router childRouter = RouterUtils.toRouter(router, module); - router.children.add(childRouter); - doChildRouter(childRouter, modules); - }); - } - - private static Long getId(Named entity) { - return entity.getId(); - } - - private static Long toLong(Object key) { - if(key instanceof Number) { - return ((Number) key).longValue(); + public Collection routers(@RequestParam(required = false) Long sys, + Principal principal, @SortDefault("orderNo") Sort sort) { + //获取有权限的全部module + Collection modulePerms = service.fetchModulePerms(principal, sort); + //获取有权限的全部action,并且按view做好分组 + Multimap actionPerms = LinkedListMultimap.create(); + service.fetchActionPerms(principal, sort) + .forEach(x -> actionPerms.put(x.getViewId(), x.getIdentifier())); + + if(Objects.nonNull(sys)) { + //如果指定了sys,只查询本sys下的module + Router topRouter = RouterUtils.toRouter(sysPermRepo.getOne(sys)); + service.doRouter(topRouter, modulePerms, actionPerms); + return topRouter.children; } else { - return 0L; + Collection routers = service.topRouters(principal, sort); + routers.forEach(router -> service.doRouter(router, modulePerms, actionPerms)); + return routers; } } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java index e2fe5f7..1dabcac 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java @@ -6,8 +6,6 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; -import org.apache.commons.lang3.BooleanUtils; - import cn.seqdata.core.jpa.JpaIdGenerated; import cn.seqdata.oauth2.domain.PermissionType; import cn.seqdata.oauth2.jpa.perm.JpaPermission; @@ -36,7 +34,7 @@ public class PermissionPredicate implements Predicate { @Override public boolean test(JpaPermission permission) { //系统管理员,模块不需要权限,是否包含所需权限 - return sysAdmin || BooleanUtils.isTrue(permission.getVisible()) || permissions.contains(permission.getId()); + return sysAdmin || permissions.contains(permission.getId()); } /** diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java new file mode 100644 index 0000000..8449ff0 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java @@ -0,0 +1,126 @@ +package cn.seqdata.oauth2.service; + +import java.security.Principal; +import java.util.Collection; +import java.util.Objects; +import java.util.function.BiPredicate; +import java.util.stream.Collectors; + +import org.apache.commons.lang.BooleanUtils; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import com.google.common.collect.Multimap; +import lombok.AllArgsConstructor; + +import cn.seqdata.antd.Router; +import cn.seqdata.core.domain.Named; +import cn.seqdata.oauth2.domain.PermissionType; +import cn.seqdata.oauth2.jpa.perm.ActionPermission; +import cn.seqdata.oauth2.jpa.perm.JpaPermission; +import cn.seqdata.oauth2.jpa.perm.ModulePermission; +import cn.seqdata.oauth2.repos.perm.ActionPermRepo; +import cn.seqdata.oauth2.repos.perm.JpaPermRepository; +import cn.seqdata.oauth2.repos.perm.ModulePermRepo; +import cn.seqdata.oauth2.repos.perm.SysPermRepo; +import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; +import cn.seqdata.oauth2.util.RouterUtils; + +/** + * @author jrxian + * @date 2020-08-29 10:17 + */ +@Service +@AllArgsConstructor +public class RouterService { + private final SysPermRepo sysPermRepo; + private final ModulePermRepo modulePermRepo; + private final ActionPermRepo actionPermRepo; + private final AuthorityPermissionRepo permissionRepo; + + /** + * 所有菜单 + */ + public Collection fetchModulePerms(Principal principal, Sort sort) { + return fetchPermissions(modulePermRepo, PermissionType.module, principal, sort); + } + + /** + * 所有按钮 + */ + public Collection fetchActionPerms(Principal principal, Sort sort) { + return fetchPermissions(actionPermRepo, PermissionType.action, principal, sort); + } + + private Collection fetchPermissions( + JpaPermRepository repository, PermissionType permType, Principal principal, Sort sort) { + return repository.findAll(sort) + .stream() + .filter(x -> BooleanUtils.isNotFalse(x.getVisible())) + .filter(new PermissionPredicate(permissionRepo, permType, principal)) + .collect(Collectors.toList()); + } + + public Collection topRouters(Principal principal, Sort sort) { + return sysPermRepo.findAll(sort) + .stream() + .filter(x -> BooleanUtils.isNotFalse(x.getVisible())) + .filter(new PermissionPredicate(permissionRepo, PermissionType.sys, principal)) + .map(RouterUtils::toRouter) + .collect(Collectors.toList()); + } + + public Collection childRouters(Router parent, Principal principal, Sort sort) { + return modulePermRepo.findBySysPermId(toLong(parent.key), sort) + .stream() + .filter(x -> BooleanUtils.isNotFalse(x.getVisible())) + .filter(new PermissionPredicate(permissionRepo, PermissionType.module, principal)) + .map(modulePerm -> RouterUtils.toRouter(parent, modulePerm)) + .collect(Collectors.toList()); + } + + public void doRouter(Router router, Collection modulePerms, Multimap actionPerms) { + doNestedRouter(router, this::topRouterFilter, modulePerms, actionPerms); + } + + /** + * 顶层路由,顶层路由和下级路由最大的区别是顶层按sysId和parent为空过滤 + */ + private void doNestedRouter(Router router, BiPredicate predicate, + Collection modulePerms, Multimap actionPerms) { + modulePerms.stream() + .filter(modulePerm -> predicate.test(router, modulePerm)) + .forEach(modulePerm -> { + Router childRouter = RouterUtils.toRouter(router, modulePerm); + router.meta.actions.addAll(actionPerms.get(router.meta.view)); + router.children.add(childRouter); + doNestedRouter(childRouter, this::chdRouterFilter, modulePerms, actionPerms); + }); + } + + /** + * 顶层路由过滤规则,按sysId和parent为空过滤 + */ + private boolean topRouterFilter(Router router, ModulePermission modulePerm) { + return Objects.isNull(modulePerm.getParentId()) + && Objects.equals(toLong(router.key), getId(modulePerm.getSysPerm())); + } + + /** + * 普通路由过滤规则,按parent过滤 + */ + private boolean chdRouterFilter(Router router, ModulePermission modulePerm) { + return Objects.equals(toLong(router.key), modulePerm.getParentId()); + } + + private static Long getId(Named entity) { + return entity.getId(); + } + + private static Long toLong(Object key) { + if(key instanceof Number) { + return ((Number) key).longValue(); + } else { + return 0L; + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java index cad98a5..9e1123a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/RouterUtils.java @@ -38,6 +38,7 @@ public final class RouterUtils { ViewPermission viewPerm = modulePerm.getViewPerm(); if(Objects.nonNull(viewPerm)) { router.component = viewPerm.getComponent(); + router.meta.view = viewPerm.getId(); router.meta.keepAlive = viewPerm.getKeepAlive(); } else { router.redirect = modulePerm.getRedirect(); -- Gitee From 70b1a5a526d429cea1e4e47b6b16eb2a8c52b4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 31 Aug 2020 16:56:39 +0800 Subject: [PATCH 052/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 10 +-- .../oauth2/AuthcServerConfiguration.java | 3 +- .../cn/seqdata/oauth2/WxAppConfiguration.java | 30 ++++++++ .../controller/OAuth2CodeController.java | 2 +- .../oauth2/controller/RouterController.java | 12 ++-- .../oauth2/controller/WxAppController.java | 68 ++++++++++++++++++- .../oauth2/provider/OAuth2Provider.java | 6 +- .../seqdata/oauth2/service/RouterService.java | 2 +- .../seqdata/oauth2/service/TokenService.java | 23 +++++-- .../wechat/WechatRestTemplateCustomizer.java | 1 - .../oauth2/wxapp/Code2SessionRequest.java | 14 ---- .../oauth2/wxapp/Code2SessionResponse.java | 15 ---- .../oauth2/wxapp/WxAppFeignClient.java | 15 ---- .../seqdata/oauth2/wxapp/WxAppOAuth2User.java | 37 ++++++++++ .../seqdata/oauth2/wxapp/WxAppProvider.java | 43 ++++++++++++ .../cn/seqdata/oauth2/wxapp/WxAppService.java | 59 ++++++++++++++++ 16 files changed, 270 insertions(+), 70 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 90704f3..34df85b 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -41,11 +41,6 @@ spring-security-oauth2-autoconfigure - - org.springframework.cloud - spring-cloud-starter-openfeign - - com.aliyun aliyun-java-sdk-core @@ -57,6 +52,11 @@ 2.1.0 + + cn.seqdata.cloud + seqdata-cloud-wechat + ${project.version} + cn.seqdata.cloud seqdata-cloud-oauth2 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index e24dacc..33f1d9e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -22,7 +22,8 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.authorizeRequests() .antMatchers( "/v2/api-docs/**", "/swagger-resources/**", - "/connect/**", "/oauth/code/**", "/rbac/**", "/perm/**") + "/rbac/**", "/perm/**", + "/connect/**", "/oauth/code/**", "/wxapp/**") .permitAll(); super.configure(http); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java new file mode 100644 index 0000000..4176476 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java @@ -0,0 +1,30 @@ +package cn.seqdata.oauth2; + +import java.util.Collections; +import java.util.List; + +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; + +import cn.seqdata.wxapp.WxAppFeignClient; + +/** + * @author jrxian + * @date 2020-08-31 01:37 + */ +@Configuration +@EnableFeignClients(basePackageClasses = WxAppFeignClient.class) +public class WxAppConfiguration { + + @Bean + public HttpMessageConverter messageConverter() { + List supportedMediaTypes = Collections.singletonList(MediaType.TEXT_PLAIN); + MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); + jackson2HttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes); + return jackson2HttpMessageConverter; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 9ba27e2..405fa3c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -50,7 +50,7 @@ public class OAuth2CodeController { //从远程拉取用户信息,以 registrationId + remoteUser.name 作为唯一识别符 OAuth2AccessTokenResponse tokenResponse = oAuth2Template.exchangeForAccess(code, state); OAuth2User remoteUser = oAuth2Template.loadUser(tokenResponse); - UserProfile profile = provider.profiler.apply(remoteUser); + UserProfile profile = provider.profile.apply(remoteUser); //这里的 state 是 accessToken OAuth2Authentication authentication = tokenStore.readAuthentication(state); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index 63dfa5a..e26b0bd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -41,17 +41,17 @@ public class RouterController { //获取有权限的全部action,并且按view做好分组 Multimap actionPerms = LinkedListMultimap.create(); service.fetchActionPerms(principal, sort) - .forEach(x -> actionPerms.put(x.getViewId(), x.getIdentifier())); + .forEach(x -> actionPerms.put(x.getViewPermId(), x.getIdentifier())); - if(Objects.nonNull(sys)) { + if(Objects.isNull(sys)) { + Collection routers = service.topRouters(principal, sort); + routers.forEach(router -> service.doRouter(router, modulePerms, actionPerms)); + return routers; + } else { //如果指定了sys,只查询本sys下的module Router topRouter = RouterUtils.toRouter(sysPermRepo.getOne(sys)); service.doRouter(topRouter, modulePerms, actionPerms); return topRouter.children; - } else { - Collection routers = service.topRouters(principal, sort); - routers.forEach(router -> service.doRouter(router, modulePerms, actionPerms)); - return routers; } } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index bd66184..737ff03 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -1,13 +1,79 @@ package cn.seqdata.oauth2.controller; +import java.util.Optional; + +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.oauth.UserDetail; +import cn.seqdata.oauth2.jpa.oauth.UserDetailId; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.service.TokenService; +import cn.seqdata.oauth2.wxapp.WxAppOAuth2User; +import cn.seqdata.oauth2.wxapp.WxAppService; +import cn.seqdata.wxapp.message.Code2SessionResponse; /** * @author jrxian * @date 2020-06-22 09:17 */ @RestController -@RequestMapping("/wxapp") +@RequestMapping("/wxapp/{id}") +@AllArgsConstructor public class WxAppController { + private final WxAppService service; + private final TokenService tokenService; + private final TokenStore tokenStore; + private final UserAccountRepo userAccountRepo; + private final UserDetailRepo userDetailRepo; + + @GetMapping("/signin") + public OAuth2AccessToken signin(@PathVariable("id") String id, String code) { + Code2SessionResponse response = service.signin(id, code); + + UserDetailId entityId = new UserDetailId(id, response.getOpenid()); + UserDetail userDetail = userDetailRepo.findById(entityId) + .orElseGet(() -> { + //用户不存在,创建 + UserDetail supplier = new UserDetail(entityId); + //在系统中查询到unionid,绑定到该userid + Optional accountOptional = userAccountRepo.findFirstByUnionId(response.getUnionid()); + if(accountOptional.isPresent()) { + UserAccount userAccount = accountOptional.get(); + supplier.setUserId(userAccount.getId()); + } + return userDetailRepo.save(supplier); + }); + + OAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(response.getSession_key()); + OAuth2User principal = new WxAppOAuth2User(response); + OAuth2Authentication authentication = tokenService.createOAuth2Authentication(id, principal); + tokenStore.storeAccessToken(accessToken, authentication); + + return accessToken; + } + + @GetMapping("/signout") + public void signout(@PathVariable("id") String id) { + } + + @GetMapping("/info") + public OAuth2AccessToken info(@PathVariable("id") String id, String iv, String data) { + return null; + } + + @GetMapping("/token") + public String token(@PathVariable("id") String id) { + return service.accessToken(id); + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java index 75cd5d2..8d5bbbc 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java @@ -65,11 +65,11 @@ public enum OAuth2Provider { }; public final String hostname; - public final Function profiler; + public final Function profile; - OAuth2Provider(String hostname, Function profiler) { + OAuth2Provider(String hostname, Function profile) { this.hostname = hostname; - this.profiler = profiler; + this.profile = profile; } public static OAuth2Provider fromString(String providerId) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java index 8449ff0..6463f2e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java @@ -6,7 +6,7 @@ import java.util.Objects; import java.util.function.BiPredicate; import java.util.stream.Collectors; -import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.BooleanUtils; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import com.google.common.collect.Multimap; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java index 9d72b5d..85c1f2e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java @@ -1,8 +1,8 @@ package cn.seqdata.oauth2.service; +import java.util.Collection; import java.util.Collections; import java.util.Objects; -import java.util.Set; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; @@ -29,16 +29,25 @@ public class TokenService { * 为第三方认证的用户创建本系统的 token */ public OAuth2AccessToken createToken(String clientId, OAuth2User principal) { - Set authorities = userService.loadUser(clientId, principal.getName()) + return tokenServices.createAccessToken(createOAuth2Authentication(clientId, principal)); + } + + public OAuth2Authentication createOAuth2Authentication(String clientId, OAuth2User principal) { + Collection authorities = loadAuthorities(clientId, principal); + OAuth2Request oAuth2Request = createOAuth2Request(clientId, authorities); + OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal, authorities, clientId); + return new OAuth2Authentication(oAuth2Request, authenticationToken); + } + + public Collection loadAuthorities(String clientId, OAuth2User principal) { + return userService.loadUser(clientId, principal.getName()) .map(user -> authorityService.loadAuthorities(Objects.requireNonNull(user.getId()))) .orElse(Collections.emptySet()); + } - OAuth2Request oAuth2Request = new OAuth2Request(Collections.emptyMap(), clientId, + public static OAuth2Request createOAuth2Request(String clientId, Collection authorities) { + return new OAuth2Request(Collections.emptyMap(), clientId, authorities, true, Collections.emptySet(), Collections.emptySet(), null, null, null); - - OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(principal, authorities, clientId); - OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); - return tokenServices.createAccessToken(oAuth2Authentication); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java index 75db1e4..8ba7504 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatRestTemplateCustomizer.java @@ -21,7 +21,6 @@ public class WechatRestTemplateCustomizer implements RestTemplateCustomizer { @Override public void customize(RestTemplate restTemplate) { - List> messageConverters = restTemplate.getMessageConverters(); messageConverters.add(new FormHttpMessageConverter()); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java deleted file mode 100644 index 1008f44..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionRequest.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.seqdata.oauth2.wxapp; - -/** - * @author jrxian - * @date 2020-06-22 09:27 - */ -@lombok.Getter -@lombok.Setter -public class Code2SessionRequest { - private String appid; - private String secret; - private String js_code; - private String grant_type; -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java deleted file mode 100644 index b546352..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/Code2SessionResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.seqdata.oauth2.wxapp; - -/** - * @author jrxian - * @date 2020-06-22 09:28 - */ -@lombok.Getter -@lombok.Setter -public class Code2SessionResponse { - private String openid; - private String session_key; - private String unionid; - private String errcode; - private String errmsg; -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java deleted file mode 100644 index 46c59aa..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppFeignClient.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.seqdata.oauth2.wxapp; - -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestBody; - -/** - * @author jrxian - * @date 2020-06-22 09:21 - */ -@FeignClient(url = "https://api.weixin.qq.com") -public interface WxAppFeignClient { - @GetMapping("/sns/jscode2session") - Code2SessionResponse code2session(@RequestBody Code2SessionRequest request); -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java new file mode 100644 index 0000000..4e25b22 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java @@ -0,0 +1,37 @@ +package cn.seqdata.oauth2.wxapp; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import cn.seqdata.wxapp.message.Code2SessionResponse; + +/** + * @author jrxian + * @date 2020-08-31 16:47 + */ +public class WxAppOAuth2User implements OAuth2User { + private final String name; + + public WxAppOAuth2User(Code2SessionResponse response) { + this.name = response.getOpenid(); + } + + @Override + public String getName() { + return name; + } + + @Override + public Map getAttributes() { + return Collections.emptyMap(); + } + + @Override + public Collection getAuthorities() { + return Collections.emptyList(); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java new file mode 100644 index 0000000..3dde52e --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java @@ -0,0 +1,43 @@ +package cn.seqdata.oauth2.wxapp; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.support.JdbcDaoSupport; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.stereotype.Component; + +/** + * @author jrxian + * @date 2020-08-30 23:26 + */ +@Component +public class WxAppProvider extends JdbcDaoSupport implements Function { + private final Map providers = new HashMap<>(); + + public WxAppProvider(JdbcTemplate jdbcTemplate) { + setJdbcTemplate(jdbcTemplate); + } + + @Override + public ClientRegistration apply(String id) { + return providers.get(id); + } + + @Override + protected void initDao() { + String sql = "SELECT id, client_id, client_secret FROM oauth_registration WHERE provider_id = ?"; + Objects.requireNonNull(getJdbcTemplate()) + .query(sql, rs -> { + ClientRegistration registration = ClientRegistration + .withRegistrationId(rs.getString("id")) + .clientId(rs.getString("client_id")) + .clientSecret(rs.getString("client_secret")) + .build(); + providers.put(registration.getRegistrationId(), registration); + }, "wxapp"); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java new file mode 100644 index 0000000..a773535 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -0,0 +1,59 @@ +package cn.seqdata.oauth2.wxapp; + +import java.util.concurrent.TimeUnit; + +import org.joda.time.DateTimeConstants; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.stereotype.Service; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import lombok.AllArgsConstructor; + +import cn.seqdata.wxapp.WxAppException; +import cn.seqdata.wxapp.WxAppFeignClient; +import cn.seqdata.wxapp.message.AccessTokenRequest; +import cn.seqdata.wxapp.message.AccessTokenResponse; +import cn.seqdata.wxapp.message.Code2SessionRequest; +import cn.seqdata.wxapp.message.Code2SessionResponse; + +/** + * @author jrxian + * @date 2020-08-31 00:33 + */ +@Service +@AllArgsConstructor +public class WxAppService { + private final LoadingCache cache = CacheBuilder.newBuilder() + .expireAfterWrite(DateTimeConstants.SECONDS_PER_HOUR, TimeUnit.SECONDS) + .build(new CacheLoader() { + @Override + public String load(String id) { + AccessTokenRequest request = new AccessTokenRequest(); + ClientRegistration registration = provider.apply(id); + request.setAppid(registration.getClientId()); + request.setSecret(registration.getClientSecret()); + AccessTokenResponse response = feignClient.getAccessToken(request); + WxAppException.check(response); + return response.getAccess_token(); + } + }); + + private final WxAppProvider provider; + private final WxAppFeignClient feignClient; + + public String accessToken(String appid) { + return cache.getUnchecked(appid); + } + + public Code2SessionResponse signin(String id, String code) { + Code2SessionRequest request = new Code2SessionRequest(); + ClientRegistration registration = provider.apply(id); + request.setAppid(registration.getClientId()); + request.setSecret(registration.getClientSecret()); + request.setJs_code(code); + Code2SessionResponse response = feignClient.code2session(request); + WxAppException.check(response); + return response; + } +} -- Gitee From a71afd7b0e884084ee09554ea5b3a6a024383079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 31 Aug 2020 17:18:42 +0800 Subject: [PATCH 053/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/wxapp/ClientProperties.java | 14 ++++++ .../seqdata/oauth2/wxapp/WxAppProvider.java | 45 +++++++++++-------- .../cn/seqdata/oauth2/wxapp/WxAppService.java | 13 +++--- 3 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/ClientProperties.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/ClientProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/ClientProperties.java new file mode 100644 index 0000000..826f2f7 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/ClientProperties.java @@ -0,0 +1,14 @@ +package cn.seqdata.oauth2.wxapp; + +/** + * @author jrxian + * @date 2020-08-31 17:13 + */ +@lombok.Data +@lombok.Builder +@lombok.AllArgsConstructor +public class ClientProperties { + private final String id; + private String clientId; + private String clientSecret; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java index 3dde52e..948b16b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java @@ -5,39 +5,46 @@ import java.util.Map; import java.util.Objects; import java.util.function.Function; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.support.JdbcDaoSupport; -import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.oauth.Provider; +import cn.seqdata.oauth2.repos.oauth.RegistrationRepo; /** * @author jrxian * @date 2020-08-30 23:26 */ @Component -public class WxAppProvider extends JdbcDaoSupport implements Function { - private final Map providers = new HashMap<>(); +@AllArgsConstructor +public class WxAppProvider implements Function, InitializingBean { + private final Map providers = new HashMap<>(); - public WxAppProvider(JdbcTemplate jdbcTemplate) { - setJdbcTemplate(jdbcTemplate); - } + private final RegistrationRepo registrationRepo; @Override - public ClientRegistration apply(String id) { + public ClientProperties apply(String id) { return providers.get(id); } @Override - protected void initDao() { - String sql = "SELECT id, client_id, client_secret FROM oauth_registration WHERE provider_id = ?"; - Objects.requireNonNull(getJdbcTemplate()) - .query(sql, rs -> { - ClientRegistration registration = ClientRegistration - .withRegistrationId(rs.getString("id")) - .clientId(rs.getString("client_id")) - .clientSecret(rs.getString("client_secret")) + public void afterPropertiesSet() throws Exception { + registrationRepo.findAll() + .stream() + .filter(x -> Objects.nonNull(x.getClientId()) && Objects.nonNull(x.getClientSecret())) + .filter(x -> { + Provider provider = x.getProvider(); + return StringUtils.equalsIgnoreCase("wxapp", provider.getId()); + }) + .forEach(x -> { + ClientProperties properties = ClientProperties.builder() + .id(x.getId()) + .clientId(x.getClientId()) + .clientSecret(x.getClientSecret()) .build(); - providers.put(registration.getRegistrationId(), registration); - }, "wxapp"); + providers.put(properties.getId(), properties); + }); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index a773535..e2019d7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -3,7 +3,6 @@ package cn.seqdata.oauth2.wxapp; import java.util.concurrent.TimeUnit; import org.joda.time.DateTimeConstants; -import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.stereotype.Service; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -30,9 +29,9 @@ public class WxAppService { @Override public String load(String id) { AccessTokenRequest request = new AccessTokenRequest(); - ClientRegistration registration = provider.apply(id); - request.setAppid(registration.getClientId()); - request.setSecret(registration.getClientSecret()); + ClientProperties clientProps = provider.apply(id); + request.setAppid(clientProps.getClientId()); + request.setSecret(clientProps.getClientSecret()); AccessTokenResponse response = feignClient.getAccessToken(request); WxAppException.check(response); return response.getAccess_token(); @@ -48,9 +47,9 @@ public class WxAppService { public Code2SessionResponse signin(String id, String code) { Code2SessionRequest request = new Code2SessionRequest(); - ClientRegistration registration = provider.apply(id); - request.setAppid(registration.getClientId()); - request.setSecret(registration.getClientSecret()); + ClientProperties clientProps = provider.apply(id); + request.setAppid(clientProps.getClientId()); + request.setSecret(clientProps.getClientSecret()); request.setJs_code(code); Code2SessionResponse response = feignClient.code2session(request); WxAppException.check(response); -- Gitee From 507636d2eb3fe90acd6d3416bebc9555cec2bc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 31 Aug 2020 17:59:43 +0800 Subject: [PATCH 054/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/RouterController.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index e26b0bd..f66c44e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -17,6 +17,7 @@ import lombok.AllArgsConstructor; import cn.seqdata.antd.Router; import cn.seqdata.oauth2.jpa.perm.ModulePermission; +import cn.seqdata.oauth2.jpa.perm.ViewPermission; import cn.seqdata.oauth2.repos.perm.SysPermRepo; import cn.seqdata.oauth2.service.RouterService; import cn.seqdata.oauth2.util.RouterUtils; @@ -41,7 +42,10 @@ public class RouterController { //获取有权限的全部action,并且按view做好分组 Multimap actionPerms = LinkedListMultimap.create(); service.fetchActionPerms(principal, sort) - .forEach(x -> actionPerms.put(x.getViewPermId(), x.getIdentifier())); + .forEach(x -> { + ViewPermission viewPerm = x.getViewPerm(); + actionPerms.put(viewPerm.getId(), x.getIdentifier()); + }); if(Objects.isNull(sys)) { Collection routers = service.topRouters(principal, sort); -- Gitee From d0d75c014c045b255c53355a9c81f1ac537e8c5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 1 Sep 2020 15:54:43 +0800 Subject: [PATCH 055/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/OAuth2CodeController.java | 20 +---- .../oauth2/controller/WxAppController.java | 80 +++++++++---------- .../seqdata/oauth2/service/TokenService.java | 5 ++ .../seqdata/oauth2/service/UserService.java | 31 ++++++- .../seqdata/oauth2/wxapp/WxAppOAuth2User.java | 37 --------- .../cn/seqdata/oauth2/wxapp/WxAppService.java | 51 ++++++++++-- 6 files changed, 115 insertions(+), 109 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 405fa3c..52c3c7e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -1,8 +1,5 @@ package cn.seqdata.oauth2.controller; -import java.util.Objects; -import java.util.Optional; - import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -17,7 +14,6 @@ import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.provider.UserProfile; @@ -50,23 +46,11 @@ public class OAuth2CodeController { //从远程拉取用户信息,以 registrationId + remoteUser.name 作为唯一识别符 OAuth2AccessTokenResponse tokenResponse = oAuth2Template.exchangeForAccess(code, state); OAuth2User remoteUser = oAuth2Template.loadUser(tokenResponse); - UserProfile profile = provider.profile.apply(remoteUser); + UserProfile profile = provider.profile.apply(remoteUser); //这里的 state 是 accessToken OAuth2Authentication authentication = tokenStore.readAuthentication(state); - if(Objects.nonNull(authentication)) { - //获取当前用户身份 - String localClientId = SecurityUtils.clientId(authentication); - String localUsername = SecurityUtils.username(authentication); - Optional userOptional = userService.loadUser(localClientId, localUsername); - if(userOptional.isPresent()) { - User user = userOptional.get(); - userService.bindUser(user.getId(), registrationId, profile); - } - } else { - //当用户不存在时自动创建 - userService.createUser(registrationId, profile); - } + userService.updateUser(registrationId, profile, authentication); return tokenService.createToken(registrationId, remoteUser); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 737ff03..5d9281d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -1,27 +1,26 @@ package cn.seqdata.oauth2.controller; -import java.util.Optional; +import java.io.IOException; +import java.security.Principal; +import java.util.HashMap; +import java.util.Map; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.jpa.oauth.UserDetail; -import cn.seqdata.oauth2.jpa.oauth.UserDetailId; -import cn.seqdata.oauth2.jpa.rbac.UserAccount; -import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; -import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.TokenService; -import cn.seqdata.oauth2.wxapp.WxAppOAuth2User; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.wechat.WechatUserProfile; import cn.seqdata.oauth2.wxapp.WxAppService; import cn.seqdata.wxapp.message.Code2SessionResponse; +import cn.seqdata.wxapp.pojo.UserInfo; +import cn.seqdata.wxapp.util.WxAppUtils; /** * @author jrxian @@ -31,49 +30,42 @@ import cn.seqdata.wxapp.message.Code2SessionResponse; @RequestMapping("/wxapp/{id}") @AllArgsConstructor public class WxAppController { - private final WxAppService service; + private final ObjectMapper objectMapper; + private final WxAppService wxappService; + private final UserService userService; private final TokenService tokenService; - private final TokenStore tokenStore; - private final UserAccountRepo userAccountRepo; - private final UserDetailRepo userDetailRepo; - @GetMapping("/signin") - public OAuth2AccessToken signin(@PathVariable("id") String id, String code) { - Code2SessionResponse response = service.signin(id, code); - - UserDetailId entityId = new UserDetailId(id, response.getOpenid()); - UserDetail userDetail = userDetailRepo.findById(entityId) - .orElseGet(() -> { - //用户不存在,创建 - UserDetail supplier = new UserDetail(entityId); - //在系统中查询到unionid,绑定到该userid - Optional accountOptional = userAccountRepo.findFirstByUnionId(response.getUnionid()); - if(accountOptional.isPresent()) { - UserAccount userAccount = accountOptional.get(); - supplier.setUserId(userAccount.getId()); - } - return userDetailRepo.save(supplier); - }); + @GetMapping("/signup") + public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { + Code2SessionResponse response = wxappService.signin(id, code); + String decryptedData = WxAppUtils.decryptData(response.getSession_key(), iv, encryptedData); + UserInfo userInfo = objectMapper.readValue(decryptedData, UserInfo.class); - OAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(response.getSession_key()); - OAuth2User principal = new WxAppOAuth2User(response); - OAuth2Authentication authentication = tokenService.createOAuth2Authentication(id, principal); - tokenStore.storeAccessToken(accessToken, authentication); + Map attributes = new HashMap<>(); + WxAppUtils.toAttributes(attributes, response); + WxAppUtils.toAttributes(attributes, userInfo); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, "openid"); + WechatUserProfile profile = new WechatUserProfile(remoteUser); + userService.updateUser(id, profile, null); - return accessToken; + return tokenService.createToken(id, remoteUser); } - @GetMapping("/signout") - public void signout(@PathVariable("id") String id) { - } + @GetMapping("/signin") + public OAuth2AccessToken signin(@PathVariable("id") String id, String code, Principal principal) { + Code2SessionResponse response = wxappService.signin(id, code); + + Map attributes = new HashMap<>(); + WxAppUtils.toAttributes(attributes, response); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, "openid"); + WechatUserProfile profile = new WechatUserProfile(remoteUser); + userService.updateUser(id, profile, principal); - @GetMapping("/info") - public OAuth2AccessToken info(@PathVariable("id") String id, String iv, String data) { - return null; + return tokenService.createToken(id, remoteUser); } @GetMapping("/token") public String token(@PathVariable("id") String id) { - return service.accessToken(id); + return wxappService.accessToken(id); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java index 85c1f2e..497904b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.Objects; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -50,4 +51,8 @@ public class TokenService { authorities, true, Collections.emptySet(), Collections.emptySet(), null, null, null); } + + public static Collection defaultAuthorities() { + return Collections.singletonList(new SimpleGrantedAuthority("guest")); + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 3be7742..16ead21 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -1,5 +1,6 @@ package cn.seqdata.oauth2.service; +import java.security.Principal; import java.util.Objects; import java.util.Optional; @@ -14,6 +15,7 @@ import cn.seqdata.oauth2.provider.UserProfile; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.util.SecurityUtils; import cn.seqdata.oauth2.wechat.WechatUserProfile; /** @@ -28,6 +30,25 @@ public class UserService { private final UserAccountRepo accountRepo; private final UserDetailRepo userDetailRepo; + /** + * 如果用户存在就绑定,否则就创建 + */ + public void updateUser(String registrationId, UserProfile profile, Principal principal) { + if(Objects.nonNull(principal)) { + //获取当前用户身份 + String localClientId = SecurityUtils.clientId(principal); + String localUsername = SecurityUtils.username(principal); + Optional userOptional = loadUser(localClientId, localUsername); + if(userOptional.isPresent()) { + User user = userOptional.get(); + bindUser(user.getId(), registrationId, profile); + } + } else { + //当用户不存在时自动创建 + createUser(registrationId, profile); + } + } + /** * 将第三方认证获取的用户信息转换为本系统的 User */ @@ -42,10 +63,12 @@ public class UserService { if(profile instanceof WechatUserProfile) { //查找微信的unionid,如果找到,视为同一个用户 String unionId = ((WechatUserProfile) profile).getUnionId(); - Optional accountOptional = accountRepo.findFirstByUnionId(unionId); - if(accountOptional.isPresent()) { - UserAccount account = accountOptional.get(); - userDetail.setUserId(account.getId()); + if(Objects.nonNull(unionId)) { + Optional accountOptional = accountRepo.findFirstByUnionId(unionId); + if(accountOptional.isPresent()) { + UserAccount account = accountOptional.get(); + userDetail.setUserId(account.getId()); + } } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java deleted file mode 100644 index 4e25b22..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppOAuth2User.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.seqdata.oauth2.wxapp; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.core.user.OAuth2User; - -import cn.seqdata.wxapp.message.Code2SessionResponse; - -/** - * @author jrxian - * @date 2020-08-31 16:47 - */ -public class WxAppOAuth2User implements OAuth2User { - private final String name; - - public WxAppOAuth2User(Code2SessionResponse response) { - this.name = response.getOpenid(); - } - - @Override - public String getName() { - return name; - } - - @Override - public Map getAttributes() { - return Collections.emptyMap(); - } - - @Override - public Collection getAuthorities() { - return Collections.emptyList(); - } -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index e2019d7..43adcd2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -3,16 +3,20 @@ package cn.seqdata.oauth2.wxapp; import java.util.concurrent.TimeUnit; import org.joda.time.DateTimeConstants; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.stereotype.Service; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import lombok.AllArgsConstructor; +import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.wxapp.WxAppException; import cn.seqdata.wxapp.WxAppFeignClient; import cn.seqdata.wxapp.message.AccessTokenRequest; -import cn.seqdata.wxapp.message.AccessTokenResponse; import cn.seqdata.wxapp.message.Code2SessionRequest; import cn.seqdata.wxapp.message.Code2SessionResponse; @@ -23,6 +27,7 @@ import cn.seqdata.wxapp.message.Code2SessionResponse; @Service @AllArgsConstructor public class WxAppService { + //将微信access_token缓存到本地,每小时刷新一次 private final LoadingCache cache = CacheBuilder.newBuilder() .expireAfterWrite(DateTimeConstants.SECONDS_PER_HOUR, TimeUnit.SECONDS) .build(new CacheLoader() { @@ -32,14 +37,19 @@ public class WxAppService { ClientProperties clientProps = provider.apply(id); request.setAppid(clientProps.getClientId()); request.setSecret(clientProps.getClientSecret()); - AccessTokenResponse response = feignClient.getAccessToken(request); - WxAppException.check(response); - return response.getAccess_token(); + + return WxAppException.check(feignClient.getAccessToken(request)) + .getAccess_token(); } }); private final WxAppProvider provider; private final WxAppFeignClient feignClient; + private final TokenService tokenService; + private final TokenStore tokenStore; + private final TokenEnhancer tokenEnhancer; + private final UserAccountRepo userAccountRepo; + private final UserDetailRepo userDetailRepo; public String accessToken(String appid) { return cache.getUnchecked(appid); @@ -52,7 +62,36 @@ public class WxAppService { request.setSecret(clientProps.getClientSecret()); request.setJs_code(code); Code2SessionResponse response = feignClient.code2session(request); - WxAppException.check(response); - return response; + return WxAppException.check(response); } + +// public OAuth2AccessToken createAccessToken(String id, Code2SessionResponse response, OAuth2User remoteUser, Principal principal) { +// OAuth2Authentication authentication = tokenService.createOAuth2Authentication(id, remoteUser); +// +// DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(response.getSession_key()); +// accessToken.setExpiration(new Date(System.currentTimeMillis() + DateTimeConstants.MILLIS_PER_DAY)); +// +// UserDetailId entityId = new UserDetailId(id, response.getOpenid()); +// Optional userDetailOptional = userDetailRepo.findById(entityId); +// UserDetail userDetail = userDetailOptional.orElse(new UserDetail(entityId)); +// +// if(Objects.nonNull(principal)) { +// userDetail.setUserId(SecurityUtils.userId(principal)); +// } +// +// if(Objects.isNull(userDetail.getUserId()) && Objects.nonNull(response.getUnionid())) { +// Optional accountOptional = userAccountRepo.findFirstByUnionId(response.getUnionid()); +// if(accountOptional.isPresent()) { +// UserAccount userAccount = accountOptional.get(); +// userDetail.setUserId(userAccount.getId()); +// } +// } +// +// userDetailRepo.save(userDetail); +// +// OAuth2AccessToken enhanceToken = tokenEnhancer.enhance(accessToken, authentication); +// tokenStore.storeAccessToken(enhanceToken, authentication); +// +// return enhanceToken; +// } } -- Gitee From 15790391b4b7dc797ca1b6f8657735225baaaecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 1 Sep 2020 15:59:07 +0800 Subject: [PATCH 056/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/WxAppController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 5d9281d..49004de 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -36,7 +36,7 @@ public class WxAppController { private final TokenService tokenService; @GetMapping("/signup") - public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { + public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData, Principal principal) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); String decryptedData = WxAppUtils.decryptData(response.getSession_key(), iv, encryptedData); UserInfo userInfo = objectMapper.readValue(decryptedData, UserInfo.class); @@ -46,7 +46,7 @@ public class WxAppController { WxAppUtils.toAttributes(attributes, userInfo); DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, "openid"); WechatUserProfile profile = new WechatUserProfile(remoteUser); - userService.updateUser(id, profile, null); + userService.updateUser(id, profile, principal); return tokenService.createToken(id, remoteUser); } -- Gitee From 82f5b1c2533987d91223e535fdb7166ea806e3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 2 Sep 2020 15:13:49 +0800 Subject: [PATCH 057/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/WxAppController.java | 4 +- .../seqdata/oauth2/wxapp/WxAppProvider.java | 3 +- .../cn/seqdata/oauth2/wxapp/WxAppService.java | 40 ------------------- 3 files changed, 4 insertions(+), 43 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 49004de..e28821d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -44,7 +44,7 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); WxAppUtils.toAttributes(attributes, userInfo); - DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, "openid"); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); @@ -57,7 +57,7 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); - DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, "openid"); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java index 948b16b..1bed548 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java @@ -12,6 +12,7 @@ import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.Provider; import cn.seqdata.oauth2.repos.oauth.RegistrationRepo; +import cn.seqdata.wxapp.util.WxAppUtils; /** * @author jrxian @@ -36,7 +37,7 @@ public class WxAppProvider implements Function, Initia .filter(x -> Objects.nonNull(x.getClientId()) && Objects.nonNull(x.getClientSecret())) .filter(x -> { Provider provider = x.getProvider(); - return StringUtils.equalsIgnoreCase("wxapp", provider.getId()); + return StringUtils.equalsIgnoreCase(WxAppUtils.WXAPP, provider.getId()); }) .forEach(x -> { ClientProperties properties = ClientProperties.builder() diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index 43adcd2..43fca81 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -3,17 +3,12 @@ package cn.seqdata.oauth2.wxapp; import java.util.concurrent.TimeUnit; import org.joda.time.DateTimeConstants; -import org.springframework.security.oauth2.provider.token.TokenEnhancer; -import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.stereotype.Service; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; -import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; -import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.wxapp.WxAppException; import cn.seqdata.wxapp.WxAppFeignClient; import cn.seqdata.wxapp.message.AccessTokenRequest; @@ -45,11 +40,6 @@ public class WxAppService { private final WxAppProvider provider; private final WxAppFeignClient feignClient; - private final TokenService tokenService; - private final TokenStore tokenStore; - private final TokenEnhancer tokenEnhancer; - private final UserAccountRepo userAccountRepo; - private final UserDetailRepo userDetailRepo; public String accessToken(String appid) { return cache.getUnchecked(appid); @@ -64,34 +54,4 @@ public class WxAppService { Code2SessionResponse response = feignClient.code2session(request); return WxAppException.check(response); } - -// public OAuth2AccessToken createAccessToken(String id, Code2SessionResponse response, OAuth2User remoteUser, Principal principal) { -// OAuth2Authentication authentication = tokenService.createOAuth2Authentication(id, remoteUser); -// -// DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(response.getSession_key()); -// accessToken.setExpiration(new Date(System.currentTimeMillis() + DateTimeConstants.MILLIS_PER_DAY)); -// -// UserDetailId entityId = new UserDetailId(id, response.getOpenid()); -// Optional userDetailOptional = userDetailRepo.findById(entityId); -// UserDetail userDetail = userDetailOptional.orElse(new UserDetail(entityId)); -// -// if(Objects.nonNull(principal)) { -// userDetail.setUserId(SecurityUtils.userId(principal)); -// } -// -// if(Objects.isNull(userDetail.getUserId()) && Objects.nonNull(response.getUnionid())) { -// Optional accountOptional = userAccountRepo.findFirstByUnionId(response.getUnionid()); -// if(accountOptional.isPresent()) { -// UserAccount userAccount = accountOptional.get(); -// userDetail.setUserId(userAccount.getId()); -// } -// } -// -// userDetailRepo.save(userDetail); -// -// OAuth2AccessToken enhanceToken = tokenEnhancer.enhance(accessToken, authentication); -// tokenStore.storeAccessToken(enhanceToken, authentication); -// -// return enhanceToken; -// } } -- Gitee From 1593d32d2d8aa028e4201a5baa6937b8ecb31c2b Mon Sep 17 00:00:00 2001 From: penghaoyang <343219660@qq.com> Date: Wed, 2 Sep 2020 15:51:59 +0800 Subject: [PATCH 058/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=86=E5=88=AB?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=94=A8=E6=88=B7=E5=90=8D=E5=92=8C=E5=AF=86?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E4=BF=AE=E6=94=B9=E9=82=AE=E7=AE=B1=E7=9A=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index b80fe30..ef81413 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -7,12 +7,12 @@ import java.util.Optional; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ServerWebInputException; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -20,6 +20,7 @@ import cn.seqdata.oauth2.repos.perm.ActionPermRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -69,22 +70,39 @@ public class CurrentController { } /** - * 修改用户名和密码 + * 修改用户名或密码 */ @GetMapping(value = "/password") - public void password(Principal principal, @RequestParam String username, @RequestParam String password) { - UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); + public void password(Principal principal, @RequestParam(required = false) String username, @RequestParam(required = false) String password) { + if(StringUtils.isAllBlank(username, password)){ + return; + } - Optional optional = accountRepo.findByUsername(username); - if(optional.isPresent()) { - UserAccount account = optional.get(); - if(!Objects.equals(entity.getId(), account.getId())) { - throw new ServerWebInputException("用户名已经被使用!"); + UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); + if(StringUtils.isNotBlank(username)) { + Optional optional = accountRepo.findByUsername(username); + if(optional.isPresent()) { + UserAccount account = optional.get(); + if(!Objects.equals(entity.getId(), account.getId())) { + throw new ServerWebInputException("用户名已经被使用!"); + } } + entity.setUsername(username); + } + + if(StringUtils.isNotBlank(password)) { + entity.setPassword(password); } + accountRepo.save(entity); + } - entity.setUsername(username); - entity.setPassword(password); + /** + * 修改邮箱 + */ + @GetMapping(value = "/email") + public void email(Principal principal, @RequestParam String email, @RequestParam String nonce) { + UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); + entity.setEmail(email); accountRepo.save(entity); } -- Gitee From 3e9960f0a845acc761ab7b6c7361ddd4299b1a30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 2 Sep 2020 16:44:39 +0800 Subject: [PATCH 059/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerApplication.java | 4 +++ .../cn/seqdata/oauth2/WxAppConfiguration.java | 30 ------------------- .../seqdata/oauth2/wxapp/WxAppProvider.java | 1 + 3 files changed, 5 insertions(+), 30 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index 03fdde7..6e8a9e6 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -3,14 +3,18 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; import springfox.documentation.swagger2.annotations.EnableSwagger2; +import cn.seqdata.wxapp.WxAppFeignClient; + /** * Author: jrxian * Date: 2019-10-02 19:11 */ @SpringBootApplication @EnableDiscoveryClient +@EnableFeignClients(basePackageClasses = WxAppFeignClient.class) @EnableSwagger2 public class AuthzServerApplication { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java deleted file mode 100644 index 4176476..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WxAppConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.seqdata.oauth2; - -import java.util.Collections; -import java.util.List; - -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.MediaType; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; - -import cn.seqdata.wxapp.WxAppFeignClient; - -/** - * @author jrxian - * @date 2020-08-31 01:37 - */ -@Configuration -@EnableFeignClients(basePackageClasses = WxAppFeignClient.class) -public class WxAppConfiguration { - - @Bean - public HttpMessageConverter messageConverter() { - List supportedMediaTypes = Collections.singletonList(MediaType.TEXT_PLAIN); - MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); - jackson2HttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes); - return jackson2HttpMessageConverter; - } -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java index 1bed548..39344ee 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java @@ -35,6 +35,7 @@ public class WxAppProvider implements Function, Initia registrationRepo.findAll() .stream() .filter(x -> Objects.nonNull(x.getClientId()) && Objects.nonNull(x.getClientSecret())) + .filter(x -> Objects.nonNull(x.getProvider())) .filter(x -> { Provider provider = x.getProvider(); return StringUtils.equalsIgnoreCase(WxAppUtils.WXAPP, provider.getId()); -- Gitee From 22025540ac8e20db271b3bafbcb8cc196244e74a Mon Sep 17 00:00:00 2001 From: penghaoyang <343219660@qq.com> Date: Thu, 3 Sep 2020 09:42:14 +0800 Subject: [PATCH 060/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0patch=E7=9A=84?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=AF=86=E7=A0=81=E3=80=81=E9=82=AE=E7=AE=B1?= =?UTF-8?q?=E3=80=81=E7=94=B5=E8=AF=9D=E5=8F=B7=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 38 +++++++++++++++++-- .../oauth2/params/UserAccountParam.java | 18 +++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/params/UserAccountParam.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index ef81413..68fd6d7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -16,6 +16,7 @@ import org.springframework.web.server.ServerWebInputException; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.params.UserAccountParam; import cn.seqdata.oauth2.repos.perm.ActionPermRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; @@ -71,6 +72,7 @@ public class CurrentController { /** * 修改用户名或密码 + * @deprecated 使用 patch方法 的/password */ @GetMapping(value = "/password") public void password(Principal principal, @RequestParam(required = false) String username, @RequestParam(required = false) String password) { @@ -96,18 +98,35 @@ public class CurrentController { accountRepo.save(entity); } + /** + * 修改密码 + */ + @PatchMapping(value = "/password") + public void updatePassword(Principal principal, @RequestBody UserAccountParam param) { + if(StringUtils.isBlank(param.getPassword())){ + return; + } + UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); + entity.setPassword(param.getPassword()); + accountRepo.save(entity); + } + /** * 修改邮箱 */ - @GetMapping(value = "/email") - public void email(Principal principal, @RequestParam String email, @RequestParam String nonce) { + @PatchMapping(value = "/email") + public void updateEmail(Principal principal, @RequestBody UserAccountParam param) { + if(StringUtils.isBlank(param.getEmail())){ + return; + } UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); - entity.setEmail(email); + entity.setEmail(param.getEmail()); accountRepo.save(entity); } /** * 更换手机号 + * @deprecated 使用 patch方法 的/mobile */ @GetMapping(value = "/mobile") public void mobile(Principal principal, @RequestParam String mobile, @RequestParam String nonce) { @@ -115,4 +134,17 @@ public class CurrentController { entity.setMobile(mobile); accountRepo.save(entity); } + + /** + * 修改手机号 + */ + @PatchMapping(value = "/mobile") + public void updateMobile(Principal principal, @RequestBody UserAccountParam param) { + if(StringUtils.isBlank(param.getMobile())){ + return; + } + UserAccount entity = accountRepo.getOne(SecurityUtils.userId(principal)); + entity.setMobile(param.getMobile()); + accountRepo.save(entity); + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/params/UserAccountParam.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/params/UserAccountParam.java new file mode 100644 index 0000000..272e88e --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/params/UserAccountParam.java @@ -0,0 +1,18 @@ +package cn.seqdata.oauth2.params; + +import lombok.Data; + +/** + * @author penghaoyang + * @date 2020/9/3 + */ +@Data +public class UserAccountParam { + + private String password; + + private String email; + + private String mobile; + +} -- Gitee From d6188be934bf2f48bb4a3593687fc457d538eba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 4 Sep 2020 16:03:05 +0800 Subject: [PATCH 061/228] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E6=9D=83=E9=99=90?= =?UTF-8?q?=E7=AE=A1=E7=90=86=EF=BC=8C=E6=89=A9=E5=A4=A7cache=E5=B0=BA?= =?UTF-8?q?=E5=AF=B8=E5=92=8C=E7=BC=93=E5=AD=98=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- .../seqdata/oauth2/wxapp/WxAppProvider.java | 2 +- seqdata-cloud-gateway/pom.xml | 7 ------ .../gateway/AuthClientConfiguration.java | 2 ++ .../seqdata/gateway/GatewayConfiguration.java | 6 ++--- .../CachedResourceServerTokenServices.java | 8 +++---- .../filter/ResourceServerProperties.java | 2 -- ...Predicate.java => AllowlistPredicate.java} | 23 ++++++++++--------- .../filter/authc/PathMatcherGlobalFilter.java | 12 +++++----- .../src/main/resources/application.yml | 2 +- 10 files changed, 31 insertions(+), 37 deletions(-) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/{WhitelistPredicate.java => AllowlistPredicate.java} (58%) diff --git a/pom.xml b/pom.xml index c8aa125..2629652 100644 --- a/pom.xml +++ b/pom.xml @@ -43,12 +43,12 @@ com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery + spring-cloud-starter-alibaba-nacos-config ${nacos.version} com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-config + spring-cloud-starter-alibaba-nacos-discovery ${nacos.version} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java index 39344ee..43035a4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java @@ -31,7 +31,7 @@ public class WxAppProvider implements Function, Initia } @Override - public void afterPropertiesSet() throws Exception { + public void afterPropertiesSet() { registrationRepo.findAll() .stream() .filter(x -> Objects.nonNull(x.getClientId()) && Objects.nonNull(x.getClientSecret())) diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index fe26424..a51ed44 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -31,13 +31,6 @@ ${project.version} - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-config - ${nacos.version} - - - compile - true - - - org.apache.commons - commons-lang3 - 3.9 - - - com.google.guava - guava - 21.0 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.1.RELEASE + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + pom + + 1.8 + Hoxton.SR1 + 2.9.2 + 2.2.1.RELEASE + + + seqdata-cloud-gateway + seqdata-cloud-authz + seqdata-cloud-authc + + + + org.projectlombok + lombok + + compile + true + + + org.apache.commons + commons-lang3 + 3.9 + + + com.google.guava + guava + 21.0 + - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-config - ${nacos.version} - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - ${nacos.version} - + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + ${nacos.version} + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + ${nacos.version} + - - io.springfox - springfox-swagger2 - ${swagger.version} - - - io.springfox - springfox-swagger-ui - ${swagger.version} - - - - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud.version} - pom - import - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - com.spotify - docker-maven-plugin - 1.2.2 - - - install - - build - - - - - ${project.artifactId} - ${project.basedir}/src/main/docker - http://dev.voltmao.com:14775 - - - / - ${project.build.directory} - ${project.build.finalName}.jar - - - - - - + + io.springfox + springfox-swagger2 + ${swagger.version} + + + io.springfox + springfox-swagger-ui + ${swagger.version} + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + com.spotify + docker-maven-plugin + 1.2.2 + + + install + + build + + + + + ${project.artifactId} + ${project.basedir}/src/main/docker + http://dev.voltmao.com:14775 + + + / + ${project.build.directory} + ${project.build.finalName}.jar + + + + + + -- Gitee From 37ea02f70c7a76e9abbbb955c3a95e418e5d2fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 14 Sep 2020 09:21:45 +0800 Subject: [PATCH 074/228] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E7=9B=B8=E5=85=B3api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index 098a7fb..1fc84f5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -33,7 +33,7 @@ public class WxAppService { request.setAppid(clientProps.getClientId()); request.setSecret(clientProps.getClientSecret()); - AccessTokenResponse response = feignClient.getAccessToken(request); + AccessTokenResponse response = feignClient.accessToken(request); WxAppException.check(response); return response.getAccess_token(); } @@ -52,7 +52,7 @@ public class WxAppService { request.setAppid(clientProps.getClientId()); request.setSecret(clientProps.getClientSecret()); request.setJs_code(code); - + Code2SessionResponse response = feignClient.code2session(request); return WxAppException.check(response); } -- Gitee From 41a8a2aeff341d0b592e9e791a9c551be9be48b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 16 Sep 2020 11:30:41 +0800 Subject: [PATCH 075/228] +seqdata-cloud-message --- .../java/cn/seqdata/oauth2/AuthcServerConfiguration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 33f1d9e..110aea6 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -21,9 +21,9 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.authorizeRequests() .antMatchers( - "/v2/api-docs/**", "/swagger-resources/**", - "/rbac/**", "/perm/**", - "/connect/**", "/oauth/code/**", "/wxapp/**") + "/v2/api-docs/**", "/swagger-resources/**" + , "/oauth2/**", "/rbac/**", "/perm/**" + , "/connect/**", "/oauth/code/**", "/wxapp/**") .permitAll(); super.configure(http); -- Gitee From 6e27ac7407921df80e346bd4c485441b55acd46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 16 Sep 2020 12:26:31 +0800 Subject: [PATCH 076/228] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=EF=BC=8Cjpa?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=E6=94=AF=E6=8C=81=E5=B5=8C=E5=A5=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 23 +++++++++---------- .../seqdata/gateway/JacksonConfiguration.java | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 5f41fb7..e284d54 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -17,6 +17,7 @@ import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; +import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -25,7 +26,6 @@ import cn.seqdata.oauth2.repos.perm.ActionPermRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; /** * Author: jrxian @@ -81,7 +81,7 @@ public class CurrentController { */ @PatchMapping(value = "/name") public void updateName(Principal principal, @RequestBody UserAccountParam param) throws BindException { - UserAccount entity = getUserAccountFrom(principal); + UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); checkField(ex, param, "name", UserAccountParam::getName); @@ -96,7 +96,7 @@ public class CurrentController { */ @PatchMapping(value = "/email") public void updateEmail(Principal principal, @RequestBody UserAccountParam param) throws BindException { - UserAccount entity = getUserAccountFrom(principal); + UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); checkField(ex, param, "email", UserAccountParam::getEmail); @@ -112,7 +112,7 @@ public class CurrentController { */ @PatchMapping(value = "/mobile") public void updateMobile(Principal principal, @RequestBody UserAccountParam param) throws BindException { - UserAccount entity = getUserAccountFrom(principal); + UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); checkField(ex, param, "mobile", UserAccountParam::getMobile); @@ -123,17 +123,12 @@ public class CurrentController { accountRepo.save(entity); } - private UserAccount getUserAccountFrom(Principal principal) { - return accountRepo.findByUsername(SecurityUtils.username(principal)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); - } - /** * 修改密码 */ @PatchMapping(value = "/password") - public Boolean updatePassword(Principal principal, @RequestBody UserAccountParam param) throws BindException { - UserAccount entity = getUserAccountFrom(principal); + public void updatePassword(Principal principal, @RequestBody UserAccountParam param) throws BindException { + UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); checkField(ex, param, "password", UserAccountParam::getPassword); @@ -141,7 +136,11 @@ public class CurrentController { entity.setPassword(param.getPassword()); accountRepo.save(entity); - return true; + } + + private UserAccount getUserAccount(Principal principal) { + return accountRepo.findByUsername(SecurityUtils.username(principal)) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); } private static void checkField(BindException ex, UserAccountParam param, String fieldName, diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java index f8eb3b4..0abaf3b 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java @@ -43,7 +43,7 @@ public class JacksonConfiguration { static final class LongDeserializer extends StdDeserializer { private static final long serialVersionUID = 1L; - public static JacksonConfiguration.LongDeserializer instance = new JacksonConfiguration.LongDeserializer(); + public static final JacksonConfiguration.LongDeserializer instance = new JacksonConfiguration.LongDeserializer(); private LongDeserializer() { super(Long.class); -- Gitee From d806891934acb25ad03dd1bac5782114b806ec2f Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 16 Sep 2020 17:00:11 +0800 Subject: [PATCH 077/228] =?UTF-8?q?=E5=BC=82=E5=B8=B8=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E5=A4=84=E7=90=86+=E8=A7=A3=E7=BB=91=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 798c0a7..77a703e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import javax.servlet.http.HttpServletRequest; +import javax.transaction.Transactional; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTimeConstants; @@ -15,7 +16,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.web.bind.annotation.*; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; @@ -27,6 +27,7 @@ import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -106,9 +107,15 @@ public class ConnectController { .build(); } + @Transactional(rollbackOn = Exception.class) @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) { - UserDetailId entityId = new UserDetailId(registrationId, principal.getName()); - userDetailRepo.deleteById(entityId); + String username = principal.getName(); + if(StringUtils.isBlank(username)) { + throw new RuntimeException("解绑失败,用户名不存在"); + } + UserAccount user = accountRepo.findByUsername(username) + .orElseThrow(() -> new RuntimeException("解绑失败,用户名不存在")); + userDetailRepo.deleteByIdClientAndUserId(registrationId, Objects.requireNonNull(user.getId())); } } -- Gitee From d9c169d7d50b378782596bafd76e35b673d51209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 22 Sep 2020 11:00:26 +0800 Subject: [PATCH 078/228] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 8 +- .../oauth2/WebSecurityConfiguration.java | 6 +- .../oauth2/controller/CurrentController.java | 6 +- .../oauth2/service/MobileNonceService.java | 28 +++--- .../seqdata/oauth2/service/UserService.java | 86 +++++++++---------- 5 files changed, 74 insertions(+), 60 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 885bf2f..84fa89d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -34,10 +34,12 @@ import org.springframework.security.oauth2.provider.token.store.redis.RedisToken import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.mobile.MobileTokenGranter; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; +import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; import cn.seqdata.oauth2.service.UserService; @@ -53,6 +55,7 @@ import cn.seqdata.oauth2.util.SecurityUtils; public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { private final AuthenticationManager authenticationManager; private final UserService userService; + private final UserRepo userRepo; private final JpaUserDetailsManager userDetailsService; private final RedisConnectionFactory redisConnectionFactory; private final ClientDetailRepo clientDetailRepo; @@ -83,10 +86,11 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt String clientId = SecurityUtils.clientId(authentication); String username = SecurityUtils.username(authentication); - Optional userOptional = userService.loadUser(clientId, username); + Optional userOptional = userService.loadUser(clientId, username); if(userOptional.isPresent()) { - User user = userOptional.get(); + UserAccount account = userOptional.get(); + User user = userRepo.getOne(Objects.requireNonNull(account.getId())); attributes.put("user_id", user.getId()); attributes.put("org_id", user.getOrgId()); attributes.put("dept_id", user.getDeptId()); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java index 452f4d7..37627a2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -21,9 +21,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { super.configure(web); web.ignoring() - .antMatchers("/favicon.ico", - "/resources/**", "/css/**", "/js/**", - "/swagger-ui.html", "/webjars/**", "/plugins/**" + .antMatchers("/favicon.ico" + , "/resources/**", "/css/**", "/js/**" + , "/swagger-ui.html", "/webjars/**", "/plugins/**" ); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index e284d54..7292e10 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -24,6 +24,7 @@ import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.params.UserAccountParam; import cn.seqdata.oauth2.repos.perm.ActionPermRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; @@ -39,6 +40,7 @@ public class CurrentController { private static final String objectName = "UserAccount"; private final UserAccountRepo accountRepo; + private final UserRepo userRepo; private final ActionPermRepo actionPermRepo; private final UserService userService; private final TokenStore tokenStore; @@ -55,7 +57,9 @@ public class CurrentController { public Optional info(Principal principal) { String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); - return userService.loadUser(clientId, username); + return userService.loadUser(clientId, username) + .map(UserAccount::getId) + .map(userRepo::getOne); } @ApiOperation("令牌信息") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java index b3996d0..876d85f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java @@ -42,19 +42,25 @@ public class MobileNonceService implements MobileNonceHandler { public void check(String mobile, int nonce) throws AuthenticationException { UserAccount account = accountRepo.findByMobile(mobile) .orElseThrow(() -> new UsernameNotFoundException("无法用此手机号登录")); + + try { + DateTime expire = account.getNonceExpire(); + Integer code = account.getNonceCode(); + if(Objects.isNull(expire) || Objects.isNull(code)) { + throw new BadCredentialsException("未获取验证码"); + } - DateTime expire = account.getNonceExpire(); - Integer code = account.getNonceCode(); - if(Objects.isNull(expire) || Objects.isNull(code)) { - throw new BadCredentialsException("未获取验证码"); - } - - if(expire.isBeforeNow()) { - throw new NonceExpiredException("验证码已过期"); - } + if(expire.isBeforeNow()) { + throw new NonceExpiredException("验证码已过期"); + } - if(nonce != code) { - throw new BadCredentialsException("无效的验证码"); + if(nonce != code) { + throw new BadCredentialsException("无效的验证码"); + } + } finally { + account.setNonceCode(null); + account.setNonceExpire(null); + accountRepo.save(account); } } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 16ead21..867b7cc 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -26,34 +26,35 @@ import cn.seqdata.oauth2.wechat.WechatUserProfile; @AllArgsConstructor public class UserService { private final AuthorityService authorityService; - private final UserRepo userRepo; private final UserAccountRepo accountRepo; + private final UserRepo userRepo; private final UserDetailRepo userDetailRepo; - /** - * 如果用户存在就绑定,否则就创建 - */ - public void updateUser(String registrationId, UserProfile profile, Principal principal) { - if(Objects.nonNull(principal)) { - //获取当前用户身份 - String localClientId = SecurityUtils.clientId(principal); - String localUsername = SecurityUtils.username(principal); - Optional userOptional = loadUser(localClientId, localUsername); - if(userOptional.isPresent()) { - User user = userOptional.get(); - bindUser(user.getId(), registrationId, profile); - } - } else { - //当用户不存在时自动创建 - createUser(registrationId, profile); + public Optional loadUser(String clientId, String username) { + UserDetailId entityId = new UserDetailId(clientId, username); + //先从oauth_user_details中查找用户 + Optional optional = userDetailRepo.findById(entityId) + .map(UserDetail::getUser); + + //未找到,再从oauth_user中查找用户 + if(!optional.isPresent()) { + optional = accountRepo.findByUsernameOrMobileOrEmail(username, username, username); } + + if(optional.isPresent()) { + UserAccount user = optional.get(); + long userId = Objects.requireNonNull(user.getId()); + user.setAuthorities(authorityService.loadAuthorities(userId)); + } + + return optional; } /** * 将第三方认证获取的用户信息转换为本系统的 User */ //@Transactional //不要加事务,不然User和UserAccount不能保存 - public void createUser(String clientId, UserProfile profile) { + public void saveAsUser(String clientId, UserProfile profile) { UserDetailId entityId = new UserDetailId(clientId, profile.getName()); Optional optional = userDetailRepo.findById(entityId); UserDetail userDetail = new UserDetail(entityId); @@ -64,7 +65,7 @@ public class UserService { //查找微信的unionid,如果找到,视为同一个用户 String unionId = ((WechatUserProfile) profile).getUnionId(); if(Objects.nonNull(unionId)) { - Optional accountOptional = accountRepo.findFirstByUnionId(unionId); + Optional accountOptional = accountRepo.findByUnionId(unionId); if(accountOptional.isPresent()) { UserAccount account = accountOptional.get(); userDetail.setUserId(account.getId()); @@ -74,8 +75,8 @@ public class UserService { //没有找到微信unionid if(Objects.isNull(userDetail.getUserId())) { - Long userId = createUser(profile); - createUserAccount(userId, profile); + Long userId = saveAsUser(profile); + saveAsUserAccount(userId, profile); userDetail.setUserId(userId); } @@ -83,6 +84,25 @@ public class UserService { } } + /** + * 如果用户存在就绑定,否则就创建 + */ + public void updateUser(String registrationId, UserProfile profile, Principal principal) { + if(Objects.nonNull(principal)) { + //获取当前用户身份 + String localClientId = SecurityUtils.clientId(principal); + String localUsername = SecurityUtils.username(principal); + Optional userOptional = loadUser(localClientId, localUsername); + if(userOptional.isPresent()) { + UserAccount user = userOptional.get(); + bindUser(user.getId(), registrationId, profile); + } + } else { + //当用户不存在时自动创建 + saveAsUser(registrationId, profile); + } + } + /** * 将第三方认证的用户和本系统的用户绑定 */ @@ -101,27 +121,7 @@ public class UserService { userDetailRepo.save(entity); } - public Optional loadUser(String clientId, String username) { - UserDetailId entityId = new UserDetailId(clientId, username); - //先从oauth_user_details中查找用户 - Optional optional = userDetailRepo.findById(entityId) - .map(UserDetail::getUser); - - //未找到,再从oauth_user中查找用户 - if(!optional.isPresent()) { - optional = userRepo.findByUsername(username); - } - - if(optional.isPresent()) { - User user = optional.get(); - long userId = Objects.requireNonNull(user.getId()); - user.setAuthorities(authorityService.loadAuthorities(userId)); - } - - return optional; - } - - private Long createUser(UserProfile profile) { + private Long saveAsUser(UserProfile profile) { User user = new User(); user.setName(profile.getNickname()); user.setAvatar(profile.getAvatar()); @@ -129,7 +129,7 @@ public class UserService { return entity.getId(); } - private void createUserAccount(Long entityId, UserProfile profile) { + private void saveAsUserAccount(Long entityId, UserProfile profile) { UserAccount account = new UserAccount(entityId); account.setUsername(profile.getUsername()); account.setEmail(profile.getEmail()); -- Gitee From 3824b5af901bc20908def652ca4615d0c5110efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 22 Sep 2020 11:10:28 +0800 Subject: [PATCH 079/228] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 77a703e..7cb8a23 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -9,25 +9,25 @@ import java.util.Optional; import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; -import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ServerWebInputException; +import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; -import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; /** * Author: jrxian @@ -39,6 +39,7 @@ import lombok.AllArgsConstructor; public class ConnectController { private final ClientRegistrationRepository repository; private final UserAccountRepo accountRepo; + private final UserService userService; private final UserDetailRepo userDetailRepo; private final MobileNonceHandler mobileNonceHandler; @@ -46,15 +47,15 @@ public class ConnectController { public Map list(Principal principal) { Map connectors = new HashMap<>(); - Optional optional; String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); - if(StringUtils.equals("client", clientId)) { + + Optional optional = userDetailRepo + .findById(new UserDetailId(clientId, username)) + .map(UserDetail::getUser); + + if(!optional.isPresent()) { optional = accountRepo.findByUsernameOrMobileOrEmail(username, username, username); - } else { - UserDetail userDetail = userDetailRepo.getOne(new UserDetailId(clientId, username)); - User user = userDetail.getUser(); - optional = Optional.of(accountRepo.getOne(Objects.requireNonNull(user.getId()))); } if(optional.isPresent()) { @@ -110,12 +111,8 @@ public class ConnectController { @Transactional(rollbackOn = Exception.class) @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) { - String username = principal.getName(); - if(StringUtils.isBlank(username)) { - throw new RuntimeException("解绑失败,用户名不存在"); - } - UserAccount user = accountRepo.findByUsername(username) - .orElseThrow(() -> new RuntimeException("解绑失败,用户名不存在")); - userDetailRepo.deleteByIdClientAndUserId(registrationId, Objects.requireNonNull(user.getId())); + long userId = Optional.ofNullable(SecurityUtils.userId(principal)) + .orElseThrow(() -> new ServerWebInputException("用户不存在,解绑失败。")); + userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); } } -- Gitee From 3ecaa7a716e2ded3f534a1b33cc37ef185c47948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 22 Sep 2020 15:31:25 +0800 Subject: [PATCH 080/228] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/ConnectController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 7cb8a23..8d03c3f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -7,7 +7,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import javax.servlet.http.HttpServletRequest; -import javax.transaction.Transactional; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; @@ -108,7 +107,6 @@ public class ConnectController { .build(); } - @Transactional(rollbackOn = Exception.class) @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) { long userId = Optional.ofNullable(SecurityUtils.userId(principal)) -- Gitee From 926b8ce7212aaa7bee596379535ae502cdb1429a Mon Sep 17 00:00:00 2001 From: zl1995 Date: Tue, 22 Sep 2020 17:47:42 +0800 Subject: [PATCH 081/228] user:authorities --- .../cn/seqdata/oauth2/controller/CurrentController.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 7292e10..eaa67c3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -17,7 +17,6 @@ import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -27,6 +26,7 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -58,8 +58,11 @@ public class CurrentController { String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); return userService.loadUser(clientId, username) - .map(UserAccount::getId) - .map(userRepo::getOne); + .map(x -> { + User user = userRepo.getOne(Objects.requireNonNull(x.getId())); + user.setAuthorities(x.getAuthorities()); + return user; + }); } @ApiOperation("令牌信息") -- Gitee From 6408240ed37e6e97e9b8af38d8973872be730eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 22 Sep 2020 22:18:46 +0800 Subject: [PATCH 082/228] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authc/pom.xml | 88 +++++------ .../oauth2/ResourceServerConfiguration.java | 3 +- seqdata-cloud-authz/pom.xml | 140 +++++++++--------- .../oauth2/AuthzServerConfiguration.java | 6 +- .../oauth2/controller/CurrentController.java | 8 +- .../oauth2/controller/WxAppController.java | 9 +- .../oauth2/service/AliyunSmsService.java | 2 +- .../oauth2/service/MobileNonceService.java | 2 +- .../oauth2/service/PermissionPredicate.java | 3 +- .../seqdata/oauth2/service/RouterService.java | 24 +-- .../seqdata/oauth2/service/TokenService.java | 17 +-- .../cn/seqdata/oauth2/util/SecurityUtils.java | 10 +- .../cn/seqdata/oauth2/wxapp/WxAppService.java | 5 +- seqdata-cloud-gateway/pom.xml | 78 +++++----- .../gateway/AuthClientConfiguration.java | 3 +- .../seqdata/gateway/JacksonConfiguration.java | 2 +- 16 files changed, 201 insertions(+), 199 deletions(-) diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml index 2786289..cb628a9 100644 --- a/seqdata-cloud-authc/pom.xml +++ b/seqdata-cloud-authc/pom.xml @@ -1,48 +1,48 @@ - 4.0.0 - - cn.seqdata.cloud - seqdata-cloud-parent - 2.2.1-SNAPSHOT - - seqdata-cloud-authc - 资源服务器,所有的业务模块应该继承自资源服务器,自动和授权服务器交互实现认证和授权 - - - joda-time - joda-time - - - org.springframework.data - spring-data-commons - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-security - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-authc + 资源服务器,所有的业务模块应该继承自资源服务器,自动和授权服务器交互实现认证和授权 + + + joda-time + joda-time + + + org.springframework.data + spring-data-commons + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + - - org.springframework.security - spring-security-oauth2-resource-server - - - org.springframework.security - spring-security-oauth2-client - - - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - - - cn.seqdata.cloud - seqdata-cloud-auth-client - ${project.version} - - + + org.springframework.security + spring-security-oauth2-resource-server + + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + + + cn.seqdata.cloud + seqdata-cloud-auth-client + ${project.version} + + diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java index 878ba99..3844830 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java @@ -14,6 +14,7 @@ import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConv import org.springframework.security.oauth2.provider.token.RemoteTokenServices; import org.springframework.security.web.authentication.HttpStatusEntryPoint; +import cn.seqdata.oauth2.client.OAuth2Constants; import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; /** @@ -28,7 +29,7 @@ public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter @Bean public RemoteTokenServices remoteTokenServices(OAuth2ClientProperties credentials, ResourceServerProperties resource) { EnhanceUserAuthenticationConverter authenticationConverter = new EnhanceUserAuthenticationConverter(); - authenticationConverter.setDefaultAuthorities(new String[]{"guest"}); + authenticationConverter.setDefaultAuthorities(new String[]{OAuth2Constants.GUEST}); DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); tokenConverter.setUserTokenConverter(authenticationConverter); diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 34df85b..43f3fc7 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -1,77 +1,77 @@ - 4.0.0 - - cn.seqdata.cloud - seqdata-cloud-parent - 2.2.1-SNAPSHOT - - seqdata-cloud-authz - 认证服务器,提供oauth2认证,接入第三方认证(如微信、钉钉) - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jdbc - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-data-redis - - - org.springframework.boot - spring-boot-configuration-processor - true - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-authz + 认证服务器,提供oauth2认证,接入第三方认证(如微信、钉钉) + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + - - org.springframework.security - spring-security-oauth2-client - - - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + - - com.aliyun - aliyun-java-sdk-core - 4.5.2 - - - com.aliyun - aliyun-java-sdk-dysmsapi - 2.1.0 - + + com.aliyun + aliyun-java-sdk-core + 4.5.2 + + + com.aliyun + aliyun-java-sdk-dysmsapi + 2.1.0 + - - cn.seqdata.cloud - seqdata-cloud-wechat - ${project.version} - - - cn.seqdata.cloud - seqdata-cloud-oauth2 - ${project.version} - + + cn.seqdata.cloud + seqdata-cloud-wechat + ${project.version} + + + cn.seqdata.cloud + seqdata-cloud-oauth2 + ${project.version} + - - com.microsoft.sqlserver - mssql-jdbc - runtime - - - mysql - mysql-connector-java - runtime - - + + com.microsoft.sqlserver + mssql-jdbc + runtime + + + mysql + mysql-connector-java + runtime + + diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 84fa89d..aad7248 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -91,9 +91,9 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt if(userOptional.isPresent()) { UserAccount account = userOptional.get(); User user = userRepo.getOne(Objects.requireNonNull(account.getId())); - attributes.put("user_id", user.getId()); - attributes.put("org_id", user.getOrgId()); - attributes.put("dept_id", user.getDeptId()); + attributes.put(OAuth2Constants.USER_ID, user.getId()); + attributes.put(OAuth2Constants.DEPT_ID, user.getDeptId()); + attributes.put(OAuth2Constants.ORG_ID, user.getOrgId()); } ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index eaa67c3..6e210a8 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -17,6 +17,7 @@ import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; +import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -26,7 +27,6 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; /** * Author: jrxian @@ -150,14 +150,14 @@ public class CurrentController { .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); } - private static void checkField(BindException ex, UserAccountParam param, String fieldName, + private void checkField(BindException ex, UserAccountParam param, String fieldName, Function function) { if(StringUtils.isBlank(function.apply(param))) { ex.addError(new FieldError(objectName, fieldName, fieldName + "不能为空")); } } - private static void checkExist(BindException ex, UserAccountParam param, String fieldName, UserAccount entity, + private void checkExist(BindException ex, UserAccountParam param, String fieldName, UserAccount entity, Function getter, Function> querier) { String value = getter.apply(param); Optional optional = querier.apply(value); @@ -169,7 +169,7 @@ public class CurrentController { } } - private static void checkErrors(BindException ex) throws BindException { + private void checkErrors(BindException ex) throws BindException { if(ex.hasErrors()) { throw ex; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index e28821d..c4f032b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -2,9 +2,11 @@ package cn.seqdata.oauth2.controller; import java.io.IOException; import java.security.Principal; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.web.bind.annotation.GetMapping; @@ -14,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; +import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.wechat.WechatUserProfile; @@ -44,7 +47,8 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); WxAppUtils.toAttributes(attributes, userInfo); - DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, WxAppUtils.WXAPP_ATTR_KEY); + SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(OAuth2Constants.GUEST); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(Collections.singletonList(defaultAuthority), attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); @@ -57,7 +61,8 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); - DefaultOAuth2User remoteUser = new DefaultOAuth2User(TokenService.defaultAuthorities(), attributes, WxAppUtils.WXAPP_ATTR_KEY); + SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(OAuth2Constants.GUEST); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(Collections.singleton(defaultAuthority), attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java index 7692093..97866ca 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java @@ -64,7 +64,7 @@ public class AliyunSmsService implements BiConsumer { } @SneakyThrows - private static String toJSONString(Object value) { + private String toJSONString(Object value) { return new ObjectMapper().writeValueAsString(value); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java index 876d85f..f135753 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java @@ -42,7 +42,7 @@ public class MobileNonceService implements MobileNonceHandler { public void check(String mobile, int nonce) throws AuthenticationException { UserAccount account = accountRepo.findByMobile(mobile) .orElseThrow(() -> new UsernameNotFoundException("无法用此手机号登录")); - + try { DateTime expire = account.getNonceExpire(); Integer code = account.getNonceCode(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java index 1bb147b..af0efa5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java @@ -7,6 +7,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import cn.seqdata.jpa.JpaIdGenerated; +import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.domain.PermissionType; import cn.seqdata.oauth2.jpa.perm.JpaPermission; import cn.seqdata.oauth2.jpa.rbac.AuthorityPermission; @@ -23,7 +24,7 @@ public class PermissionPredicate implements Predicate { public PermissionPredicate(AuthorityPermissionRepo permissionRepo, PermissionType permType, Principal principal) { Set authorities = SecurityUtils.authorities(principal); - sysAdmin = authorities.contains(SecurityUtils.ROLE_SYS_ADMIN); + sysAdmin = authorities.contains(OAuth2Constants.SYSADMIN); if(sysAdmin) { permissions = Collections.emptySet(); } else { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java index 6463f2e..465309e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java @@ -37,6 +37,18 @@ public class RouterService { private final ActionPermRepo actionPermRepo; private final AuthorityPermissionRepo permissionRepo; + private static Long getId(Named entity) { + return entity.getId(); + } + + private static Long toLong(Object key) { + if(key instanceof Number) { + return ((Number) key).longValue(); + } else { + return 0L; + } + } + /** * 所有菜单 */ @@ -111,16 +123,4 @@ public class RouterService { private boolean chdRouterFilter(Router router, ModulePermission modulePerm) { return Objects.equals(toLong(router.key), modulePerm.getParentId()); } - - private static Long getId(Named entity) { - return entity.getId(); - } - - private static Long toLong(Object key) { - if(key instanceof Number) { - return ((Number) key).longValue(); - } else { - return 0L; - } - } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java index 497904b..4e0e1aa 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java @@ -5,7 +5,6 @@ import java.util.Collections; import java.util.Objects; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -26,6 +25,12 @@ public class TokenService { private final UserService userService; private final AuthorityService authorityService; + public static OAuth2Request createOAuth2Request(String clientId, Collection authorities) { + return new OAuth2Request(Collections.emptyMap(), clientId, + authorities, true, Collections.emptySet(), + Collections.emptySet(), null, null, null); + } + /** * 为第三方认证的用户创建本系统的 token */ @@ -45,14 +50,4 @@ public class TokenService { .map(user -> authorityService.loadAuthorities(Objects.requireNonNull(user.getId()))) .orElse(Collections.emptySet()); } - - public static OAuth2Request createOAuth2Request(String clientId, Collection authorities) { - return new OAuth2Request(Collections.emptyMap(), clientId, - authorities, true, Collections.emptySet(), - Collections.emptySet(), null, null, null); - } - - public static Collection defaultAuthorities() { - return Collections.singletonList(new SimpleGrantedAuthority("guest")); - } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index dbee1f5..8040195 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -12,6 +12,7 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.provider.OAuth2Provider; /** @@ -19,7 +20,6 @@ import cn.seqdata.oauth2.provider.OAuth2Provider; * Date: 2020-01-27 01:59 */ public final class SecurityUtils { - public static final String ROLE_SYS_ADMIN = "sysAdmin"; private SecurityUtils() { } @@ -63,25 +63,25 @@ public final class SecurityUtils { * 用户编号 */ public static Long userId(Principal principal) { - return toLong(detailValue(details(principal), "user_id")); + return toLong(detailValue(details(principal), OAuth2Constants.USER_ID)); } /** * 所属部门编号 */ public static Long deptId(Principal principal) { - return toLong(detailValue(details(principal), "dept_id")); + return toLong(detailValue(details(principal), OAuth2Constants.DEPT_ID)); } /** * 所属单位编号 */ public static Long orgId(Principal principal) { - return toLong(detailValue(details(principal), "org_id")); + return toLong(detailValue(details(principal), OAuth2Constants.ORG_ID)); } public static boolean isSysAdmin(Principal principal) { - return authorities(principal).contains(ROLE_SYS_ADMIN); + return authorities(principal).contains(OAuth2Constants.SYSADMIN); } /** diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index 1fc84f5..ba95e2d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -22,6 +22,8 @@ import cn.seqdata.wxapp.message.Code2SessionResponse; @Service @AllArgsConstructor public class WxAppService { + private final WxAppProvider provider; + private final WxAppFeignClient feignClient; //将微信access_token缓存到本地,每小时刷新一次 private final LoadingCache cache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.HOURS) @@ -39,9 +41,6 @@ public class WxAppService { } }); - private final WxAppProvider provider; - private final WxAppFeignClient feignClient; - public String accessToken(String appid) { return cache.getUnchecked(appid); } diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index a51ed44..5dc8af9 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -1,44 +1,44 @@ - 4.0.0 - - cn.seqdata.cloud - seqdata-cloud-parent - 2.2.1-SNAPSHOT - - seqdata-cloud-gateway - 网关,提供路由功能和其它一些通过用功能(防重放攻击、url过滤、统一日志等等) - - - org.springframework.cloud - spring-cloud-starter-gateway - - - org.springframework.boot - spring-boot-configuration-processor - true - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-gateway + 网关,提供路由功能和其它一些通过用功能(防重放攻击、url过滤、统一日志等等) + + + org.springframework.cloud + spring-cloud-starter-gateway + + + org.springframework.boot + spring-boot-configuration-processor + true + - - cn.seqdata.cloud - seqdata-cloud-log - ${project.version} - - - cn.seqdata.cloud - seqdata-cloud-auth-client - ${project.version} - + + cn.seqdata.cloud + seqdata-cloud-log + ${project.version} + + + cn.seqdata.cloud + seqdata-cloud-auth-client + ${project.version} + - - - - org.springframework.boot - spring-boot-starter-amqp - - + + + + org.springframework.boot + spring-boot-starter-amqp + + diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java index 9b9b6fd..6045e4d 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java @@ -13,6 +13,7 @@ import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; import cn.seqdata.gateway.filter.ResourceServerProperties; import cn.seqdata.gateway.swagger.OAuth2ClientProperties; import cn.seqdata.oauth2.client.FeignAuthzClient; +import cn.seqdata.oauth2.client.OAuth2Constants; import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; import cn.seqdata.oauth2.client.security.PathPermissionEvaluatorImpl; @@ -32,7 +33,7 @@ public class AuthClientConfiguration { @Bean public RemoteTokenServices remoteTokenServices(OAuth2ClientProperties credentials, ResourceServerProperties resource) { EnhanceUserAuthenticationConverter authenticationConverter = new EnhanceUserAuthenticationConverter(); - authenticationConverter.setDefaultAuthorities(new String[]{"guest"}); + authenticationConverter.setDefaultAuthorities(new String[]{OAuth2Constants.GUEST}); DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); tokenConverter.setUserTokenConverter(authenticationConverter); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java index 0abaf3b..65d8c0a 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/JacksonConfiguration.java @@ -42,7 +42,7 @@ public class JacksonConfiguration { static final class LongDeserializer extends StdDeserializer { private static final long serialVersionUID = 1L; - + public static final JacksonConfiguration.LongDeserializer instance = new JacksonConfiguration.LongDeserializer(); private LongDeserializer() { -- Gitee From 0ea466c8d8ef35dc590f5bb04ed010c524eb61bd Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 23 Sep 2020 11:50:58 +0800 Subject: [PATCH 083/228] =?UTF-8?q?principal.userId=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=8B=BF=E5=8F=96=EF=BC=9A=E6=9B=BF=E6=8D=A2=E4=B8=BAusername?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 8d03c3f..5ffcbe0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -7,15 +7,15 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import javax.servlet.http.HttpServletRequest; +import javax.transaction.Transactional; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.web.bind.annotation.*; -import org.springframework.web.server.ServerWebInputException; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; @@ -27,6 +27,7 @@ import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -107,10 +108,15 @@ public class ConnectController { .build(); } + @Transactional(rollbackOn = Exception.class) @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) { - long userId = Optional.ofNullable(SecurityUtils.userId(principal)) - .orElseThrow(() -> new ServerWebInputException("用户不存在,解绑失败。")); - userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); + String username = principal.getName(); + if(StringUtils.isBlank(username)) { + throw new RuntimeException("用户不存在,解绑失败。"); + } + UserAccount user = accountRepo.findByUsername(username) + .orElseThrow(() -> new RuntimeException("用户不存在,解绑失败。")); + userDetailRepo.deleteByIdClientAndUserId(registrationId, Objects.requireNonNull(user.getId())); } } -- Gitee From e200d409c088e93d5c59fee69f0866e609649f86 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 23 Sep 2020 15:24:42 +0800 Subject: [PATCH 084/228] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E6=B7=BB=E5=8A=A0=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/AuthzServerConfiguration.java | 4 ++-- .../MobileNonceAuthenticationProvider.java | 4 +--- .../oauth2/mobile/MobileTokenGranter.java | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index aad7248..63329ad 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -31,7 +31,6 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -44,6 +43,7 @@ import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -161,7 +161,7 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt //自定义的手机验证码登录 if(Objects.nonNull(mobileNonceHandler)) { MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceHandler); - tokenGranters.add(new MobileTokenGranter(authenticationProvider, tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new MobileTokenGranter(authenticationProvider, userService, tokenServices, clientDetails, requestFactory)); } return new CompositeTokenGranter(tokenGranters); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java index 1b488f1..069e2fa 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java @@ -1,7 +1,5 @@ package cn.seqdata.oauth2.mobile; -import java.util.Collections; - import org.apache.commons.lang3.math.NumberUtils; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -24,7 +22,7 @@ public class MobileNonceAuthenticationProvider implements AuthenticationProvider String mobile = String.valueOf(authentication.getPrincipal()); int nonce = NumberUtils.toInt(String.valueOf(authentication.getCredentials()), -1); mobileNonceHandler.check(mobile, nonce); - return new UsernamePasswordAuthenticationToken(mobile, null, Collections.emptyList()); + return new UsernamePasswordAuthenticationToken(mobile, null, authentication.getAuthorities()); } @Override diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java index f41019d..9e29960 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java @@ -1,5 +1,7 @@ package cn.seqdata.oauth2.mobile; +import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -7,12 +9,16 @@ import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AccountStatusException; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.provider.*; import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.service.UserService; + /** * Author: jrxian * Date: 2020-01-26 00:16 @@ -21,12 +27,14 @@ public class MobileTokenGranter extends AbstractTokenGranter { public static final String GRANT_TYPE = "mobile"; private final AuthenticationProvider authenticationProvider; + private final UserService userService; - public MobileTokenGranter(AuthenticationProvider authenticationProvider, + public MobileTokenGranter(AuthenticationProvider authenticationProvider, UserService userService, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); this.authenticationProvider = authenticationProvider; + this.userService = userService; } @Override @@ -35,7 +43,11 @@ public class MobileTokenGranter extends AbstractTokenGranter { String username = parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); String password = parameters.remove(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); - Authentication userAuth = new MobileNonceAuthenticationToken(username, password); + Collection authorities = userService.loadUser(null, username) + .map(UserAccount::getAuthorities) + .orElse(Collections.emptySet()); + + Authentication userAuth = new MobileNonceAuthenticationToken(username, password, authorities); ((AbstractAuthenticationToken) userAuth).setDetails(parameters); try { userAuth = authenticationProvider.authenticate(userAuth); -- Gitee From 27b28e4d3781a6e2fc982ae0ad7089991b3fe15f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 24 Sep 2020 08:38:37 +0800 Subject: [PATCH 085/228] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 13 +++-- .../MobileNonceAuthenticationFilter.java | 3 +- .../MobileNonceAuthenticationProvider.java | 34 +++++++---- .../MobileNonceAuthenticationToken.java | 56 ------------------- .../oauth2/mobile/MobileNonceHandler.java | 4 +- .../oauth2/mobile/MobileTokenGranter.java | 17 +----- 6 files changed, 35 insertions(+), 92 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 63329ad..46993dd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; +import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -43,7 +44,6 @@ import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; /** * Author: jrxian @@ -154,15 +154,16 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + //自定义的手机验证码登录 + //!!!必须在用户名密码验证前面 + if(Objects.nonNull(mobileNonceHandler)) { + MobileNonceAuthenticationProvider mobileNonceProvider = new MobileNonceAuthenticationProvider(userDetailsService, mobileNonceHandler); + tokenGranters.add(new MobileTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory)); + } //用户名密码验证 if(Objects.nonNull(authenticationManager)) { tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); } - //自定义的手机验证码登录 - if(Objects.nonNull(mobileNonceHandler)) { - MobileNonceAuthenticationProvider authenticationProvider = new MobileNonceAuthenticationProvider(mobileNonceHandler); - tokenGranters.add(new MobileTokenGranter(authenticationProvider, userService, tokenServices, clientDetails, requestFactory)); - } return new CompositeTokenGranter(tokenGranters); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java index eb88e81..d1c3d07 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; @@ -33,7 +34,7 @@ public class MobileNonceAuthenticationFilter extends AbstractAuthenticationProce String username = obtainUsername(request); String password = obtainPassword(request); - MobileNonceAuthenticationToken authRequest = new MobileNonceAuthenticationToken(username, password); + UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); return getAuthenticationManager().authenticate(authRequest); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java index 069e2fa..982683c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java @@ -1,32 +1,42 @@ package cn.seqdata.oauth2.mobile; +import java.util.Optional; + import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import lombok.AllArgsConstructor; /** * Author: jrxian * Date: 2020-01-26 00:52 */ -public class MobileNonceAuthenticationProvider implements AuthenticationProvider { +@AllArgsConstructor +public class MobileNonceAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { + private final UserDetailsService userDetailsService; private final MobileNonceHandler mobileNonceHandler; - public MobileNonceAuthenticationProvider(MobileNonceHandler mobileNonceHandler) { - this.mobileNonceHandler = mobileNonceHandler; + @Override + protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + try { + return Optional.ofNullable(userDetailsService.loadUserByUsername(username)) + .orElseThrow(() -> new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation")); + } catch(AuthenticationServiceException ex) { + throw ex; + } catch(Exception ex) { + throw new InternalAuthenticationServiceException(ex.getMessage(), ex); + } } @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { String mobile = String.valueOf(authentication.getPrincipal()); int nonce = NumberUtils.toInt(String.valueOf(authentication.getCredentials()), -1); mobileNonceHandler.check(mobile, nonce); - return new UsernamePasswordAuthenticationToken(mobile, null, authentication.getAuthorities()); - } - - @Override - public boolean supports(Class authentication) { - return authentication.isAssignableFrom(MobileNonceAuthenticationToken.class); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java deleted file mode 100644 index 6bcb9a8..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationToken.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.seqdata.oauth2.mobile; - -import java.util.Collection; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.SpringSecurityCoreVersion; - -/** - * Author: jrxian - * Date: 2020-01-22 12:12 - */ -public class MobileNonceAuthenticationToken extends AbstractAuthenticationToken { - private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; - - private final Object principal; - private Object credentials; - - public MobileNonceAuthenticationToken(Object principal, Object credentials) { - super(null); - this.principal = principal; - this.credentials = credentials; - setAuthenticated(false); - } - - public MobileNonceAuthenticationToken(Object principal, Object credentials, - Collection authorities) { - super(authorities); - this.principal = principal; - this.credentials = credentials; - super.setAuthenticated(true); // must use super, as we override - } - - public Object getCredentials() { - return this.credentials; - } - - public Object getPrincipal() { - return this.principal; - } - - public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { - if(isAuthenticated) { - throw new IllegalArgumentException( - "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); - } - - super.setAuthenticated(false); - } - - @Override - public void eraseCredentials() { - super.eraseCredentials(); - credentials = null; - } -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java index 9feaf46..59c8022 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java @@ -1,7 +1,5 @@ package cn.seqdata.oauth2.mobile; -import org.springframework.security.core.AuthenticationException; - /** * Author: jrxian * Date: 2020-01-26 10:28 @@ -15,5 +13,5 @@ public interface MobileNonceHandler { /** * 检查一次性验证码 */ - void check(String mobile, int nonce) throws AuthenticationException; + void check(String mobile, int nonce); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java index 9e29960..dc9ca6f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java @@ -1,24 +1,19 @@ package cn.seqdata.oauth2.mobile; -import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.authentication.AccountStatusException; import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.provider.*; import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import cn.seqdata.oauth2.jpa.rbac.UserAccount; -import cn.seqdata.oauth2.service.UserService; - /** * Author: jrxian * Date: 2020-01-26 00:16 @@ -27,14 +22,12 @@ public class MobileTokenGranter extends AbstractTokenGranter { public static final String GRANT_TYPE = "mobile"; private final AuthenticationProvider authenticationProvider; - private final UserService userService; - public MobileTokenGranter(AuthenticationProvider authenticationProvider, UserService userService, + public MobileTokenGranter(AuthenticationProvider authenticationProvider, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); this.authenticationProvider = authenticationProvider; - this.userService = userService; } @Override @@ -43,11 +36,7 @@ public class MobileTokenGranter extends AbstractTokenGranter { String username = parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); String password = parameters.remove(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); - Collection authorities = userService.loadUser(null, username) - .map(UserAccount::getAuthorities) - .orElse(Collections.emptySet()); - - Authentication userAuth = new MobileNonceAuthenticationToken(username, password, authorities); + Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password); ((AbstractAuthenticationToken) userAuth).setDetails(parameters); try { userAuth = authenticationProvider.authenticate(userAuth); -- Gitee From 3ab890bab27a2e615459c8b1082e084ab6dda2e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 29 Sep 2020 18:07:55 +0800 Subject: [PATCH 086/228] =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=B8=B8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/UserInfoController.java | 2 +- .../security/EnhancePrincipalExtractor.java | 6 ++- .../oauth2/AuthzServerConfiguration.java | 42 +++------------ .../seqdata/oauth2/DefaultTokenEnhancer.java | 52 +++++++++++++++++++ .../oauth2/controller/ConnectController.java | 9 ++-- .../oauth2/controller/CurrentController.java | 13 ++--- .../MobileNonceAuthenticationFilter.java | 10 ++-- .../oauth2/mobile/MobileTokenGranter.java | 4 +- .../seqdata/oauth2/provider/UserProfile.java | 24 +++++---- .../oauth2/wechat/WechatUserProfile.java | 8 +-- 10 files changed, 101 insertions(+), 69 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/DefaultTokenEnhancer.java diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java index 7153cd9..efa4d3a 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java @@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.RestController; * Date: 2020-01-27 17:16 */ @RestController -@RequestMapping("/user") +@RequestMapping("/current") public class UserInfoController { @ApiOperation("通行证") diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java index b32490a..cb4a2dd 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/security/EnhancePrincipalExtractor.java @@ -5,13 +5,15 @@ import java.util.Map; import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor; import org.springframework.stereotype.Component; +import cn.seqdata.oauth2.client.OAuth2Constants; + /** * Author: jrxian * Date: 2020-02-14 08:47 */ @Component -public class EnhancePrincipalExtractor implements PrincipalExtractor { - private static final String[] PRINCIPAL_KEYS = new String[]{"username", "mobile", "email", "id"}; +public class EnhancePrincipalExtractor implements PrincipalExtractor, OAuth2Constants { + private static final String[] PRINCIPAL_KEYS = new String[]{USERNAME, MOBILE, EMAIL, ID}; @Override public Object extractPrincipal(Map map) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 46993dd..b520c7c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -1,6 +1,9 @@ package cn.seqdata.oauth2; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -11,7 +14,6 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; @@ -28,22 +30,16 @@ import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGrante import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.jpa.rbac.User; -import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.mobile.MobileTokenGranter; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; -import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; -import cn.seqdata.oauth2.service.UserService; -import cn.seqdata.oauth2.util.SecurityUtils; /** * Author: jrxian @@ -54,12 +50,11 @@ import cn.seqdata.oauth2.util.SecurityUtils; @AllArgsConstructor public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { private final AuthenticationManager authenticationManager; - private final UserService userService; - private final UserRepo userRepo; private final JpaUserDetailsManager userDetailsService; private final RedisConnectionFactory redisConnectionFactory; private final ClientDetailRepo clientDetailRepo; private final MobileNonceHandler mobileNonceHandler; + private final DefaultTokenEnhancer tokenEnhancer; @Bean public PasswordEncoder passwordEncoder() { @@ -78,31 +73,6 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt return redisTokenStore; } - @Bean - public TokenEnhancer tokenEnhancer() { - return (accessToken, authentication) -> { - if(accessToken instanceof DefaultOAuth2AccessToken) { - Map attributes = new HashMap<>(); - - String clientId = SecurityUtils.clientId(authentication); - String username = SecurityUtils.username(authentication); - Optional userOptional = userService.loadUser(clientId, username); - - if(userOptional.isPresent()) { - UserAccount account = userOptional.get(); - User user = userRepo.getOne(Objects.requireNonNull(account.getId())); - attributes.put(OAuth2Constants.USER_ID, user.getId()); - attributes.put(OAuth2Constants.DEPT_ID, user.getDeptId()); - attributes.put(OAuth2Constants.ORG_ID, user.getOrgId()); - } - - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); - } - - return accessToken; - }; - } - @Override public void configure(AuthorizationServerSecurityConfigurer security) { security @@ -136,7 +106,7 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt .authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenStore(tokenStore()) - .tokenEnhancer(tokenEnhancer()) + .tokenEnhancer(tokenEnhancer) .tokenGranter(tokenGranter(endpoints)); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/DefaultTokenEnhancer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/DefaultTokenEnhancer.java new file mode 100644 index 0000000..15c7e96 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/DefaultTokenEnhancer.java @@ -0,0 +1,52 @@ +package cn.seqdata.oauth2; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.stereotype.Component; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * @author jrxian + * @date 2020-09-24 14:55 + */ +@Component +@AllArgsConstructor +public class DefaultTokenEnhancer implements TokenEnhancer { + private final UserRepo userRepo; + private final UserService userService; + + @Override + public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { + if(accessToken instanceof DefaultOAuth2AccessToken) { + Map attributes = new HashMap<>(); + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); + + String clientId = SecurityUtils.clientId(authentication); + String username = SecurityUtils.username(authentication); + Optional userOptional = userService.loadUser(clientId, username); + + if(userOptional.isPresent()) { + UserAccount account = userOptional.get(); + User user = userRepo.getOne(Objects.requireNonNull(account.getId())); + attributes.put(OAuth2Constants.USER_ID, user.getId()); + attributes.put(OAuth2Constants.DEPT_ID, user.getDeptId()); + attributes.put(OAuth2Constants.ORG_ID, user.getOrgId()); + } + } + + return accessToken; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 5ffcbe0..f47d490 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -16,7 +16,9 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.web.bind.annotation.*; +import lombok.AllArgsConstructor; +import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -27,7 +29,6 @@ import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; /** * Author: jrxian @@ -61,9 +62,9 @@ public class ConnectController { if(optional.isPresent()) { UserAccount account = optional.get(); - connectors.put("username", account.getUsername()); - connectors.put("mobile", account.getMobile()); - connectors.put("email", account.getEmail()); + connectors.put(OAuth2Constants.USERNAME, account.getUsername()); + connectors.put(OAuth2Constants.MOBILE, account.getMobile()); + connectors.put(OAuth2Constants.EMAIL, account.getEmail()); userDetailRepo.findByUserId(Objects.requireNonNull(account.getId())) .stream() diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 6e210a8..e2dde08 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; import lombok.AllArgsConstructor; +import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.params.UserAccountParam; @@ -91,7 +92,7 @@ public class CurrentController { UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); - checkField(ex, param, "name", UserAccountParam::getName); + checkField(ex, param, OAuth2Constants.NAME, UserAccountParam::getName); checkErrors(ex); entity.setName(param.getName()); @@ -106,8 +107,8 @@ public class CurrentController { UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); - checkField(ex, param, "email", UserAccountParam::getEmail); - checkExist(ex, param, "email", entity, UserAccountParam::getEmail, accountRepo::findByEmail); + checkField(ex, param, OAuth2Constants.EMAIL, UserAccountParam::getEmail); + checkExist(ex, param, OAuth2Constants.EMAIL, entity, UserAccountParam::getEmail, accountRepo::findByEmail); checkErrors(ex); entity.setEmail(param.getEmail()); @@ -122,8 +123,8 @@ public class CurrentController { UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); - checkField(ex, param, "mobile", UserAccountParam::getMobile); - checkExist(ex, param, "mobile", entity, UserAccountParam::getMobile, accountRepo::findByMobile); + checkField(ex, param, OAuth2Constants.MOBILE, UserAccountParam::getMobile); + checkExist(ex, param, OAuth2Constants.MOBILE, entity, UserAccountParam::getMobile, accountRepo::findByMobile); checkErrors(ex); entity.setMobile(param.getMobile()); @@ -138,7 +139,7 @@ public class CurrentController { UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); - checkField(ex, param, "password", UserAccountParam::getPassword); + checkField(ex, param, OAuth2Constants.PASSWORD, UserAccountParam::getPassword); checkErrors(ex); entity.setPassword(param.getPassword()); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java index d1c3d07..2450873 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java @@ -14,6 +14,8 @@ import org.springframework.security.web.authentication.AbstractAuthenticationPro import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import cn.seqdata.oauth2.OAuth2Constants; + /** * Author: jrxian * Date: 2020-01-22 12:06 @@ -43,17 +45,17 @@ public class MobileNonceAuthenticationFilter extends AbstractAuthenticationProce protected String obtainUsername(HttpServletRequest request) { String username = request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); if(StringUtils.isBlank(username)) { - username = request.getParameter("mobile"); + username = request.getParameter(OAuth2Constants.MOBILE); } - return ObjectUtils.defaultIfNull(username, ""); + return ObjectUtils.defaultIfNull(username, StringUtils.EMPTY); } protected String obtainPassword(HttpServletRequest request) { String password = request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); if(StringUtils.isBlank(password)) { - password = request.getParameter("nonce"); + password = request.getParameter(OAuth2Constants.NONCE); } - return ObjectUtils.defaultIfNull(password, ""); + return ObjectUtils.defaultIfNull(password, StringUtils.EMPTY); } public void setPostOnly(boolean postOnly) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java index dc9ca6f..2acead0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java @@ -14,12 +14,14 @@ import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import cn.seqdata.oauth2.OAuth2Constants; + /** * Author: jrxian * Date: 2020-01-26 00:16 */ public class MobileTokenGranter extends AbstractTokenGranter { - public static final String GRANT_TYPE = "mobile"; + public static final String GRANT_TYPE = OAuth2Constants.MOBILE; private final AuthenticationProvider authenticationProvider; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java index b635ca4..1eace87 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/UserProfile.java @@ -9,13 +9,15 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; +import cn.seqdata.oauth2.OAuth2Constants; + /** * Author: jrxian * Date: 2020-01-23 23:26 */ @lombok.Getter @lombok.Setter -public class UserProfile implements OAuth2User, Serializable { +public class UserProfile implements OAuth2User, OAuth2Constants, Serializable { private final OAuth2User delegate; public UserProfile(OAuth2User delegate) { @@ -51,29 +53,29 @@ public class UserProfile implements OAuth2User, Serializable { return delegate.getAuthorities(); } - /** - * 昵称 - */ - public String getNickname() { - return getAttribute("nickname"); - } - /** * 登录名 */ public String getUsername() { - return getAttribute("username"); + return getAttribute(USERNAME); } /** * 邮箱 */ public String getEmail() { - return getAttribute("email"); + return getAttribute(EMAIL); } public String getMobile() { - return getAttribute("mobile"); + return getAttribute(MOBILE); + } + + /** + * 昵称 + */ + public String getNickname() { + return getAttribute("nickname"); } /** diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java index e9e22f5..0d39f8c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java @@ -21,12 +21,12 @@ public class WechatUserProfile extends UserProfile { return getAttribute("openid"); } + public String getUnionId() { + return getAttribute("unionid"); + } + @Override public String getAvatar() { return getAttribute("headimgurl"); } - - public String getUnionId() { - return getAttribute("unionid"); - } } -- Gitee From 85308a7aa49afed207e735a9ef7c53fc1016b580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 30 Sep 2020 09:20:03 +0800 Subject: [PATCH 087/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=A7=A3=E7=BB=91?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index f47d490..75c9341 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -1,15 +1,16 @@ package cn.seqdata.oauth2.controller; import java.net.URI; +import java.security.GeneralSecurityException; import java.security.Principal; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; +import javax.security.auth.login.AccountNotFoundException; import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; -import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -111,13 +112,16 @@ public class ConnectController { @Transactional(rollbackOn = Exception.class) @DeleteMapping(value = "/{registrationId}") - public void unbind(@PathVariable String registrationId, Principal principal) { - String username = principal.getName(); - if(StringUtils.isBlank(username)) { - throw new RuntimeException("用户不存在,解绑失败。"); + public void unbind(@PathVariable String registrationId, Principal principal) throws GeneralSecurityException { + String username = SecurityUtils.username(principal); + UserDetailId entityId = new UserDetailId(registrationId, username); + if(userDetailRepo.existsById(entityId)) { + userDetailRepo.deleteById(entityId); + } else { + Long userId = userService.loadUser(registrationId, username) + .map(UserAccount::getId) + .orElseThrow(AccountNotFoundException::new); + userDetailRepo.deleteByIdClientAndUserId(registrationId, Objects.requireNonNull(userId)); } - UserAccount user = accountRepo.findByUsername(username) - .orElseThrow(() -> new RuntimeException("用户不存在,解绑失败。")); - userDetailRepo.deleteByIdClientAndUserId(registrationId, Objects.requireNonNull(user.getId())); } } -- Gitee From 935ce53fc3ee3e51af10c1a6460f4bd16fb14947 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 30 Sep 2020 09:29:39 +0800 Subject: [PATCH 088/228] =?UTF-8?q?=E6=8E=88=E6=9D=83=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=EF=BC=8C=E8=8E=B7=E5=8F=96userId=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/controller/CurrentController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index e2dde08..a02afd1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -17,7 +17,6 @@ import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.rbac.User; @@ -28,6 +27,7 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -147,8 +147,10 @@ public class CurrentController { } private UserAccount getUserAccount(Principal principal) { - return accountRepo.findByUsername(SecurityUtils.username(principal)) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); + String clientId = SecurityUtils.clientId(principal); + String username = SecurityUtils.username(principal); + Optional user = userService.loadUser(clientId, username); + return user.orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); } private void checkField(BindException ex, UserAccountParam param, String fieldName, -- Gitee From f61422834ab6a510d8809f020d29a668a96e215a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 12 Oct 2020 11:28:21 +0800 Subject: [PATCH 089/228] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/ConnectController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 75c9341..31f22b9 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -118,10 +118,10 @@ public class ConnectController { if(userDetailRepo.existsById(entityId)) { userDetailRepo.deleteById(entityId); } else { - Long userId = userService.loadUser(registrationId, username) + long userId = userService.loadUser(registrationId, username) .map(UserAccount::getId) .orElseThrow(AccountNotFoundException::new); - userDetailRepo.deleteByIdClientAndUserId(registrationId, Objects.requireNonNull(userId)); + userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); } } } -- Gitee From 266e0443c9d91b332832984b2567033dde29d31b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 2 Nov 2020 12:14:56 +0800 Subject: [PATCH 090/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9view,action=E7=9A=84?= =?UTF-8?q?=E6=9D=83=E9=99=90=E4=BF=9D=E5=AD=98=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/service/RouterService.java | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java index 465309e..1688ec0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java @@ -13,7 +13,6 @@ import com.google.common.collect.Multimap; import lombok.AllArgsConstructor; import cn.seqdata.antd.Router; -import cn.seqdata.core.domain.Named; import cn.seqdata.oauth2.domain.PermissionType; import cn.seqdata.oauth2.jpa.perm.ActionPermission; import cn.seqdata.oauth2.jpa.perm.JpaPermission; @@ -37,18 +36,6 @@ public class RouterService { private final ActionPermRepo actionPermRepo; private final AuthorityPermissionRepo permissionRepo; - private static Long getId(Named entity) { - return entity.getId(); - } - - private static Long toLong(Object key) { - if(key instanceof Number) { - return ((Number) key).longValue(); - } else { - return 0L; - } - } - /** * 所有菜单 */ @@ -114,7 +101,7 @@ public class RouterService { */ private boolean topRouterFilter(Router router, ModulePermission modulePerm) { return Objects.isNull(modulePerm.getParentId()) - && Objects.equals(toLong(router.key), getId(modulePerm.getSysPerm())); + && Objects.equals(toLong(router.key), modulePerm.getSysPermId()); } /** @@ -123,4 +110,12 @@ public class RouterService { private boolean chdRouterFilter(Router router, ModulePermission modulePerm) { return Objects.equals(toLong(router.key), modulePerm.getParentId()); } + + private static Long toLong(Object key) { + if(key instanceof Number) { + return ((Number) key).longValue(); + } else { + return 0L; + } + } } -- Gitee From 4b31b0e7fe9f8e285c9e0f5b779cea611b016992 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Mon, 9 Nov 2020 10:35:58 +0800 Subject: [PATCH 091/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3error=3D\"invalid=5Ft?= =?UTF-8?q?oken\",=20error=5Fdescription=3D\"adasfasgs\"=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8Ccache=E5=A4=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/CachedResourceServerTokenServices.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java index a6ffbfd..61fc982 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java @@ -10,6 +10,8 @@ import org.springframework.security.oauth2.provider.token.ResourceServerTokenSer import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.util.concurrent.UncheckedExecutionException; + import lombok.AllArgsConstructor; /** @@ -41,7 +43,11 @@ public class CachedResourceServerTokenServices implements ResourceServerTokenSer @Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { - return authenticationCache.getUnchecked(accessToken); + try { + return authenticationCache.getUnchecked(accessToken); + } catch(UncheckedExecutionException ex) { + throw new InvalidTokenException("Token无效"); + } } @Override -- Gitee From d9d836f8f4c5dbbc64d3e17288baed23887d0549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 9 Nov 2020 16:32:30 +0800 Subject: [PATCH 092/228] =?UTF-8?q?authz=E6=94=AF=E6=8C=81http:///?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/ResourceServerConfiguration.java | 16 +++++++++++--- .../src/main/resources/application.yml | 12 +++++------ .../src/main/resources/bootstrap.yml | 2 +- .../oauth2/controller/WxAppController.java | 6 +++--- .../oauth2/service/PermissionPredicate.java | 4 ++-- .../cn/seqdata/oauth2/util/SecurityUtils.java | 7 ++++++- .../src/main/resources/bootstrap.yml | 2 +- seqdata-cloud-gateway/pom.xml | 10 ++++++--- .../gateway/AuthClientConfiguration.java | 21 ++++++++++++------- .../src/main/resources/application.yml | 2 +- 10 files changed, 54 insertions(+), 28 deletions(-) diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java index 3844830..9ddd414 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; @@ -13,8 +14,9 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.RemoteTokenServices; import org.springframework.security.web.authentication.HttpStatusEntryPoint; +import org.springframework.web.client.RestTemplate; -import cn.seqdata.oauth2.client.OAuth2Constants; +import cn.seqdata.oauth2.client.DefaultRole; import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; /** @@ -27,14 +29,22 @@ import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Bean - public RemoteTokenServices remoteTokenServices(OAuth2ClientProperties credentials, ResourceServerProperties resource) { + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Bean + public RemoteTokenServices remoteTokenServices(RestTemplate restTemplate, + OAuth2ClientProperties credentials, ResourceServerProperties resource) { EnhanceUserAuthenticationConverter authenticationConverter = new EnhanceUserAuthenticationConverter(); - authenticationConverter.setDefaultAuthorities(new String[]{OAuth2Constants.GUEST}); + authenticationConverter.setDefaultAuthorities(new String[]{DefaultRole.anonymous.name()}); DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); tokenConverter.setUserTokenConverter(authenticationConverter); RemoteTokenServices services = new RemoteTokenServices(); + services.setRestTemplate(restTemplate); services.setAccessTokenConverter(tokenConverter); services.setClientId(credentials.getClientId()); diff --git a/seqdata-cloud-authc/src/main/resources/application.yml b/seqdata-cloud-authc/src/main/resources/application.yml index fe8dd46..2b78727 100644 --- a/seqdata-cloud-authc/src/main/resources/application.yml +++ b/seqdata-cloud-authc/src/main/resources/application.yml @@ -1,3 +1,7 @@ +logging: + level: + root: info + cn.seqdata: trace spring: profiles: active: jackson @@ -5,12 +9,8 @@ security: oauth2: resource: prefer-token-info: false - token-info-uri: http://localhost:30001/oauth/check_token - user-info-uri: http://localhost:30001/user/info + token-info-uri: http://authz/oauth/check_token + user-info-uri: http://authz/user/info client: client-id: client client-secret: secret -logging: - level: - root: info - cn.seqdata: trace \ No newline at end of file diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml index 79b91a4..677e710 100644 --- a/seqdata-cloud-authc/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authc/src/main/resources/bootstrap.yml @@ -9,4 +9,4 @@ spring: server-addr: ${spring.cloud.nacos.discovery.server-addr} file-extension: ${CONFIG_FORMAT:yml} server: - port: 30002 \ No newline at end of file + port: 0 \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index c4f032b..8e9209f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; -import cn.seqdata.oauth2.OAuth2Constants; +import cn.seqdata.oauth2.DefaultRole; import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.wechat.WechatUserProfile; @@ -47,7 +47,7 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); WxAppUtils.toAttributes(attributes, userInfo); - SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(OAuth2Constants.GUEST); + SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(DefaultRole.anonymous.name()); DefaultOAuth2User remoteUser = new DefaultOAuth2User(Collections.singletonList(defaultAuthority), attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); @@ -61,7 +61,7 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); - SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(OAuth2Constants.GUEST); + SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(DefaultRole.anonymous.name()); DefaultOAuth2User remoteUser = new DefaultOAuth2User(Collections.singleton(defaultAuthority), attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java index af0efa5..ec13e92 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/PermissionPredicate.java @@ -7,7 +7,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import cn.seqdata.jpa.JpaIdGenerated; -import cn.seqdata.oauth2.OAuth2Constants; +import cn.seqdata.oauth2.DefaultRole; import cn.seqdata.oauth2.domain.PermissionType; import cn.seqdata.oauth2.jpa.perm.JpaPermission; import cn.seqdata.oauth2.jpa.rbac.AuthorityPermission; @@ -24,7 +24,7 @@ public class PermissionPredicate implements Predicate { public PermissionPredicate(AuthorityPermissionRepo permissionRepo, PermissionType permType, Principal principal) { Set authorities = SecurityUtils.authorities(principal); - sysAdmin = authorities.contains(OAuth2Constants.SYSADMIN); + sysAdmin = authorities.contains(DefaultRole.sysadmin.name()); if(sysAdmin) { permissions = Collections.emptySet(); } else { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index 8040195..e6634ec 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -12,6 +12,7 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import cn.seqdata.oauth2.DefaultRole; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.provider.OAuth2Provider; @@ -81,7 +82,7 @@ public final class SecurityUtils { } public static boolean isSysAdmin(Principal principal) { - return authorities(principal).contains(OAuth2Constants.SYSADMIN); + return authorities(principal).contains(DefaultRole.sysadmin.name()); } /** @@ -101,9 +102,13 @@ public final class SecurityUtils { return mapAuthorities(grantedAuthorities(principal)); } + /** + * 所有角色全部转换为小写 + */ private static Set mapAuthorities(Collection authorities) { return authorities.stream() .map(GrantedAuthority::getAuthority) + .map(String::toLowerCase) .collect(Collectors.toSet()); } diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index a811d81..c2b6e4f 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -13,4 +13,4 @@ spring: server-addr: ${spring.cloud.nacos.discovery.server-addr} file-extension: ${CONFIG_FORMAT:yml} server: - port: 30001 \ No newline at end of file + port: 0 \ No newline at end of file diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index 5dc8af9..5f2678a 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -14,6 +14,10 @@ org.springframework.cloud spring-cloud-starter-gateway + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + org.springframework.boot spring-boot-configuration-processor @@ -33,9 +37,9 @@ + org.springframework.boot + spring-boot-starter-jdbc + --> org.springframework.boot spring-boot-starter-amqp diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java index 6045e4d..a4b8657 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java @@ -1,6 +1,7 @@ package cn.seqdata.gateway; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -8,12 +9,13 @@ import org.springframework.context.annotation.Primary; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.RemoteTokenServices; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.web.client.RestTemplate; import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; import cn.seqdata.gateway.filter.ResourceServerProperties; import cn.seqdata.gateway.swagger.OAuth2ClientProperties; +import cn.seqdata.oauth2.client.DefaultRole; import cn.seqdata.oauth2.client.FeignAuthzClient; -import cn.seqdata.oauth2.client.OAuth2Constants; import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; import cn.seqdata.oauth2.client.security.PathPermissionEvaluatorImpl; @@ -27,25 +29,30 @@ import cn.seqdata.oauth2.client.security.PathPermissionEvaluatorImpl; @EnableConfigurationProperties({OAuth2ClientProperties.class, ResourceServerProperties.class}) public class AuthClientConfiguration { + @Bean + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(); + } + /** * 连接授权服务器 */ @Bean - public RemoteTokenServices remoteTokenServices(OAuth2ClientProperties credentials, ResourceServerProperties resource) { + public RemoteTokenServices remoteTokenServices(RestTemplate restTemplate, + OAuth2ClientProperties credentials, ResourceServerProperties resource) { EnhanceUserAuthenticationConverter authenticationConverter = new EnhanceUserAuthenticationConverter(); - authenticationConverter.setDefaultAuthorities(new String[]{OAuth2Constants.GUEST}); + authenticationConverter.setDefaultAuthorities(new String[]{DefaultRole.anonymous.name()}); DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); tokenConverter.setUserTokenConverter(authenticationConverter); RemoteTokenServices services = new RemoteTokenServices(); - - services.setAccessTokenConverter(tokenConverter); - + services.setRestTemplate(restTemplate); services.setClientId(credentials.getClientId()); services.setClientSecret(credentials.getClientSecret()); - services.setCheckTokenEndpointUrl(resource.getTokenInfoUri()); + services.setAccessTokenConverter(tokenConverter); return services; } diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index bc2d564..148acd9 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -16,7 +16,7 @@ security: client-id: client client-secret: secret resource: - token-info-uri: http://localhost:30001/oauth/check_token + token-info-uri: http://authz/oauth/check_token prefer-token-info: true server: port: 8080 \ No newline at end of file -- Gitee From 90de095d4ea38a01cfdc6f8a87e798aab5ef6ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 9 Nov 2020 23:02:14 +0800 Subject: [PATCH 093/228] =?UTF-8?q?=E9=9C=80=E8=A6=81=E9=A2=9D=E5=A4=96?= =?UTF-8?q?=E8=AE=A1=E7=AE=97view=E5=92=8Caction=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84url=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/CachedResourceServerTokenServices.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java index 61fc982..51244ab 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java @@ -1,5 +1,6 @@ package cn.seqdata.gateway.filter; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.springframework.security.core.AuthenticationException; @@ -10,8 +11,6 @@ import org.springframework.security.oauth2.provider.token.ResourceServerTokenSer import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.util.concurrent.UncheckedExecutionException; - import lombok.AllArgsConstructor; /** @@ -44,14 +43,18 @@ public class CachedResourceServerTokenServices implements ResourceServerTokenSer @Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { try { - return authenticationCache.getUnchecked(accessToken); - } catch(UncheckedExecutionException ex) { - throw new InvalidTokenException("Token无效"); + return authenticationCache.get(accessToken); + } catch(ExecutionException ex) { + throw new InvalidTokenException("Invalid token " + accessToken, ex); } } @Override public OAuth2AccessToken readAccessToken(String accessToken) { - return accessTokenCache.getUnchecked(accessToken); + try { + return accessTokenCache.get(accessToken); + } catch(ExecutionException ex) { + throw new InvalidTokenException("Invalid token " + accessToken, ex); + } } } -- Gitee From 3851552edde155fcd9369f778fb06c5db0d30bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 10 Nov 2020 01:11:59 +0800 Subject: [PATCH 094/228] =?UTF-8?q?gateway=E6=94=AF=E6=8C=81anonymous?= =?UTF-8?q?=E7=9A=84url=E8=AE=BF=E9=97=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/UserInfoController.java | 8 ++ .../seqdata/gateway/GatewayConfiguration.java | 14 ++-- .../filter/authc/AllowlistPredicate.java | 2 +- .../filter/authc/AnonymousPredicate.java | 73 +++++++++++++++++++ 4 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java index efa4d3a..e2179b5 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java @@ -1,6 +1,7 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; +import javax.annotation.security.PermitAll; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; @@ -28,4 +29,11 @@ public class UserInfoController { public long test(long id) { return id; } + + @ApiOperation("标记为@PermitAll不检查任何权限") + @GetMapping("/permit") + @PermitAll + public long permit(long id) { + return id; + } } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index 2c5720b..a08493b 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -8,13 +8,14 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import lombok.extern.slf4j.Slf4j; -import cn.seqdata.gateway.filter.authc.AllowlistPredicate; +import cn.seqdata.gateway.filter.authc.AnonymousPredicate; import cn.seqdata.gateway.filter.authc.PathMatcherGlobalFilter; import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; import cn.seqdata.log.LogRecorder; import cn.seqdata.log.RabbitRecorder; import cn.seqdata.log.url.UrlRecord; +import cn.seqdata.oauth2.client.FeignAuthzClient; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; /** @@ -38,20 +39,23 @@ public class GatewayConfiguration { } @Bean + @ConditionalOnProperty(prefix = "spring.cloud.gateway.logging", value = "enable", matchIfMissing = true) public LoggingGlobalFilter loggingGlobalFilter(LogRecorder logRecorder, ResourceServerTokenServices tokenServices) { return new LoggingGlobalFilter(logRecorder, tokenServices); } @Bean - public PathMatcherGlobalFilter pathMatcherGlobalFilter(ResourceServerTokenServices tokenServices, - PathPermissionEvaluator permissionEvaluator) { - return new PathMatcherGlobalFilter(new AllowlistPredicate(), tokenServices, permissionEvaluator); + @ConditionalOnProperty(prefix = "spring.cloud.gateway.path-matcher", value = "enable", matchIfMissing = true) + public PathMatcherGlobalFilter pathMatcherGlobalFilter(FeignAuthzClient authzClient, + ResourceServerTokenServices tokenServices, PathPermissionEvaluator permissionEvaluator) { + return new PathMatcherGlobalFilter(new AnonymousPredicate(authzClient), tokenServices, permissionEvaluator); } /** * 对前端签名进行验证 */ -// @Bean + @Bean + @ConditionalOnProperty(prefix = "spring.cloud.gateway.hmac-verify", value = "enable") public HmacVerifyGlobalFilter hmacVerifyGlobalFilter() { return new HmacVerifyGlobalFilter(); } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java index a0e6135..60ae08a 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java @@ -16,7 +16,7 @@ import org.springframework.util.PathMatcher; * Date: 2020-02-12 23:49 */ public class AllowlistPredicate implements Predicate { - private static final PathMatcher pathMatcher = new AntPathMatcher(); + protected static final PathMatcher pathMatcher = new AntPathMatcher(); private final List allowlist = new ArrayList<>(); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java new file mode 100644 index 0000000..6d7270c --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java @@ -0,0 +1,73 @@ +package cn.seqdata.gateway.filter.authc; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; + +import cn.seqdata.oauth2.client.DefaultRole; +import cn.seqdata.oauth2.client.FeignAuthzClient; +import cn.seqdata.oauth2.client.entity.Authority; +import cn.seqdata.oauth2.client.entity.AuthorityPermission; +import cn.seqdata.oauth2.client.entity.IdentifiableObject; + +/** + * @author jrxian + * @date 2020-11-10 00:54 + */ +public class AnonymousPredicate extends AllowlistPredicate implements InitializingBean { + private final FeignAuthzClient authzClient; + private final List allowlist = new ArrayList<>(); + private long nextTimestamp; + + public AnonymousPredicate(FeignAuthzClient authzClient) { + super(); + this.authzClient = authzClient; + } + + @Override + public boolean test(ServerHttpRequest request) { + afterPropertiesSet(); + + RequestPath requestPath = request.getPath(); + String path = requestPath.value(); + + for(String pattern : allowlist) { + if(pathMatcher.match(pattern, path)) { + return true; + } + } + + return super.test(request); + } + + @Override + public void afterPropertiesSet() { + long currTimestamp = System.currentTimeMillis(); + if(currTimestamp > nextTimestamp) { + Optional optional = authzClient.findAuthorityByIdentifier(DefaultRole.anonymous.name()); + + if(optional.isPresent()) { + Authority authority = optional.get(); + allowlist.clear(); + allowlist.addAll(urls(authority.getId())); + } + + nextTimestamp = currTimestamp + TimeUnit.HOURS.toMillis(1); + } + } + + private Collection urls(long authority) { + return authzClient.findPermissions(authority, "url") + .stream() + .map(AuthorityPermission::getPermission) + .map(IdentifiableObject::getIdentifier) + .collect(Collectors.toSet()); + } +} -- Gitee From 056a0ba52e5d30b7b25bf3a7c6b1e48c1a9ceb82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 11 Nov 2020 23:28:11 +0800 Subject: [PATCH 095/228] =?UTF-8?q?=E6=94=AF=E6=8C=81view=E5=92=8Caction?= =?UTF-8?q?=E7=9A=84urls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/MethodSecurityConfiguration.java | 5 +- .../gateway/AuthClientConfiguration.java | 7 ++- .../seqdata/gateway/GatewayConfiguration.java | 12 ++++- .../cn/seqdata/gateway/GatewayController.java | 46 +++++++++++++++++++ .../CachedResourceServerTokenServices.java | 9 ++-- .../filter/authc/AnonymousPredicate.java | 20 ++++---- 6 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java index 5c69acf..b67d08b 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java @@ -17,6 +17,7 @@ import org.springframework.security.access.vote.UnanimousBased; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; +import cn.seqdata.oauth2.client.FeignAuthzClient; import cn.seqdata.oauth2.client.security.*; /** @@ -48,8 +49,8 @@ public class MethodSecurityConfiguration extends GlobalMethodSecurityConfigurati } @Bean - public PathPermissionEvaluator pathPermissionEvaluator() { - return new PathPermissionEvaluatorImpl("repo"); + public PathPermissionEvaluator pathPermissionEvaluator(FeignAuthzClient authzClient) { + return new PathPermissionEvaluatorImpl("repo", authzClient); } @Bean diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java index a4b8657..378dc18 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java @@ -8,7 +8,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.RemoteTokenServices; -import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import org.springframework.web.client.RestTemplate; import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; @@ -62,12 +61,12 @@ public class AuthClientConfiguration { */ @Bean @Primary - public ResourceServerTokenServices tokenServices(RemoteTokenServices tokenServices) { + public CachedResourceServerTokenServices tokenServices(RemoteTokenServices tokenServices) { return new CachedResourceServerTokenServices(tokenServices); } @Bean - public PathPermissionEvaluator permissionEvaluator() { - return new PathPermissionEvaluatorImpl("url"); + public PathPermissionEvaluator permissionEvaluator(FeignAuthzClient authzClient) { + return new PathPermissionEvaluatorImpl("url", authzClient); } } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index a08493b..14235b0 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -1,10 +1,13 @@ package cn.seqdata.gateway; +import java.util.function.Predicate; + import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import lombok.extern.slf4j.Slf4j; @@ -44,11 +47,16 @@ public class GatewayConfiguration { return new LoggingGlobalFilter(logRecorder, tokenServices); } + @Bean + public AnonymousPredicate anonymousPredicate(FeignAuthzClient authzClient) { + return new AnonymousPredicate(authzClient); + } + @Bean @ConditionalOnProperty(prefix = "spring.cloud.gateway.path-matcher", value = "enable", matchIfMissing = true) - public PathMatcherGlobalFilter pathMatcherGlobalFilter(FeignAuthzClient authzClient, + public PathMatcherGlobalFilter pathMatcherGlobalFilter(Predicate pathPredicate, ResourceServerTokenServices tokenServices, PathPermissionEvaluator permissionEvaluator) { - return new PathMatcherGlobalFilter(new AnonymousPredicate(authzClient), tokenServices, permissionEvaluator); + return new PathMatcherGlobalFilter(pathPredicate, tokenServices, permissionEvaluator); } /** diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java new file mode 100644 index 0000000..7af455e --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java @@ -0,0 +1,46 @@ +package cn.seqdata.gateway; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import lombok.AllArgsConstructor; + +import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; +import cn.seqdata.gateway.filter.authc.AnonymousPredicate; + +/** + * @author jrxian + * @date 2020-11-11 23:03 + */ +@RestController +@AllArgsConstructor +public class GatewayController { + private final AnonymousPredicate allowlistPredicate; + private final CachedResourceServerTokenServices tokenServices; + + @PostMapping("/allowlist") + public void refreshAllowlist() { + allowlistPredicate.afterPropertiesSet(); + } + + @DeleteMapping("/authentication/{key}") + public void invalidateAuthentication(@PathVariable("key") String key) { + tokenServices.authenticationCache.invalidate(key); + } + + @DeleteMapping("/authentication") + public void invalidateAuthentications() { + tokenServices.authenticationCache.invalidateAll(); + } + + @DeleteMapping("/token/{key}") + public void invalidateToken(@PathVariable("key") String key) { + tokenServices.accessTokenCache.invalidate(key); + } + + @PostMapping("/token") + public void invalidateTokens() { + tokenServices.accessTokenCache.invalidateAll(); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java index 51244ab..7eee501 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java @@ -3,6 +3,7 @@ package cn.seqdata.gateway.filter; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import org.springframework.lang.NonNull; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; @@ -20,22 +21,22 @@ import lombok.AllArgsConstructor; public class CachedResourceServerTokenServices implements ResourceServerTokenServices { private final ResourceServerTokenServices delegate; - private final LoadingCache authenticationCache = CacheBuilder.newBuilder() + public final LoadingCache authenticationCache = CacheBuilder.newBuilder() .maximumSize(1024) .expireAfterAccess(30, TimeUnit.SECONDS) .build(new CacheLoader() { @Override - public OAuth2Authentication load(String key) { + public OAuth2Authentication load(@NonNull String key) { return delegate.loadAuthentication(key); } }); - private final LoadingCache accessTokenCache = CacheBuilder.newBuilder() + public final LoadingCache accessTokenCache = CacheBuilder.newBuilder() .maximumSize(1024) .expireAfterAccess(30, TimeUnit.SECONDS) .build(new CacheLoader() { @Override - public OAuth2AccessToken load(String key) { + public OAuth2AccessToken load(@NonNull String key) { return delegate.readAccessToken(key); } }); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java index 6d7270c..8cdb25f 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java @@ -1,9 +1,9 @@ package cn.seqdata.gateway.filter.authc; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -20,6 +20,7 @@ import cn.seqdata.oauth2.client.entity.IdentifiableObject; /** * @author jrxian * @date 2020-11-10 00:54 + * 将anonymous的全部url放入白名单,并且每小时刷新一次 */ public class AnonymousPredicate extends AllowlistPredicate implements InitializingBean { private final FeignAuthzClient authzClient; @@ -55,19 +56,18 @@ public class AnonymousPredicate extends AllowlistPredicate implements Initializi if(optional.isPresent()) { Authority authority = optional.get(); + + Set urls = authzClient.findPermissions(authority.getId(), "url") + .stream() + .map(AuthorityPermission::getPermission) + .map(IdentifiableObject::getIdentifier) + .collect(Collectors.toSet()); + allowlist.clear(); - allowlist.addAll(urls(authority.getId())); + allowlist.addAll(urls); } nextTimestamp = currTimestamp + TimeUnit.HOURS.toMillis(1); } } - - private Collection urls(long authority) { - return authzClient.findPermissions(authority, "url") - .stream() - .map(AuthorityPermission::getPermission) - .map(IdentifiableObject::getIdentifier) - .collect(Collectors.toSet()); - } } -- Gitee From f41247dfc06bf256195c7a5e1216ca9d4274f5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 11 Nov 2020 23:45:12 +0800 Subject: [PATCH 096/228] =?UTF-8?q?gateway=E6=B7=BB=E5=8A=A0controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/gateway/GatewayConfiguration.java | 14 ++-- .../cn/seqdata/gateway/GatewayController.java | 53 ++++++++++++-- .../filter/authc/AllowlistPredicate.java | 14 +++- .../filter/authc/AnonymousPredicate.java | 73 ------------------- 4 files changed, 61 insertions(+), 93 deletions(-) delete mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index 14235b0..e13798d 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -1,24 +1,20 @@ package cn.seqdata.gateway; -import java.util.function.Predicate; - import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import lombok.extern.slf4j.Slf4j; -import cn.seqdata.gateway.filter.authc.AnonymousPredicate; +import cn.seqdata.gateway.filter.authc.AllowlistPredicate; import cn.seqdata.gateway.filter.authc.PathMatcherGlobalFilter; import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; import cn.seqdata.log.LogRecorder; import cn.seqdata.log.RabbitRecorder; import cn.seqdata.log.url.UrlRecord; -import cn.seqdata.oauth2.client.FeignAuthzClient; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; /** @@ -48,15 +44,15 @@ public class GatewayConfiguration { } @Bean - public AnonymousPredicate anonymousPredicate(FeignAuthzClient authzClient) { - return new AnonymousPredicate(authzClient); + public AllowlistPredicate allowlistPredicate() { + return new AllowlistPredicate(); } @Bean @ConditionalOnProperty(prefix = "spring.cloud.gateway.path-matcher", value = "enable", matchIfMissing = true) - public PathMatcherGlobalFilter pathMatcherGlobalFilter(Predicate pathPredicate, + public PathMatcherGlobalFilter pathMatcherGlobalFilter(AllowlistPredicate allowlistPredicate, ResourceServerTokenServices tokenServices, PathPermissionEvaluator permissionEvaluator) { - return new PathMatcherGlobalFilter(pathPredicate, tokenServices, permissionEvaluator); + return new PathMatcherGlobalFilter(allowlistPredicate, tokenServices, permissionEvaluator); } /** diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java index 7af455e..2bc99af 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java @@ -1,13 +1,20 @@ package cn.seqdata.gateway; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RestController; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; -import cn.seqdata.gateway.filter.authc.AnonymousPredicate; +import cn.seqdata.gateway.filter.authc.AllowlistPredicate; +import cn.seqdata.oauth2.client.DefaultRole; +import cn.seqdata.oauth2.client.FeignAuthzClient; +import cn.seqdata.oauth2.client.entity.AuthorityPermission; +import cn.seqdata.oauth2.client.entity.IdentifiableObject; +import cn.seqdata.oauth2.client.entity.NamedObject; /** * @author jrxian @@ -16,12 +23,30 @@ import cn.seqdata.gateway.filter.authc.AnonymousPredicate; @RestController @AllArgsConstructor public class GatewayController { - private final AnonymousPredicate allowlistPredicate; + private final FeignAuthzClient authzClient; + private final AllowlistPredicate allowlistPredicate; private final CachedResourceServerTokenServices tokenServices; + @GetMapping("/allowlist") + public Collection getAllowlist() { + return allowlistPredicate.allowlist; + } + @PostMapping("/allowlist") - public void refreshAllowlist() { - allowlistPredicate.afterPropertiesSet(); + public void setAllowlist(Collection allowlist) { + allowlistPredicate.allowlist.clear(); + allowlistPredicate.allowlist.addAll(allowlist); + } + + @GetMapping("/anonymous") + public Collection getAnonymous() { + return allowlistPredicate.anonymous; + } + + @PostMapping("/anonymous") + public void refreshAnonymous() { + allowlistPredicate.anonymous.clear(); + allowlistPredicate.anonymous.addAll(anonymousUrls()); } @DeleteMapping("/authentication/{key}") @@ -43,4 +68,16 @@ public class GatewayController { public void invalidateTokens() { tokenServices.accessTokenCache.invalidateAll(); } + + private Set anonymousUrls() { + return authzClient.findAuthorityByIdentifier(DefaultRole.anonymous.name()) + .map(NamedObject::getId) + .map(x -> authzClient.findPermissions(x, "url") + .stream() + .map(AuthorityPermission::getPermission) + .map(IdentifiableObject::getIdentifier) + .collect(Collectors.toSet()) + ) + .orElse(Collections.emptySet()); + } } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java index 60ae08a..d146244 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java @@ -1,9 +1,9 @@ package cn.seqdata.gateway.filter.authc; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import java.util.function.Predicate; import org.springframework.http.server.RequestPath; @@ -18,7 +18,8 @@ import org.springframework.util.PathMatcher; public class AllowlistPredicate implements Predicate { protected static final PathMatcher pathMatcher = new AntPathMatcher(); - private final List allowlist = new ArrayList<>(); + public final Set allowlist = new HashSet<>(); + public final Set anonymous = new HashSet<>(); public AllowlistPredicate() { //authz: 授权服务器是否需要token由授权服务器决定,网关不做前置拦截 @@ -41,11 +42,18 @@ public class AllowlistPredicate implements Predicate { RequestPath requestPath = request.getPath(); String path = requestPath.value(); + //静态配置的白名单 for(String pattern : allowlist) { if(pathMatcher.match(pattern, path)) { return true; } } + //匿名用户动态配置的白名单,需要定期刷新 + for(String pattern : anonymous) { + if(pathMatcher.match(pattern, path)) { + return true; + } + } return false; } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java deleted file mode 100644 index 8cdb25f..0000000 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousPredicate.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.seqdata.gateway.filter.authc; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import org.springframework.beans.factory.InitializingBean; -import org.springframework.http.server.RequestPath; -import org.springframework.http.server.reactive.ServerHttpRequest; - -import cn.seqdata.oauth2.client.DefaultRole; -import cn.seqdata.oauth2.client.FeignAuthzClient; -import cn.seqdata.oauth2.client.entity.Authority; -import cn.seqdata.oauth2.client.entity.AuthorityPermission; -import cn.seqdata.oauth2.client.entity.IdentifiableObject; - -/** - * @author jrxian - * @date 2020-11-10 00:54 - * 将anonymous的全部url放入白名单,并且每小时刷新一次 - */ -public class AnonymousPredicate extends AllowlistPredicate implements InitializingBean { - private final FeignAuthzClient authzClient; - private final List allowlist = new ArrayList<>(); - private long nextTimestamp; - - public AnonymousPredicate(FeignAuthzClient authzClient) { - super(); - this.authzClient = authzClient; - } - - @Override - public boolean test(ServerHttpRequest request) { - afterPropertiesSet(); - - RequestPath requestPath = request.getPath(); - String path = requestPath.value(); - - for(String pattern : allowlist) { - if(pathMatcher.match(pattern, path)) { - return true; - } - } - - return super.test(request); - } - - @Override - public void afterPropertiesSet() { - long currTimestamp = System.currentTimeMillis(); - if(currTimestamp > nextTimestamp) { - Optional optional = authzClient.findAuthorityByIdentifier(DefaultRole.anonymous.name()); - - if(optional.isPresent()) { - Authority authority = optional.get(); - - Set urls = authzClient.findPermissions(authority.getId(), "url") - .stream() - .map(AuthorityPermission::getPermission) - .map(IdentifiableObject::getIdentifier) - .collect(Collectors.toSet()); - - allowlist.clear(); - allowlist.addAll(urls); - } - - nextTimestamp = currTimestamp + TimeUnit.HOURS.toMillis(1); - } - } -} -- Gitee From 065f084b4446a91967e22121aadf0cd93dc12472 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Thu, 12 Nov 2020 16:01:23 +0800 Subject: [PATCH 097/228] =?UTF-8?q?fix=E6=8C=89=E9=92=AE=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/service/RouterService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java index 1688ec0..119c74d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java @@ -10,7 +10,6 @@ import org.apache.commons.lang3.BooleanUtils; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import com.google.common.collect.Multimap; -import lombok.AllArgsConstructor; import cn.seqdata.antd.Router; import cn.seqdata.oauth2.domain.PermissionType; @@ -23,6 +22,7 @@ import cn.seqdata.oauth2.repos.perm.ModulePermRepo; import cn.seqdata.oauth2.repos.perm.SysPermRepo; import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; import cn.seqdata.oauth2.util.RouterUtils; +import lombok.AllArgsConstructor; /** * @author jrxian @@ -86,11 +86,11 @@ public class RouterService { */ private void doNestedRouter(Router router, BiPredicate predicate, Collection modulePerms, Multimap actionPerms) { + router.meta.actions.addAll(actionPerms.get(router.meta.view)); modulePerms.stream() .filter(modulePerm -> predicate.test(router, modulePerm)) .forEach(modulePerm -> { Router childRouter = RouterUtils.toRouter(router, modulePerm); - router.meta.actions.addAll(actionPerms.get(router.meta.view)); router.children.add(childRouter); doNestedRouter(childRouter, this::chdRouterFilter, modulePerms, actionPerms); }); -- Gitee From e6fc78dff8a014bfca298d4a434ec3d35cc9d55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 13 Nov 2020 15:50:22 +0800 Subject: [PATCH 098/228] =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E6=94=AF=E6=8C=81@PermitAll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ation.java => AuthcServerApplication.java} | 4 +- .../oauth2/MethodSecurityConfiguration.java | 62 ++++++++++--------- .../oauth2/ResourceServerConfiguration.java | 7 ++- .../oauth2/WebSecurityConfiguration.java | 33 ---------- .../controller/SpringDataController.java | 3 + .../oauth2/controller/UserInfoController.java | 12 +--- 6 files changed, 46 insertions(+), 75 deletions(-) rename seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/{ResServerApplication.java => AuthcServerApplication.java} (84%) delete mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java similarity index 84% rename from seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java rename to seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java index d037c18..d974743 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResServerApplication.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java @@ -14,9 +14,9 @@ import cn.seqdata.oauth2.client.FeignAuthzClient; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackageClasses = FeignAuthzClient.class) -public class ResServerApplication { +public class AuthcServerApplication { public static void main(String[] args) { - new SpringApplication(ResServerApplication.class).run(args); + new SpringApplication(AuthcServerApplication.class).run(args); } } diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java index b67d08b..136dfc1 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java @@ -1,19 +1,15 @@ package cn.seqdata.oauth2; -import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionVoter; -import org.springframework.security.access.expression.method.ExpressionBasedAnnotationAttributeFactory; +import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; import org.springframework.security.access.method.MethodSecurityMetadataSource; -import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource; -import org.springframework.security.access.vote.AffirmativeBased; -import org.springframework.security.access.vote.UnanimousBased; +import org.springframework.security.access.vote.AbstractAccessDecisionManager; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; @@ -23,26 +19,14 @@ import cn.seqdata.oauth2.client.security.*; /** * Author: jrxian * Date: 2020-02-15 23:50 + * 增加jsr250Enabled支持,如果不需要检查某个RestController的权限,在类或方法上标记@PermitAll注解,但此时必须是登录用户 + * 如果要连登录都不需要,需要在额外配置http.authorizeRequests().antMatchers("/prefix/**").permitAll(); + * 即使在url层面放过,仍然需要在RestController标记@PermitAll注解,否则仍然会被后续拦截器拦截 */ @Configuration -@EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true) public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration { - @Autowired - private ControllerMethodVoter controllerMethodVoter; - - @Bean - @Primary - public MethodSecurityMetadataSource methodSecurityMetadataSource() { - ExpressionBasedAnnotationAttributeFactory attributeFactory = new ExpressionBasedAnnotationAttributeFactory(getExpressionHandler()); - PrePostAnnotationSecurityMetadataSource prePostSource = new PrePostAnnotationSecurityMetadataSource(attributeFactory); - - MethodSecurityMetadataSource customSource = new ControllerMethodSecurityMetadataSource(); - - //对此类的大幅改造主要是由于原生的DelegatingMethodSecurityMetadataSource无法同时支持多个注解的bug导致 - return new DelegatingMethodSecurityMetadataSource(Arrays.asList(prePostSource, customSource)); - } - @Bean public RowsPermissionEvaluator rowsPermissionEvaluator(List evaluators) { return new RowsPermissionEvaluator(evaluators); @@ -58,15 +42,37 @@ public class MethodSecurityConfiguration extends GlobalMethodSecurityConfigurati return new ControllerMethodVoter(permissionEvaluator); } + @Autowired + private ControllerMethodVoter controllerMethodVoter; + @Override protected AccessDecisionManager accessDecisionManager() { - AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager(); + AccessDecisionManager accessDecisionManager = super.accessDecisionManager(); + if(accessDecisionManager instanceof AbstractAccessDecisionManager) { + List> voters = ((AbstractAccessDecisionManager) accessDecisionManager).getDecisionVoters(); + + //增加@RestController带有@RequestMapping注解的Method的拦截器 + voters.add(controllerMethodVoter); + } + return accessDecisionManager; + } + + @Override + protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() { + return new ControllerMethodSecurityMetadataSource(); + } - //增加@RestController带有@RequestMapping注解的Method的拦截器 - List> decisionVoters = accessDecisionManager.getDecisionVoters(); - decisionVoters.add(controllerMethodVoter); + @Bean + @Override + public MethodSecurityMetadataSource methodSecurityMetadataSource() { + MethodSecurityMetadataSource source = super.methodSecurityMetadataSource(); - //必须全票通过 - return new UnanimousBased(decisionVoters); + if(source instanceof DelegatingMethodSecurityMetadataSource) { + //系统默认的不支持多级注解,为使ControllerMethodVoter生效,用cn.seqdata下的同名代替 + DelegatingMethodSecurityMetadataSource delegating = (DelegatingMethodSecurityMetadataSource) source; + return new cn.seqdata.oauth2.client.security.DelegatingMethodSecurityMetadataSource(delegating.getMethodSecurityMetadataSources()); + } else { + return source; + } } } diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java index 9ddd414..279123b 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java @@ -60,12 +60,15 @@ public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter http.csrf() .disable(); + http.httpBasic(); + http.authorizeRequests() - .antMatchers("/anonymous/**", "/v2/api-docs/**", "/swagger-resources/**") + .antMatchers("/v2/api-docs/**", "/swagger-resources/**" + , "/public/**", "/anonymous/**", "/joda/**") .permitAll(); http.authorizeRequests() - .antMatchers("/**") + .anyRequest() .authenticated(); http.exceptionHandling() diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java deleted file mode 100644 index 0bffd00..0000000 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.seqdata.oauth2; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; - -/** - * Author: jrxian - * Date: 2019-10-02 19:27 - */ -@Configuration -@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) -public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { - - @Override - public void configure(WebSecurity web) throws Exception { - super.configure(web); - - web.ignoring() - .antMatchers("/favicon.ico" - , "/resources/**", "/css/**", "/js/**" - , "/swagger-ui.html", "/webjars/**", "/plugins/**" - ); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); - } -} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java index 3b818af..9a576e5 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SpringDataController.java @@ -1,5 +1,7 @@ package cn.seqdata.oauth2.controller; +import javax.annotation.security.PermitAll; + import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; @@ -12,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController; * @author jrxian * @date 2020-08-20 16:43 */ +@PermitAll @RestController @RequestMapping("/spring/data") public class SpringDataController { diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java index e2179b5..6b83ea8 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java @@ -1,7 +1,6 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; -import javax.annotation.security.PermitAll; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; @@ -24,16 +23,9 @@ public class UserInfoController { } @ApiOperation("用于测试行级权限是否生效,当输入的id是用户id时通过验证,否则报403") - @GetMapping("/test") + @GetMapping("/permission") @PreAuthorize("hasPermission(#id, null, 'read')") - public long test(long id) { - return id; - } - - @ApiOperation("标记为@PermitAll不检查任何权限") - @GetMapping("/permit") - @PermitAll - public long permit(long id) { + public long permission(long id) { return id; } } -- Gitee From b6a2174f734ac43a703091e430346885f7268682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 13 Nov 2020 15:51:42 +0800 Subject: [PATCH 099/228] =?UTF-8?q?=E7=BD=91=E5=85=B3=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=88=B7=E6=96=B0=E5=92=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=88=A0=E9=99=A4url=E7=9A=84controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AllowlistController.java | 57 +++++++++++++++++++ .../gateway/controller/AnonymousService.java | 38 +++++++++++++ .../{ => controller}/GatewayController.java | 48 +--------------- 3 files changed, 97 insertions(+), 46 deletions(-) create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AllowlistController.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AnonymousService.java rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/{ => controller}/GatewayController.java (41%) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AllowlistController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AllowlistController.java new file mode 100644 index 0000000..c0ac63a --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AllowlistController.java @@ -0,0 +1,57 @@ +package cn.seqdata.gateway.controller; + +import java.util.Collection; + +import org.springframework.web.bind.annotation.*; +import lombok.AllArgsConstructor; + +import cn.seqdata.gateway.filter.authc.AllowlistPredicate; + +/** + * @author jrxian + * @date 2020-11-12 00:24 + */ +@RestController +@RequestMapping("/allow") +@AllArgsConstructor +public class AllowlistController { + private final AllowlistPredicate allowlistPredicate; + private final AnonymousService anonymousService; + + @PostMapping + public void save(@RequestBody String url) { + allowlistPredicate.allowlist.add(url); + } + + @DeleteMapping + public void delete(@RequestBody String url) { + allowlistPredicate.allowlist.remove(url); + } + + @GetMapping("/list") + public Collection findAll() { + return allowlistPredicate.allowlist; + } + + @PostMapping("/list") + public void saveAll(@RequestBody Collection allowlist) { + allowlistPredicate.allowlist.clear(); + allowlistPredicate.allowlist.addAll(allowlist); + } + + @DeleteMapping("/list") + public void deleteAll() { + allowlistPredicate.allowlist.clear(); + } + + @GetMapping("/anonymous") + public Collection getAnonymous() { + return allowlistPredicate.anonymous; + } + + @PostMapping("/anonymous") + public void refreshAnonymous() { + allowlistPredicate.anonymous.clear(); + allowlistPredicate.anonymous.addAll(anonymousService.get()); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AnonymousService.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AnonymousService.java new file mode 100644 index 0000000..2414477 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AnonymousService.java @@ -0,0 +1,38 @@ +package cn.seqdata.gateway.controller; + +import java.util.Collections; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; +import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.client.DefaultRole; +import cn.seqdata.oauth2.client.FeignAuthzClient; +import cn.seqdata.oauth2.client.entity.AuthorityPermission; +import cn.seqdata.oauth2.client.entity.IdentifiableObject; +import cn.seqdata.oauth2.client.entity.NamedObject; + +/** + * @author jrxian + * @date 2020-11-12 00:26 + */ +@Service +@AllArgsConstructor +public class AnonymousService implements Supplier> { + private final FeignAuthzClient authzClient; + + @Override + public Set get() { + return authzClient.findAuthorityByIdentifier(DefaultRole.anonymous.name()) + .map(NamedObject::getId) + .map(x -> authzClient.findPermissions(x, "url") + .stream() + .map(AuthorityPermission::getPermission) + .map(IdentifiableObject::getIdentifier) + .collect(Collectors.toSet()) + ) + .orElse(Collections.emptySet()); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/GatewayController.java similarity index 41% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/GatewayController.java index 2bc99af..143cd59 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayController.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/GatewayController.java @@ -1,54 +1,22 @@ -package cn.seqdata.gateway; - -import java.util.Collection; -import java.util.Collections; -import java.util.Set; -import java.util.stream.Collectors; +package cn.seqdata.gateway.controller; import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; -import cn.seqdata.gateway.filter.authc.AllowlistPredicate; -import cn.seqdata.oauth2.client.DefaultRole; import cn.seqdata.oauth2.client.FeignAuthzClient; -import cn.seqdata.oauth2.client.entity.AuthorityPermission; -import cn.seqdata.oauth2.client.entity.IdentifiableObject; -import cn.seqdata.oauth2.client.entity.NamedObject; /** * @author jrxian * @date 2020-11-11 23:03 */ @RestController +@RequestMapping("/cache") @AllArgsConstructor public class GatewayController { private final FeignAuthzClient authzClient; - private final AllowlistPredicate allowlistPredicate; private final CachedResourceServerTokenServices tokenServices; - @GetMapping("/allowlist") - public Collection getAllowlist() { - return allowlistPredicate.allowlist; - } - - @PostMapping("/allowlist") - public void setAllowlist(Collection allowlist) { - allowlistPredicate.allowlist.clear(); - allowlistPredicate.allowlist.addAll(allowlist); - } - - @GetMapping("/anonymous") - public Collection getAnonymous() { - return allowlistPredicate.anonymous; - } - - @PostMapping("/anonymous") - public void refreshAnonymous() { - allowlistPredicate.anonymous.clear(); - allowlistPredicate.anonymous.addAll(anonymousUrls()); - } - @DeleteMapping("/authentication/{key}") public void invalidateAuthentication(@PathVariable("key") String key) { tokenServices.authenticationCache.invalidate(key); @@ -68,16 +36,4 @@ public class GatewayController { public void invalidateTokens() { tokenServices.accessTokenCache.invalidateAll(); } - - private Set anonymousUrls() { - return authzClient.findAuthorityByIdentifier(DefaultRole.anonymous.name()) - .map(NamedObject::getId) - .map(x -> authzClient.findPermissions(x, "url") - .stream() - .map(AuthorityPermission::getPermission) - .map(IdentifiableObject::getIdentifier) - .collect(Collectors.toSet()) - ) - .orElse(Collections.emptySet()); - } } -- Gitee From 91549f2fbf2253aa38af0f8697147e27222a9a6c Mon Sep 17 00:00:00 2001 From: zl1995 Date: Tue, 17 Nov 2020 19:27:00 +0800 Subject: [PATCH 100/228] =?UTF-8?q?docker=E6=89=93=E5=8C=85=EF=BC=9A?= =?UTF-8?q?=E6=9C=AC=E6=9C=BA=E6=89=93=E5=8C=85=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 +++--- seqdata-cloud-authz/src/main/resources/bootstrap.yml | 4 ++-- seqdata-cloud-gateway/src/main/resources/bootstrap.yml | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 8a43d84..b59e48b 100644 --- a/pom.xml +++ b/pom.xml @@ -93,9 +93,9 @@ - ${project.artifactId} - ${project.basedir}/src/main/docker - http://dev.voltmao.com:14775 + ${artifactId} + http://192.168.0.31:2375 + src/main/docker / diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index c2b6e4f..03ef736 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -9,8 +9,8 @@ spring: nacos: discovery: server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} + group: ${NACOS_SERVICE_DISCOVERY:devDiscovery} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} file-extension: ${CONFIG_FORMAT:yml} -server: - port: 0 \ No newline at end of file + group: ${NACOS_SERVICE_CONFIG:devConfig} diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index ad4e0c6..f1ecf50 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -9,6 +9,8 @@ spring: nacos: discovery: server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} + group: ${NACOS_SERVICE_DISCOVERY:devDiscovery} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: ${CONFIG_FORMAT:yml} \ No newline at end of file + file-extension: ${CONFIG_FORMAT:yml} + group: ${NACOS_SERVICE_CONFIG:devConfig} \ No newline at end of file -- Gitee From 20f44b6dbda2fb46e322e46329efef4acbb278d0 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 20 Nov 2020 15:08:28 +0800 Subject: [PATCH 101/228] =?UTF-8?q?dockerfile=E6=B7=BB=E5=8A=A0=E6=97=B6?= =?UTF-8?q?=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authc/src/main/docker/Dockerfile | 2 ++ seqdata-cloud-authz/src/main/docker/Dockerfile | 2 ++ seqdata-cloud-gateway/src/main/docker/Dockerfile | 2 ++ 3 files changed, 6 insertions(+) diff --git a/seqdata-cloud-authc/src/main/docker/Dockerfile b/seqdata-cloud-authc/src/main/docker/Dockerfile index 09c1a30..e8e9948 100644 --- a/seqdata-cloud-authc/src/main/docker/Dockerfile +++ b/seqdata-cloud-authc/src/main/docker/Dockerfile @@ -1,4 +1,6 @@ FROM openjdk:8-jdk-alpine VOLUME /tmp ADD seqdata-cloud-authc-2.2.1-SNAPSHOT.jar myapp.jar +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/docker/Dockerfile b/seqdata-cloud-authz/src/main/docker/Dockerfile index 793d223..a46d314 100644 --- a/seqdata-cloud-authz/src/main/docker/Dockerfile +++ b/seqdata-cloud-authz/src/main/docker/Dockerfile @@ -1,4 +1,6 @@ FROM openjdk:8-jdk-alpine VOLUME /tmp ADD seqdata-cloud-authz-2.2.1-SNAPSHOT.jar myapp.jar +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/docker/Dockerfile b/seqdata-cloud-gateway/src/main/docker/Dockerfile index 4c23386..5895a5e 100644 --- a/seqdata-cloud-gateway/src/main/docker/Dockerfile +++ b/seqdata-cloud-gateway/src/main/docker/Dockerfile @@ -1,4 +1,6 @@ FROM openjdk:8-jdk-alpine VOLUME /tmp ADD seqdata-cloud-gateway-2.2.1-SNAPSHOT.jar myapp.jar +ENV TZ=Asia/Shanghai +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file -- Gitee From 5c9ae23eb871f0da47916aa618534b497dba93da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 20 Nov 2020 20:56:32 +0800 Subject: [PATCH 102/228] =?UTF-8?q?=E5=8D=95=E7=8B=AC=E5=89=A5=E7=A6=BB?= =?UTF-8?q?=E5=87=BAseqdata-cloud-starter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 15 +- seqdata-cloud-authc/pom.xml | 68 ++---- .../oauth2/AuthcServerApplication.java | 4 - .../oauth2/MethodSecurityConfiguration.java | 78 ------ .../oauth2/ResourceServerConfiguration.java | 77 ------ .../seqdata/oauth2/SwaggerConfiguration.java | 75 ------ .../oauth2/controller/UserInfoController.java | 1 + .../src/main/resources/application.yml | 8 + .../src/main/resources/bootstrap.yml | 12 - seqdata-cloud-authz/pom.xml | 140 +++++------ .../oauth2/AuthcServerConfiguration.java | 4 +- .../oauth2/AuthzServerApplication.java | 4 +- .../oauth2/AuthzServerConfiguration.java | 13 +- .../oauth2/WebSecurityConfiguration.java | 15 +- .../oauth2/jackson/AuthenticationBean.java | 43 ++++ .../oauth2/jackson/AuthorityConverter.java | 29 +++ .../JsonSerializationStrategy.java | 43 +--- .../jackson/OAuth2AuthenticationBean.java | 29 +++ .../jackson/OAuth2AuthenticationMixIn.java | 17 ++ .../seqdata/oauth2/jackson/OAuth2Module.java | 33 +++ .../oauth2/jackson/OAuth2RequestBean.java | 57 +++++ .../oauth2/jackson/OAuth2RequestMixIn.java | 31 +++ .../oauth2/jackson/RedisTokenStore.java | 226 ++++++++++++++++++ .../jackson/SimpleGrantedAuthorityMixIn.java | 14 ++ .../cn/seqdata/oauth2/jackson/UserBean.java | 53 ++++ .../cn/seqdata/oauth2/jackson/UserMixIn.java | 24 ++ ...rnamePasswordAuthenticationTokenMinIn.java | 19 ++ .../src/main/resources/application.yml | 8 +- .../src/main/resources/bootstrap.yml | 12 +- .../src/main/resources/application.yml | 6 +- .../src/main/resources/bootstrap.yml | 10 +- 31 files changed, 714 insertions(+), 454 deletions(-) delete mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java delete mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java delete mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java delete mode 100644 seqdata-cloud-authc/src/main/resources/bootstrap.yml create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthenticationBean.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{ => jackson}/JsonSerializationStrategy.java (32%) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationBean.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationMixIn.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestBean.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestMixIn.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/RedisTokenStore.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/SimpleGrantedAuthorityMixIn.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserMixIn.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UsernamePasswordAuthenticationTokenMinIn.java diff --git a/pom.xml b/pom.xml index b59e48b..74188ce 100644 --- a/pom.xml +++ b/pom.xml @@ -26,21 +26,11 @@ org.projectlombok lombok - compile true - - org.apache.commons - commons-lang3 - 3.9 - - - com.google.guava - guava - 21.0 - + com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config @@ -52,6 +42,7 @@ ${nacos.version} + io.springfox springfox-swagger2 @@ -94,7 +85,7 @@ ${artifactId} - http://192.168.0.31:2375 + http://docker.seqdata.cn:2375 src/main/docker diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml index cb628a9..a16199b 100644 --- a/seqdata-cloud-authc/pom.xml +++ b/seqdata-cloud-authc/pom.xml @@ -1,48 +1,26 @@ - 4.0.0 - - cn.seqdata.cloud - seqdata-cloud-parent - 2.2.1-SNAPSHOT - - seqdata-cloud-authc - 资源服务器,所有的业务模块应该继承自资源服务器,自动和授权服务器交互实现认证和授权 - - - joda-time - joda-time - - - org.springframework.data - spring-data-commons - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-security - - - - org.springframework.security - spring-security-oauth2-resource-server - - - org.springframework.security - spring-security-oauth2-client - - - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - - - cn.seqdata.cloud - seqdata-cloud-auth-client - ${project.version} - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.1.RELEASE + + seqdata-cloud-authc + + + joda-time + joda-time + + + org.springframework.data + spring-data-jpa + + + cn.seqdata.cloud + seqdata-cloud-starter + 2.2.1-SNAPSHOT + + diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java index d974743..29cd75a 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java @@ -3,9 +3,6 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.cloud.openfeign.EnableFeignClients; - -import cn.seqdata.oauth2.client.FeignAuthzClient; /** * Author: jrxian @@ -13,7 +10,6 @@ import cn.seqdata.oauth2.client.FeignAuthzClient; */ @SpringBootApplication @EnableDiscoveryClient -@EnableFeignClients(basePackageClasses = FeignAuthzClient.class) public class AuthcServerApplication { public static void main(String[] args) { diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java deleted file mode 100644 index 136dfc1..0000000 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/MethodSecurityConfiguration.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.seqdata.oauth2; - -import java.util.List; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.AccessDecisionManager; -import org.springframework.security.access.AccessDecisionVoter; -import org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource; -import org.springframework.security.access.method.MethodSecurityMetadataSource; -import org.springframework.security.access.vote.AbstractAccessDecisionManager; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; - -import cn.seqdata.oauth2.client.FeignAuthzClient; -import cn.seqdata.oauth2.client.security.*; - -/** - * Author: jrxian - * Date: 2020-02-15 23:50 - * 增加jsr250Enabled支持,如果不需要检查某个RestController的权限,在类或方法上标记@PermitAll注解,但此时必须是登录用户 - * 如果要连登录都不需要,需要在额外配置http.authorizeRequests().antMatchers("/prefix/**").permitAll(); - * 即使在url层面放过,仍然需要在RestController标记@PermitAll注解,否则仍然会被后续拦截器拦截 - */ -@Configuration -@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true) -public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration { - - @Bean - public RowsPermissionEvaluator rowsPermissionEvaluator(List evaluators) { - return new RowsPermissionEvaluator(evaluators); - } - - @Bean - public PathPermissionEvaluator pathPermissionEvaluator(FeignAuthzClient authzClient) { - return new PathPermissionEvaluatorImpl("repo", authzClient); - } - - @Bean - public ControllerMethodVoter controllerMethodVoter(PathPermissionEvaluator permissionEvaluator) { - return new ControllerMethodVoter(permissionEvaluator); - } - - @Autowired - private ControllerMethodVoter controllerMethodVoter; - - @Override - protected AccessDecisionManager accessDecisionManager() { - AccessDecisionManager accessDecisionManager = super.accessDecisionManager(); - if(accessDecisionManager instanceof AbstractAccessDecisionManager) { - List> voters = ((AbstractAccessDecisionManager) accessDecisionManager).getDecisionVoters(); - - //增加@RestController带有@RequestMapping注解的Method的拦截器 - voters.add(controllerMethodVoter); - } - return accessDecisionManager; - } - - @Override - protected MethodSecurityMetadataSource customMethodSecurityMetadataSource() { - return new ControllerMethodSecurityMetadataSource(); - } - - @Bean - @Override - public MethodSecurityMetadataSource methodSecurityMetadataSource() { - MethodSecurityMetadataSource source = super.methodSecurityMetadataSource(); - - if(source instanceof DelegatingMethodSecurityMetadataSource) { - //系统默认的不支持多级注解,为使ControllerMethodVoter生效,用cn.seqdata下的同名代替 - DelegatingMethodSecurityMetadataSource delegating = (DelegatingMethodSecurityMetadataSource) source; - return new cn.seqdata.oauth2.client.security.DelegatingMethodSecurityMetadataSource(delegating.getMethodSecurityMetadataSources()); - } else { - return source; - } - } -} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java deleted file mode 100644 index 279123b..0000000 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/ResourceServerConfiguration.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.seqdata.oauth2; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpStatus; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; -import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; -import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; -import org.springframework.security.oauth2.provider.token.RemoteTokenServices; -import org.springframework.security.web.authentication.HttpStatusEntryPoint; -import org.springframework.web.client.RestTemplate; - -import cn.seqdata.oauth2.client.DefaultRole; -import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; - -/** - * Author: jrxian - * Date: 2019-09-29 23:09 - */ -@Configuration -@EnableResourceServer -@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) -public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { - - @Bean - @LoadBalanced - public RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Bean - public RemoteTokenServices remoteTokenServices(RestTemplate restTemplate, - OAuth2ClientProperties credentials, ResourceServerProperties resource) { - EnhanceUserAuthenticationConverter authenticationConverter = new EnhanceUserAuthenticationConverter(); - authenticationConverter.setDefaultAuthorities(new String[]{DefaultRole.anonymous.name()}); - - DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter(); - tokenConverter.setUserTokenConverter(authenticationConverter); - - RemoteTokenServices services = new RemoteTokenServices(); - services.setRestTemplate(restTemplate); - services.setAccessTokenConverter(tokenConverter); - - services.setClientId(credentials.getClientId()); - services.setClientSecret(credentials.getClientSecret()); - - services.setCheckTokenEndpointUrl(resource.getTokenInfoUri()); - - return services; - } - - @Override - public void configure(HttpSecurity http) throws Exception { - http.csrf() - .disable(); - - http.httpBasic(); - - http.authorizeRequests() - .antMatchers("/v2/api-docs/**", "/swagger-resources/**" - , "/public/**", "/anonymous/**", "/joda/**") - .permitAll(); - - http.authorizeRequests() - .anyRequest() - .authenticated(); - - http.exceptionHandling() - .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); - } -} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java deleted file mode 100644 index a72e92b..0000000 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.seqdata.oauth2; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import lombok.AllArgsConstructor; -import springfox.documentation.builders.ParameterBuilder; -import springfox.documentation.schema.ModelRef; -import springfox.documentation.service.*; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -/** - * Author: jrxian - * Date: 2020-02-04 01:36 - */ -@Configuration -@EnableSwagger2 -@AllArgsConstructor -public class SwaggerConfiguration { - private final ResourceServerProperties resourceServerProperties; - - @Bean - public Docket apiDocker() { - return new Docket(DocumentationType.SWAGGER_2) -// .securitySchemes(Collections.singletonList(password())) -// .globalOperationParameters(operationParameters()) - ; - } - - private SecurityScheme password() { - return new OAuth("password", scopes(), grantTypes()); - } - - private List scopes() { - List scopes = new ArrayList<>(); - - scopes.add(new AuthorizationScope("dat", "数据")); - scopes.add(new AuthorizationScope("eff", "能效")); - scopes.add(new AuthorizationScope("ops", "运维")); - scopes.add(new AuthorizationScope("trd", "售电")); - scopes.add(new AuthorizationScope("sys", "后台")); - - return scopes; - } - - private List grantTypes() { - List grantTypes = new ArrayList<>(); - - String tokenInfoUri = resourceServerProperties.getTokenInfoUri(); - String tokenUrl = StringUtils.replace(tokenInfoUri, "/check_token", "/token"); - grantTypes.add(new ResourceOwnerPasswordCredentialsGrant(tokenUrl)); - - return grantTypes; - } - - private List operationParameters() { - List operationParameters = new ArrayList<>(); - - operationParameters.add(new ParameterBuilder() - .name("Authorization") - .required(false) - .parameterType("header") - .modelRef(new ModelRef("string")) - .defaultValue("Bearer 00000000-0000-0000-0000-000000000000") - .build()); - - return operationParameters; - } -} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java index 6b83ea8..c9e5c26 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/UserInfoController.java @@ -18,6 +18,7 @@ public class UserInfoController { @ApiOperation("通行证") @GetMapping("/principal") + @PreAuthorize("isAuthenticated()") public Principal principal(Principal principal) { return principal; } diff --git a/seqdata-cloud-authc/src/main/resources/application.yml b/seqdata-cloud-authc/src/main/resources/application.yml index 2b78727..b8132ab 100644 --- a/seqdata-cloud-authc/src/main/resources/application.yml +++ b/seqdata-cloud-authc/src/main/resources/application.yml @@ -2,9 +2,17 @@ logging: level: root: info cn.seqdata: trace + org.springframework.security: debug spring: + application: + name: authc profiles: active: jackson + security: + user: + name: authc + password: authc@123 + roles: anonymous security: oauth2: resource: diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml deleted file mode 100644 index 677e710..0000000 --- a/seqdata-cloud-authc/src/main/resources/bootstrap.yml +++ /dev/null @@ -1,12 +0,0 @@ -spring: - application: - name: authc - cloud: - nacos: - discovery: - server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} - config: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: ${CONFIG_FORMAT:yml} -server: - port: 0 \ No newline at end of file diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 43f3fc7..34df85b 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -1,77 +1,77 @@ - 4.0.0 - - cn.seqdata.cloud - seqdata-cloud-parent - 2.2.1-SNAPSHOT - - seqdata-cloud-authz - 认证服务器,提供oauth2认证,接入第三方认证(如微信、钉钉) - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jdbc - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-data-redis - - - org.springframework.boot - spring-boot-configuration-processor - true - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-authz + 认证服务器,提供oauth2认证,接入第三方认证(如微信、钉钉) + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + - - org.springframework.security - spring-security-oauth2-client - - - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + - - com.aliyun - aliyun-java-sdk-core - 4.5.2 - - - com.aliyun - aliyun-java-sdk-dysmsapi - 2.1.0 - + + com.aliyun + aliyun-java-sdk-core + 4.5.2 + + + com.aliyun + aliyun-java-sdk-dysmsapi + 2.1.0 + - - cn.seqdata.cloud - seqdata-cloud-wechat - ${project.version} - - - cn.seqdata.cloud - seqdata-cloud-oauth2 - ${project.version} - + + cn.seqdata.cloud + seqdata-cloud-wechat + ${project.version} + + + cn.seqdata.cloud + seqdata-cloud-oauth2 + ${project.version} + - - com.microsoft.sqlserver - mssql-jdbc - runtime - - - mysql - mysql-connector-java - runtime - - + + com.microsoft.sqlserver + mssql-jdbc + runtime + + + mysql + mysql-connector-java + runtime + + diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 110aea6..5746326 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -20,9 +20,7 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.cors(); http.authorizeRequests() - .antMatchers( - "/v2/api-docs/**", "/swagger-resources/**" - , "/oauth2/**", "/rbac/**", "/perm/**" + .antMatchers("/oauth2/**", "/rbac/**", "/perm/**" , "/connect/**", "/oauth/code/**", "/wxapp/**") .permitAll(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index 6e8a9e6..926ec3f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -13,9 +13,9 @@ import cn.seqdata.wxapp.WxAppFeignClient; * Date: 2019-10-02 19:11 */ @SpringBootApplication -@EnableDiscoveryClient -@EnableFeignClients(basePackageClasses = WxAppFeignClient.class) @EnableSwagger2 +@EnableDiscoveryClient +@EnableFeignClients(clients = WxAppFeignClient.class) public class AuthzServerApplication { public static void main(String[] args) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index b520c7c..76823a7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -3,11 +3,12 @@ package cn.seqdata.oauth2; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.UUID; +import lombok.AllArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.factory.PasswordEncoderFactories; @@ -31,8 +32,7 @@ import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswo import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; -import lombok.AllArgsConstructor; +import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; import cn.seqdata.oauth2.mobile.MobileNonceHandler; @@ -52,9 +52,11 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt private final AuthenticationManager authenticationManager; private final JpaUserDetailsManager userDetailsService; private final RedisConnectionFactory redisConnectionFactory; + private final StringRedisTemplate redisTemplate; private final ClientDetailRepo clientDetailRepo; private final MobileNonceHandler mobileNonceHandler; private final DefaultTokenEnhancer tokenEnhancer; + private final ObjectMapper objectMapper; @Bean public PasswordEncoder passwordEncoder() { @@ -68,9 +70,8 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt @Bean public TokenStore tokenStore() { - RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory); - redisTokenStore.setAuthenticationKeyGenerator(x -> String.valueOf(UUID.randomUUID())); - return redisTokenStore; + return new cn.seqdata.oauth2.jackson.RedisTokenStore(redisTemplate); +// return new RedisTokenStore(redisConnectionFactory); } @Override diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java index 37627a2..b058ee4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -3,7 +3,6 @@ package cn.seqdata.oauth2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -17,19 +16,9 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override - public void configure(WebSecurity web) throws Exception { - super.configure(web); - + public void configure(WebSecurity web) { web.ignoring() - .antMatchers("/favicon.ico" - , "/resources/**", "/css/**", "/js/**" - , "/swagger-ui.html", "/webjars/**", "/plugins/**" - ); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - super.configure(http); + .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/v2/api-docs/**", "/webjars/**", "/plugins/**"); } @Bean diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthenticationBean.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthenticationBean.java new file mode 100644 index 0000000..588672a --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthenticationBean.java @@ -0,0 +1,43 @@ +package cn.seqdata.oauth2.jackson; + +import java.util.*; +import java.util.function.Consumer; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * @author jrxian + * @date 2020-11-15 17:48 + */ +@lombok.Getter +@lombok.Setter +@lombok.NoArgsConstructor +public class AuthenticationBean implements Authentication, Consumer { + private String name; + private final UserBean principal = new UserBean(); + private Object credentials; + private final Set roles = new HashSet<>(); + private Map details = new HashMap<>(); + private boolean authenticated; + + @JsonIgnore + @Override + public Collection getAuthorities() { + return AuthorityConverter.toAuthorities(roles); + } + + @Override + public void accept(Authentication authentication) { + this.name = authentication.getName(); + this.principal.accept(authentication.getPrincipal()); + this.credentials = authentication.getCredentials(); + this.roles.addAll(AuthorityConverter.toRoles(authentication.getAuthorities())); + Object details = authentication.getDetails(); + if(details instanceof Map) { + ((Map) details).forEach((k, v) -> this.details.put(String.valueOf(k), String.valueOf(v))); + } + this.authenticated = authentication.isAuthenticated(); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java new file mode 100644 index 0000000..ead9182 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java @@ -0,0 +1,29 @@ +package cn.seqdata.oauth2.jackson; + +import java.util.Collection; +import java.util.stream.Collectors; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +/** + * @author jrxian + * @date 2020-11-15 17:56 + */ +public final class AuthorityConverter { + private AuthorityConverter() { + } + + public static Collection toAuthorities(Collection roles) { + return roles.stream() + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + } + + public static Collection toRoles(Collection authorities) { + return authorities.stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.toList()); + } + +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/JsonSerializationStrategy.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/JsonSerializationStrategy.java similarity index 32% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/JsonSerializationStrategy.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/JsonSerializationStrategy.java index f33804d..3d1557c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/JsonSerializationStrategy.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/JsonSerializationStrategy.java @@ -1,18 +1,8 @@ -package cn.seqdata.oauth2; +package cn.seqdata.oauth2.jackson; import java.io.IOException; -import java.io.Serializable; -import java.util.Collection; -import java.util.Map; -import java.util.Set; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.token.store.redis.StandardStringSerializationStrategy; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; @@ -21,13 +11,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; * Date: 2020-02-15 13:37 */ public class JsonSerializationStrategy extends StandardStringSerializationStrategy { - private final ObjectMapper objectMapper; + private final ObjectMapper objectMapper = new ObjectMapper(); - public JsonSerializationStrategy(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; + public JsonSerializationStrategy() { objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - objectMapper.addMixIn(OAuth2Authentication.class, JsonSerializationStrategy.OAuth2AuthenticationMixIn.class); - objectMapper.addMixIn(OAuth2Request.class, JsonSerializationStrategy.OAuth2RequestMixIn.class); + objectMapper.registerModule(new OAuth2Module()); } @Override @@ -47,27 +35,4 @@ public class JsonSerializationStrategy extends StandardStringSerializationStrate throw new RuntimeException(ex); } } - - //由于OAuth2Authentication只提供了final参数,jackson无法直接反序列化 - private static class OAuth2AuthenticationMixIn { - @JsonCreator - public OAuth2AuthenticationMixIn(@JsonProperty("oauth2Request") OAuth2Request storedRequest, - @JsonProperty("userAuthentication") Authentication userAuthentication) { - } - } - - private static class OAuth2RequestMixIn { - @JsonCreator - public OAuth2RequestMixIn( - @JsonProperty("requestParameters") Map requestParameters, - @JsonProperty("clientId") String clientId, - @JsonProperty("authorities") Collection authorities, - @JsonProperty("approved") boolean approved, - @JsonProperty("scope") Set scope, - @JsonProperty("resourceIds") Set resourceIds, - @JsonProperty("redirectUri") String redirectUri, - @JsonProperty("responseTypes") Set responseTypes, - @JsonProperty("extensions") Map extensionProperties) { - } - } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationBean.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationBean.java new file mode 100644 index 0000000..2d57ab9 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationBean.java @@ -0,0 +1,29 @@ +package cn.seqdata.oauth2.jackson; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.springframework.security.oauth2.provider.OAuth2Authentication; + +/** + * @author jrxian + * @date 2020-11-15 18:04 + */ +@lombok.Getter +@lombok.Setter +@lombok.NoArgsConstructor +public class OAuth2AuthenticationBean implements Consumer, Supplier { + private final OAuth2RequestBean storedRequest = new OAuth2RequestBean(); + private final AuthenticationBean userAuthentication = new AuthenticationBean(); + + @Override + public void accept(OAuth2Authentication wrapper) { + storedRequest.accept(wrapper.getOAuth2Request()); + userAuthentication.accept(wrapper.getUserAuthentication()); + } + + @Override + public OAuth2Authentication get() { + return new OAuth2Authentication(storedRequest.get(), userAuthentication); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationMixIn.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationMixIn.java new file mode 100644 index 0000000..66818be --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2AuthenticationMixIn.java @@ -0,0 +1,17 @@ +package cn.seqdata.oauth2.jackson; + +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author jrxian + * @date 2020-11-15 00:21 + */ +public class OAuth2AuthenticationMixIn { + @JsonCreator + public OAuth2AuthenticationMixIn(@JsonProperty("oauth2Request") OAuth2Request storedRequest, + @JsonProperty("userAuthentication") Authentication userAuthentication) { + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java new file mode 100644 index 0000000..dbdb744 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java @@ -0,0 +1,33 @@ +package cn.seqdata.oauth2.jackson; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.module.SimpleModule; + +/** + * @author jrxian + * @date 2020-11-15 00:16 + */ +public class OAuth2Module extends SimpleModule { + + public OAuth2Module() { + super(OAuth2Module.class.getSimpleName(), Version.unknownVersion()); + + setMixInAnnotation(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixIn.class); + setMixInAnnotation(User.class, UserMixIn.class); + setMixInAnnotation(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMinIn.class); + setMixInAnnotation(OAuth2Request.class, OAuth2RequestMixIn.class); + setMixInAnnotation(OAuth2Authentication.class, OAuth2AuthenticationMixIn.class); + + addAbstractTypeMapping(GrantedAuthority.class, SimpleGrantedAuthority.class); + addAbstractTypeMapping(UserDetails.class, UserBean.class); + addAbstractTypeMapping(Authentication.class, UsernamePasswordAuthenticationToken.class); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestBean.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestBean.java new file mode 100644 index 0000000..4022c75 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestBean.java @@ -0,0 +1,57 @@ +package cn.seqdata.oauth2.jackson; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.springframework.security.oauth2.provider.OAuth2Request; + +/** + * @author jrxian + * @date 2020-11-15 17:38 + */ +@lombok.Getter +@lombok.Setter +@lombok.NoArgsConstructor +public class OAuth2RequestBean implements Consumer, Supplier { + private String clientId; + private final Set scope = new HashSet<>(); + private final Map requestParameters = new HashMap<>(); + private final Set resourceIds = new HashSet<>(); + private final Set roles = new HashSet<>(); + private boolean approved; + private String redirectUri; + private final Set responseTypes = new HashSet<>(); + private final Map extensions = new HashMap<>(); + + @Override + public void accept(OAuth2Request wrapper) { + this.clientId = wrapper.getClientId(); + this.scope.addAll(wrapper.getScope()); + this.requestParameters.putAll(wrapper.getRequestParameters()); + this.resourceIds.addAll(wrapper.getResourceIds()); + this.roles.addAll(AuthorityConverter.toRoles(wrapper.getAuthorities())); + this.approved = wrapper.isApproved(); + this.redirectUri = wrapper.getRedirectUri(); + this.responseTypes.addAll(wrapper.getResponseTypes()); + this.extensions.putAll(wrapper.getExtensions()); + } + + @Override + public OAuth2Request get() { + return new OAuth2Request(requestParameters, + clientId, + AuthorityConverter.toAuthorities(roles), + approved, + scope, + resourceIds, + redirectUri, + responseTypes, + extensions + ); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestMixIn.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestMixIn.java new file mode 100644 index 0000000..7557a14 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2RequestMixIn.java @@ -0,0 +1,31 @@ +package cn.seqdata.oauth2.jackson; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.springframework.security.core.GrantedAuthority; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author jrxian + * @date 2020-11-15 00:22 + */ +@JsonIgnoreProperties("refresh") +public class OAuth2RequestMixIn { + @JsonCreator + public OAuth2RequestMixIn( + @JsonProperty("requestParameters") Map requestParameters, + @JsonProperty("clientId") String clientId, + @JsonProperty("authorities") Collection authorities, + @JsonProperty("approved") boolean approved, + @JsonProperty("scope") Set scope, + @JsonProperty("resourceIds") Set resourceIds, + @JsonProperty("redirectUri") String redirectUri, + @JsonProperty("responseTypes") Set responseTypes, + @JsonProperty("extensions") Map extensionProperties) { + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/RedisTokenStore.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/RedisTokenStore.java new file mode 100644 index 0000000..fa7ceda --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/RedisTokenStore.java @@ -0,0 +1,226 @@ +package cn.seqdata.oauth2.jackson; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.SetOperations; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; +import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2RefreshToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; +import org.springframework.security.oauth2.provider.token.TokenStore; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.oauth2.sdk.util.CollectionUtils; + +/** + * @author jrxian + * @date 2020-11-15 15:48 + */ +public class RedisTokenStore implements TokenStore { + private static final String ACCESS = "access:"; + private static final String AUTH_TO_ACCESS = "auth_to_access:"; + private static final String AUTH = "auth:"; + private static final String REFRESH_AUTH = "refresh_auth:"; + private static final String ACCESS_TO_REFRESH = "access_to_refresh:"; + private static final String REFRESH = "refresh:"; + private static final String REFRESH_TO_ACCESS = "refresh_to_access:"; + private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:"; + private static final String UNAME_TO_ACCESS = "uname_to_access:"; + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final StringRedisTemplate redisTemplate; + private final ValueOperations opsForValue; + private final SetOperations opsForSet; + private AuthenticationKeyGenerator keyGenerator = x -> String.valueOf(UUID.randomUUID()); + + public RedisTokenStore(StringRedisTemplate redisTemplate) { + this.objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + this.objectMapper.registerModule(new OAuth2Module()); + + this.redisTemplate = redisTemplate; + this.opsForValue = redisTemplate.opsForValue(); + this.opsForSet = redisTemplate.opsForSet(); + } + + public AuthenticationKeyGenerator getKeyGenerator() { + return keyGenerator; + } + + public void setKeyGenerator(AuthenticationKeyGenerator keyGenerator) { + this.keyGenerator = keyGenerator; + } + + @Override + public OAuth2Authentication readAuthentication(String token) { + return fromJSON(opsForValue.get(AUTH + token)); + } + + @Override + public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { + return readAuthentication(token.getValue()); + } + + @Override + public OAuth2AccessToken readAccessToken(String tokenValue) { + return fromJSON(opsForValue.get(ACCESS + tokenValue), OAuth2AccessToken.class); + } + + @Override + public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { + String key = keyGenerator.extractKey(authentication); + String tokenValue = opsForValue.get(AUTH_TO_ACCESS + key); + return readAccessToken(tokenValue); + } + + @Override + public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { + String key = keyGenerator.extractKey(authentication); + OAuth2Request auth2Request = authentication.getOAuth2Request(); + String tokenValue = token.getValue(); + int expires = token.getExpiresIn(); + opsForValue.set(AUTH + tokenValue, toJSON(authentication), expires, TimeUnit.SECONDS); + opsForValue.set(ACCESS + tokenValue, toJSON(token), expires, TimeUnit.SECONDS); + opsForValue.set(AUTH_TO_ACCESS + key, tokenValue, expires, TimeUnit.SECONDS); + opsForSet.add(CLIENT_ID_TO_ACCESS + auth2Request.getClientId(), tokenValue); + if(!authentication.isClientOnly()) { + opsForSet.add(UNAME_TO_ACCESS + getApprovalKey(authentication), tokenValue); + } + + OAuth2RefreshToken refreshToken = token.getRefreshToken(); + if(Objects.nonNull(refreshToken) && Objects.nonNull(refreshToken.getValue())) { + String refreshToAccessKey = REFRESH_TO_ACCESS + refreshToken.getValue(); + String accessToRefreshKey = ACCESS_TO_REFRESH + tokenValue; + + opsForValue.set(refreshToAccessKey, tokenValue); + opsForValue.set(accessToRefreshKey, refreshToken.getValue()); + if(refreshToken instanceof ExpiringOAuth2RefreshToken) { + Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration(); + redisTemplate.expireAt(refreshToAccessKey, expiration); + redisTemplate.expireAt(accessToRefreshKey, expiration); + } + } + } + + @Override + public void removeAccessToken(OAuth2AccessToken token) { + redisTemplate.delete(ACCESS + token.getValue()); + } + + @Override + public OAuth2RefreshToken readRefreshToken(String tokenValue) { + return fromJSON(opsForValue.get(REFRESH + tokenValue), DefaultOAuth2RefreshToken.class); + } + + @Override + public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { + String refreshKey = REFRESH + refreshToken.getValue(); + String refreshAuthKey = REFRESH_AUTH + refreshToken.getValue(); + + opsForValue.set(refreshKey, toJSON(refreshToken)); + opsForValue.set(refreshAuthKey, toJSON(authentication)); + if(refreshToken instanceof ExpiringOAuth2RefreshToken) { + Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration(); + redisTemplate.expireAt(refreshKey, expiration); + redisTemplate.expireAt(refreshAuthKey, expiration); + } + } + + @Override + public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) { + return fromJSON(opsForValue.get(REFRESH_AUTH + token)); + } + + @Override + public void removeRefreshToken(OAuth2RefreshToken token) { + redisTemplate.delete(REFRESH + token.getValue()); + } + + @Override + public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { + String refreshToAccessKey = REFRESH_TO_ACCESS + refreshToken.getValue(); + String tokenValue = opsForValue.get(refreshToAccessKey); + redisTemplate.delete(refreshToAccessKey); + redisTemplate.delete(ACCESS + tokenValue); + } + + @Override + public Collection findTokensByClientId(String clientId) { + return findTokens(CLIENT_ID_TO_ACCESS + clientId); + } + + @Override + public Collection findTokensByClientIdAndUserName(String clientId, String userName) { + return findTokens(UNAME_TO_ACCESS + getApprovalKey(clientId, userName)); + } + + private Collection findTokens(String key) { + List accessTokens = new LinkedList<>(); + + BoundSetOperations boundSetOps = redisTemplate.boundSetOps(key); + Set members = boundSetOps.members(); + if(CollectionUtils.isNotEmpty(members)) { + members.forEach(member -> { + OAuth2AccessToken accessToken = readAccessToken(member); + if(Objects.nonNull(accessToken)) { + accessTokens.add(accessToken); + } else { + boundSetOps.remove(member); + } + }); + } + + return accessTokens; + } + + private T fromJSON(String json, Class clazz) { + if(Objects.isNull(json)) return null; + + try { + return objectMapper.readValue(json, clazz); + } catch(JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + + private String toJSON(T object) { + try { + return objectMapper.writeValueAsString(object); + } catch(JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + + /** + * 由于多态和构造参数的原因,无法直接在OAuth2Authentication和json之间互相转换 + */ + private OAuth2Authentication fromJSON(String json) { + OAuth2AuthenticationBean bean = fromJSON(json, OAuth2AuthenticationBean.class); + return Objects.nonNull(bean) ? bean.get() : null; + } + + private String toJSON(OAuth2Authentication object) { + OAuth2AuthenticationBean bean = new OAuth2AuthenticationBean(); + bean.accept(object); + return toJSON(bean); + } + + private static String getApprovalKey(OAuth2Authentication authentication) { + OAuth2Request request = authentication.getOAuth2Request(); + Authentication userAuthentication = authentication.getUserAuthentication(); + String userName = userAuthentication == null ? "" : userAuthentication.getName(); + return getApprovalKey(request.getClientId(), userName); + } + + private static String getApprovalKey(String clientId, String userName) { + return clientId + (userName == null ? "" : ":" + userName); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/SimpleGrantedAuthorityMixIn.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/SimpleGrantedAuthorityMixIn.java new file mode 100644 index 0000000..4ec829f --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/SimpleGrantedAuthorityMixIn.java @@ -0,0 +1,14 @@ +package cn.seqdata.oauth2.jackson; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author jrxian + * @date 2020-11-15 11:06 + */ +public class SimpleGrantedAuthorityMixIn { + @JsonCreator + public SimpleGrantedAuthorityMixIn(@JsonProperty("authority") String authority) { + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java new file mode 100644 index 0000000..12b274d --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java @@ -0,0 +1,53 @@ +package cn.seqdata.oauth2.jackson; + +import java.security.Principal; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; + +import org.springframework.security.core.AuthenticatedPrincipal; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * @author jrxian + * @date 2020-11-15 23:42 + */ +@lombok.Getter +@lombok.Setter +@lombok.NoArgsConstructor +public class UserBean implements UserDetails, Consumer { + private String username; + private String password; + private boolean enabled; + private boolean accountNonExpired; + private boolean credentialsNonExpired; + private boolean accountNonLocked; + private final Set roles = new HashSet<>(); + + @JsonIgnore + @Override + public Collection getAuthorities() { + return AuthorityConverter.toAuthorities(roles); + } + + @Override + public void accept(Object wrapper) { + if(wrapper instanceof Principal) { + this.username = ((Principal) wrapper).getName(); + } else if(wrapper instanceof AuthenticatedPrincipal) { + this.username = ((AuthenticatedPrincipal) wrapper).getName(); + } else if(wrapper instanceof UserDetails) { + UserDetails userDetails = (UserDetails) wrapper; + this.username = userDetails.getUsername(); + this.password = userDetails.getPassword(); + this.enabled = userDetails.isEnabled(); + this.accountNonExpired = userDetails.isAccountNonExpired(); + this.credentialsNonExpired = userDetails.isCredentialsNonExpired(); + this.accountNonExpired = userDetails.isAccountNonLocked(); + this.roles.addAll(AuthorityConverter.toRoles(userDetails.getAuthorities())); + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserMixIn.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserMixIn.java new file mode 100644 index 0000000..7fc4e15 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserMixIn.java @@ -0,0 +1,24 @@ +package cn.seqdata.oauth2.jackson; + +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author jrxian + * @date 2020-11-15 23:47 + */ +public class UserMixIn { + @JsonCreator + public UserMixIn( + @JsonProperty("username") String username, + @JsonProperty("password") String password, + @JsonProperty("enabled") boolean enabled, + @JsonProperty("accountNonExpired") boolean accountNonExpired, + @JsonProperty("credentialsNonExpired") boolean credentialsNonExpired, + @JsonProperty("accountNonLocked") boolean accountNonLocked, + @JsonProperty("authorities") Collection authorities) { + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UsernamePasswordAuthenticationTokenMinIn.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UsernamePasswordAuthenticationTokenMinIn.java new file mode 100644 index 0000000..2c37859 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UsernamePasswordAuthenticationTokenMinIn.java @@ -0,0 +1,19 @@ +package cn.seqdata.oauth2.jackson; + +import org.springframework.security.core.userdetails.UserDetails; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author jrxian + * @date 2020-11-15 10:46 + */ +@JsonIgnoreProperties("authenticated") +public class UsernamePasswordAuthenticationTokenMinIn { + @JsonCreator + public UsernamePasswordAuthenticationTokenMinIn( + @JsonProperty("principal") UserDetails principal, + @JsonProperty("credentials") Object credentials) { + } +} diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 6e67cdf..42ec23b 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -1,11 +1,13 @@ spring: + application: + name: authz profiles: active: jackson, jpa, log datasource: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver - url: jdbc:sqlserver://cloud.seqdata.cn:1433;DatabaseName=seqdata-cloud - username: seqdata - password: cloud@123 + url: jdbc:sqlserver://dev.voltmao.com:1433;DatabaseName=data_space_base + username: voltcat + password: vot@2018 data: jdbc: repositories: diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index 03ef736..2e5a3be 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -1,6 +1,4 @@ spring: - application: - name: ${APPLICATION_NAME:authz} cloud: gateway: discovery: @@ -8,9 +6,11 @@ spring: enabled: true nacos: discovery: - server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} - group: ${NACOS_SERVICE_DISCOVERY:devDiscovery} + server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} + group: ${NACOS_GROUP:DEFAULT_GROUP} + cluster-name: ${NACOS_CLUSTER_NAME:DEFAULT} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: ${CONFIG_FORMAT:yml} - group: ${NACOS_SERVICE_CONFIG:devConfig} + file-extension: ${NACOS_FILE_EXT:yml} +server: + port: 0 diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 148acd9..3ce33dc 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -1,4 +1,6 @@ spring: + application: + name: gateway profiles: active: jackson, log cloud: @@ -17,6 +19,4 @@ security: client-secret: secret resource: token-info-uri: http://authz/oauth/check_token - prefer-token-info: true -server: - port: 8080 \ No newline at end of file + prefer-token-info: true \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index f1ecf50..704d5dc 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -1,6 +1,4 @@ spring: - application: - name: ${APPLICATION_NAME:gateway} cloud: gateway: discovery: @@ -8,9 +6,9 @@ spring: enabled: true nacos: discovery: - server-addr: ${NACOS_SERVICE_HOST:nacos.seqdata.cn}:${NACOS_SERVICE_PORT:8848} - group: ${NACOS_SERVICE_DISCOVERY:devDiscovery} + server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} + group: ${NACOS_GROUP:DEFAULT_GROUP} + cluster-name: ${NACOS_CLUSTER_NAME:DEFAULT} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} - file-extension: ${CONFIG_FORMAT:yml} - group: ${NACOS_SERVICE_CONFIG:devConfig} \ No newline at end of file + file-extension: ${NACOS_FILE_EXT:yml} \ No newline at end of file -- Gitee From d887b2e72bfb327e29c2fa0679586c1e6319bd10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 21 Nov 2020 11:49:48 +0800 Subject: [PATCH 103/228] =?UTF-8?q?=E8=A7=84=E8=8C=83gateway=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=EF=BC=8C=E6=94=AF=E6=8C=81enabled=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/MobileConfiguration.java | 2 +- .../java/cn/seqdata/gateway/GatewayConfiguration.java | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java index 9ce5ac9..bf4b66b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java @@ -23,7 +23,7 @@ import cn.seqdata.oauth2.service.MobileNonceService; public class MobileConfiguration { @Bean - @ConditionalOnProperty(value = "aliyun.sms.enabled", havingValue = "true") + @ConditionalOnProperty(value = "aliyun.sms.enabled") public AliyunSmsService smsService(AliyunProperties properties, AliyunSmsProperties smsProperties) { return new AliyunSmsService(properties, smsProperties); } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index e13798d..3bebaa4 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -1,12 +1,13 @@ package cn.seqdata.gateway; +import lombok.extern.slf4j.Slf4j; + import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; -import lombok.extern.slf4j.Slf4j; import cn.seqdata.gateway.filter.authc.AllowlistPredicate; import cn.seqdata.gateway.filter.authc.PathMatcherGlobalFilter; @@ -38,7 +39,7 @@ public class GatewayConfiguration { } @Bean - @ConditionalOnProperty(prefix = "spring.cloud.gateway.logging", value = "enable", matchIfMissing = true) + @ConditionalOnProperty(value = "spring.cloud.gateway.logging.enabled", matchIfMissing = true) public LoggingGlobalFilter loggingGlobalFilter(LogRecorder logRecorder, ResourceServerTokenServices tokenServices) { return new LoggingGlobalFilter(logRecorder, tokenServices); } @@ -49,7 +50,7 @@ public class GatewayConfiguration { } @Bean - @ConditionalOnProperty(prefix = "spring.cloud.gateway.path-matcher", value = "enable", matchIfMissing = true) + @ConditionalOnProperty(value = "spring.cloud.gateway.path-matcher.enabled", matchIfMissing = true) public PathMatcherGlobalFilter pathMatcherGlobalFilter(AllowlistPredicate allowlistPredicate, ResourceServerTokenServices tokenServices, PathPermissionEvaluator permissionEvaluator) { return new PathMatcherGlobalFilter(allowlistPredicate, tokenServices, permissionEvaluator); @@ -59,7 +60,7 @@ public class GatewayConfiguration { * 对前端签名进行验证 */ @Bean - @ConditionalOnProperty(prefix = "spring.cloud.gateway.hmac-verify", value = "enable") + @ConditionalOnProperty(value = "spring.cloud.gateway.hmac-verify.enabled") public HmacVerifyGlobalFilter hmacVerifyGlobalFilter() { return new HmacVerifyGlobalFilter(); } -- Gitee From fdc9e4b7b02a04c335e748cfaf0b06889922e307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 24 Nov 2020 14:49:13 +0800 Subject: [PATCH 104/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E8=BF=9E=E6=8E=A5=E6=95=B0=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 7 +- .../AuthorizationServerConfiguration.java | 104 +++++++++++++ .../oauth2/AuthzServerApplication.java | 2 - .../oauth2/AuthzServerConfiguration.java | 125 +++------------ .../seqdata/oauth2/SwaggerConfiguration.java | 55 +++++++ .../oauth2/WebSecurityConfiguration.java | 13 +- .../seqdata/oauth2/jackson/OAuth2Module.java | 6 + .../cn/seqdata/oauth2/jackson/UserBean.java | 17 +++ .../{ => service}/DefaultTokenEnhancer.java | 10 +- .../oauth2/service/DefaultTokenServices.java | 129 ++++++++++++++++ .../{jackson => service}/RedisTokenStore.java | 142 +++++++++++++----- .../src/main/resources/application.yml | 3 +- .../gateway/AuthClientConfiguration.java | 15 +- 13 files changed, 473 insertions(+), 155 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{ => service}/DefaultTokenEnhancer.java (89%) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{jackson => service}/RedisTokenStore.java (59%) diff --git a/seqdata-cloud-authc/src/main/resources/application.yml b/seqdata-cloud-authc/src/main/resources/application.yml index b8132ab..e930461 100644 --- a/seqdata-cloud-authc/src/main/resources/application.yml +++ b/seqdata-cloud-authc/src/main/resources/application.yml @@ -1,13 +1,8 @@ -logging: - level: - root: info - cn.seqdata: trace - org.springframework.security: debug spring: application: name: authc profiles: - active: jackson + active: logging, jackson security: user: name: authc diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java new file mode 100644 index 0000000..29048f5 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -0,0 +1,104 @@ +package cn.seqdata.oauth2; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import lombok.AllArgsConstructor; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.CompositeTokenGranter; +import org.springframework.security.oauth2.provider.OAuth2RequestFactory; +import org.springframework.security.oauth2.provider.TokenGranter; +import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter; +import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; +import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; +import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.provisioning.UserDetailsManager; + +import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; +import cn.seqdata.oauth2.mobile.MobileNonceHandler; +import cn.seqdata.oauth2.mobile.MobileTokenGranter; +import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; +import cn.seqdata.oauth2.service.JpaClientDetailsService; + +/** + * Author: jrxian + * Date: 2019-10-02 19:11 + */ +@Configuration +@EnableAuthorizationServer +@AllArgsConstructor +public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { + private final AuthenticationManager authenticationManager; + private final UserDetailsManager userDetailsService; + private final AuthorizationServerTokenServices tokenServices; + private final TokenStore tokenStore; + private final TokenEnhancer tokenEnhancer; + private final MobileNonceHandler mobileNonceHandler; + private final ClientDetailRepo clientDetailRepo; + + @Override + public void configure(AuthorizationServerSecurityConfigurer security) { + security + .tokenKeyAccess("isAuthenticated()") + .checkTokenAccess("isAuthenticated()"); + } + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); + } + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + endpoints + .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) + .authenticationManager(authenticationManager) + .userDetailsService(userDetailsService) + .tokenServices(tokenServices) + .tokenStore(tokenStore) + .tokenEnhancer(tokenEnhancer) + .tokenGranter(tokenGranter(endpoints)); + } + + /** + * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter + */ + private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { + ClientDetailsService clientDetails = endpoints.getClientDetailsService(); + AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); + AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); + OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); + + List tokenGranters = new ArrayList<>(); + tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); + tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); + //自定义的手机验证码登录 + //!!!必须在用户名密码验证前面 + if(Objects.nonNull(mobileNonceHandler)) { + MobileNonceAuthenticationProvider mobileNonceProvider = new MobileNonceAuthenticationProvider(userDetailsService, mobileNonceHandler); + tokenGranters.add(new MobileTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory)); + } + //用户名密码验证 + if(Objects.nonNull(authenticationManager)) { + tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + } + + return new CompositeTokenGranter(tokenGranters); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index 926ec3f..81a2e98 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -4,7 +4,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; -import springfox.documentation.swagger2.annotations.EnableSwagger2; import cn.seqdata.wxapp.WxAppFeignClient; @@ -13,7 +12,6 @@ import cn.seqdata.wxapp.WxAppFeignClient; * Date: 2019-10-02 19:11 */ @SpringBootApplication -@EnableSwagger2 @EnableDiscoveryClient @EnableFeignClients(clients = WxAppFeignClient.class) public class AuthzServerApplication { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 76823a7..d783478 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -1,62 +1,32 @@ package cn.seqdata.oauth2; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import lombok.AllArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; -import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; -import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; -import org.springframework.security.oauth2.provider.ClientDetailsService; -import org.springframework.security.oauth2.provider.CompositeTokenGranter; -import org.springframework.security.oauth2.provider.OAuth2RequestFactory; -import org.springframework.security.oauth2.provider.TokenGranter; -import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter; -import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; -import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter; -import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; -import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; -import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; -import com.fasterxml.jackson.databind.ObjectMapper; -import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; -import cn.seqdata.oauth2.mobile.MobileNonceHandler; -import cn.seqdata.oauth2.mobile.MobileTokenGranter; -import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; -import cn.seqdata.oauth2.service.JpaClientDetailsService; -import cn.seqdata.oauth2.service.JpaUserDetailsManager; +import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.service.DefaultTokenEnhancer; +import cn.seqdata.oauth2.service.DefaultTokenServices; +import cn.seqdata.oauth2.service.RedisTokenStore; +import cn.seqdata.oauth2.service.UserService; /** - * Author: jrxian - * Date: 2019-10-02 19:11 + * @author jrxian + * @date 2020-11-23 19:07 */ @Configuration -@EnableAuthorizationServer @AllArgsConstructor -public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapter { - private final AuthenticationManager authenticationManager; - private final JpaUserDetailsManager userDetailsService; - private final RedisConnectionFactory redisConnectionFactory; - private final StringRedisTemplate redisTemplate; - private final ClientDetailRepo clientDetailRepo; - private final MobileNonceHandler mobileNonceHandler; - private final DefaultTokenEnhancer tokenEnhancer; - private final ObjectMapper objectMapper; +public class AuthzServerConfiguration { @Bean public PasswordEncoder passwordEncoder() { @@ -69,73 +39,24 @@ public class AuthzServerConfiguration extends AuthorizationServerConfigurerAdapt } @Bean - public TokenStore tokenStore() { - return new cn.seqdata.oauth2.jackson.RedisTokenStore(redisTemplate); -// return new RedisTokenStore(redisConnectionFactory); + public TokenStore tokenStore(StringRedisTemplate redisTemplate) { + RedisTokenStore tokenStore = new RedisTokenStore(redisTemplate); + tokenStore.setMaximumSessions(3); + return tokenStore; } - @Override - public void configure(AuthorizationServerSecurityConfigurer security) { - security - .tokenKeyAccess("isAuthenticated()") - .checkTokenAccess("isAuthenticated()"); - } - - @Override - public void configure(ClientDetailsServiceConfigurer clients) throws Exception { - clients.withClientDetails(new JpaClientDetailsService(clientDetailRepo)); - -// InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); -// builder.withClient("client") -// .secret("secret") -// .authorizedGrantTypes(MobileTokenGranter.GRANT_TYPE, -// AuthorizationGrantType.PASSWORD.getValue(), -// AuthorizationGrantType.AUTHORIZATION_CODE.getValue(), -// AuthorizationGrantType.CLIENT_CREDENTIALS.getValue(), -// AuthorizationGrantType.REFRESH_TOKEN.getValue()) -// .autoApprove(true); -// -// registrationRepository.forEach(clientRegistration -> builder -// .withClient(clientRegistration.getRegistrationId()) -// .authorizedGrantTypes(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())); + @Bean + public TokenEnhancer tokenEnhancer(UserRepo userRepo, UserService userService) { + return new DefaultTokenEnhancer(userRepo, userService); } - @Override - public void configure(AuthorizationServerEndpointsConfigurer endpoints) { - endpoints - .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) - .authenticationManager(authenticationManager) - .userDetailsService(userDetailsService) - .tokenStore(tokenStore()) - .tokenEnhancer(tokenEnhancer) - .tokenGranter(tokenGranter(endpoints)); + @Bean + public AuthorizationServerTokenServices tokenServices(TokenStore tokenStore, TokenEnhancer tokenEnhancer) { + return new DefaultTokenServices(tokenStore, tokenEnhancer); } - /** - * 重写 AuthorizationServerEndpointsConfigurer.createDefaultTokenServices,添加 NonceTokenGranter - */ - private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) { - ClientDetailsService clientDetails = endpoints.getClientDetailsService(); - AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices(); - AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices(); - OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory(); - - List tokenGranters = new ArrayList<>(); - tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); - tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); - tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); - //自定义的手机验证码登录 - //!!!必须在用户名密码验证前面 - if(Objects.nonNull(mobileNonceHandler)) { - MobileNonceAuthenticationProvider mobileNonceProvider = new MobileNonceAuthenticationProvider(userDetailsService, mobileNonceHandler); - tokenGranters.add(new MobileTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory)); - } - //用户名密码验证 - if(Objects.nonNull(authenticationManager)) { - tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); - } - - return new CompositeTokenGranter(tokenGranters); + @Bean + public ApplicationEventPublisher applicationEventPublisher() { + return System.out::println; } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java new file mode 100644 index 0000000..52873e5 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java @@ -0,0 +1,55 @@ +package cn.seqdata.oauth2; + +import java.util.Collections; +import java.util.Objects; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.AntPathMatcher; +import springfox.documentation.builders.OAuthBuilder; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Author: jrxian + * Date: 2020-02-04 01:36 + */ +@Configuration +@EnableSwagger2 +public class SwaggerConfiguration { + + @Bean + public Docket apiDocker() { + return new Docket(DocumentationType.SWAGGER_2) + .securitySchemes(Collections.singletonList(new OAuthBuilder() + .name("OAuth2") + .grantTypes(Collections.singletonList(new ResourceOwnerPasswordCredentialsGrant("/authz/oauth/token"))) + .build())) + .securityContexts(Collections.singletonList(SecurityContext.builder() + .securityReferences(Collections.singletonList(SecurityReference.builder() + .reference("OAuth2") + .scopes(new AuthorizationScope[]{}) + .build())) + .forPaths(input -> { + if(Objects.nonNull(input)) { + String[] patterns = {"/router/**", "/rbac/**", "/perm/**"}; + AntPathMatcher matcher = new AntPathMatcher(); + + for(String pattern : patterns) { + if(matcher.match(pattern, input)) { + return true; + } + } + } + + return false; + }) + .build())) + ; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java index b058ee4..86e8f1c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -17,10 +18,20 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) { - web.ignoring() + web + .ignoring() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/v2/api-docs/**", "/webjars/**", "/plugins/**"); } + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .sessionManagement() + .maximumSessions(1); + + super.configure(http); + } + @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java index dbdb744..64f8d20 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/OAuth2Module.java @@ -6,6 +6,9 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.OAuth2AccessTokenJackson2Deserializer; +import org.springframework.security.oauth2.common.OAuth2AccessTokenJackson2Serializer; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import com.fasterxml.jackson.core.Version; @@ -29,5 +32,8 @@ public class OAuth2Module extends SimpleModule { addAbstractTypeMapping(GrantedAuthority.class, SimpleGrantedAuthority.class); addAbstractTypeMapping(UserDetails.class, UserBean.class); addAbstractTypeMapping(Authentication.class, UsernamePasswordAuthenticationToken.class); + + addSerializer(OAuth2AccessToken.class, new OAuth2AccessTokenJackson2Serializer()); + addDeserializer(OAuth2AccessToken.class, new OAuth2AccessTokenJackson2Deserializer()); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java index 12b274d..5f47f95 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/UserBean.java @@ -3,9 +3,11 @@ package cn.seqdata.oauth2.jackson; import java.security.Principal; import java.util.Collection; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.AuthenticatedPrincipal; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -50,4 +52,19 @@ public class UserBean implements UserDetails, Consumer { this.roles.addAll(AuthorityConverter.toRoles(userDetails.getAuthorities())); } } + + @Override + public boolean equals(Object o) { + if(this == o) return true; + if(o instanceof UserDetails) { + return StringUtils.equals(getUsername(), ((UserDetails) o).getUsername()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(username); + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/DefaultTokenEnhancer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java similarity index 89% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/DefaultTokenEnhancer.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java index 15c7e96..825cfe7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/DefaultTokenEnhancer.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java @@ -1,28 +1,26 @@ -package cn.seqdata.oauth2; +package cn.seqdata.oauth2.service; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; +import lombok.AllArgsConstructor; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenEnhancer; -import org.springframework.stereotype.Component; -import lombok.AllArgsConstructor; +import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserRepo; -import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; /** * @author jrxian * @date 2020-09-24 14:55 */ -@Component @AllArgsConstructor public class DefaultTokenEnhancer implements TokenEnhancer { private final UserRepo userRepo; @@ -41,7 +39,9 @@ public class DefaultTokenEnhancer implements TokenEnhancer { if(userOptional.isPresent()) { UserAccount account = userOptional.get(); User user = userRepo.getOne(Objects.requireNonNull(account.getId())); + attributes.put(OAuth2Constants.NAME, user.getName()); attributes.put(OAuth2Constants.USER_ID, user.getId()); + attributes.put(OAuth2Constants.POST_ID, user.getPostId()); attributes.put(OAuth2Constants.DEPT_ID, user.getDeptId()); attributes.put(OAuth2Constants.ORG_ID, user.getOrgId()); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java new file mode 100644 index 0000000..9baa02c --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java @@ -0,0 +1,129 @@ +package cn.seqdata.oauth2.service; + +import java.util.Date; +import java.util.Objects; +import java.util.UUID; +import lombok.RequiredArgsConstructor; + +import org.joda.time.DateTimeConstants; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.*; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.TokenRequest; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.stereotype.Service; + +/** + * @author jrxian + * @date 2020-11-23 16:49 + */ +@Service +@RequiredArgsConstructor +public class DefaultTokenServices implements AuthorizationServerTokenServices { + private boolean supportRefreshToken; + private int accessTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_MINUTE; + private int refreshTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_DAY; + + private final TokenStore tokenStore; + private final TokenEnhancer tokenEnhancer; + + public boolean isSupportRefreshToken() { + return supportRefreshToken; + } + + public void setSupportRefreshToken(boolean supportRefreshToken) { + this.supportRefreshToken = supportRefreshToken; + } + + public int getAccessTokenValiditySeconds() { + return accessTokenValiditySeconds; + } + + public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) { + this.accessTokenValiditySeconds = accessTokenValiditySeconds; + } + + public int getRefreshTokenValiditySeconds() { + return refreshTokenValiditySeconds; + } + + public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) { + this.refreshTokenValiditySeconds = refreshTokenValiditySeconds; + } + + @Override + public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { + return tokenStore.getAccessToken(authentication); + } + + @Override + public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { + OAuth2RefreshToken refreshToken = createRefreshToken(authentication); + OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); + tokenStore.storeAccessToken(accessToken, authentication); + return accessToken; + } + + @Override + public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException { + if(!supportRefreshToken) { + throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); + } + + OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue); + if(Objects.isNull(refreshToken)) { + throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); + } + + tokenStore.removeAccessTokenUsingRefreshToken(refreshToken); + + if(isExpired(refreshToken)) { + tokenStore.removeRefreshToken(refreshToken); + throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken); + } + + OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken); + OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); + tokenStore.storeAccessToken(accessToken, authentication); + + return accessToken; + } + + private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { + DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(createTokenValue()); + + token.setExpiration(expiration(accessTokenValiditySeconds)); + token.setRefreshToken(refreshToken); + OAuth2Request auth2Request = authentication.getOAuth2Request(); + token.setScope(auth2Request.getScope()); + + return Objects.nonNull(tokenEnhancer) ? tokenEnhancer.enhance(token, authentication) : token; + } + + private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) { + if(!supportRefreshToken) return null; + return new DefaultExpiringOAuth2RefreshToken(createTokenValue(), expiration(refreshTokenValiditySeconds)); + } + + private String createTokenValue() { + return String.valueOf(UUID.randomUUID()); + } + + private Date expiration(int validitySeconds) { + return new Date(System.currentTimeMillis() + validitySeconds * DateTimeConstants.MILLIS_PER_SECOND); + } + + private boolean isExpired(OAuth2RefreshToken refreshToken) { + if(refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken; + Date expiration = expiringToken.getExpiration(); + return expiration == null || System.currentTimeMillis() > expiration.getTime(); + } + return false; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/RedisTokenStore.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java similarity index 59% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/RedisTokenStore.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java index fa7ceda..4a0cc82 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/RedisTokenStore.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java @@ -1,12 +1,13 @@ -package cn.seqdata.oauth2.jackson; +package cn.seqdata.oauth2.service; import java.util.*; import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.core.BoundSetOperations; -import org.springframework.data.redis.core.SetOperations; +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.data.redis.connection.RedisZSetCommands; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; +import org.springframework.data.redis.core.ZSetOperations; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; @@ -14,12 +15,14 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.oauth2.sdk.util.CollectionUtils; + +import cn.seqdata.oauth2.jackson.OAuth2AuthenticationBean; +import cn.seqdata.oauth2.jackson.OAuth2Module; /** * @author jrxian @@ -27,20 +30,21 @@ import com.nimbusds.oauth2.sdk.util.CollectionUtils; */ public class RedisTokenStore implements TokenStore { private static final String ACCESS = "access:"; - private static final String AUTH_TO_ACCESS = "auth_to_access:"; - private static final String AUTH = "auth:"; + private static final String ACCESS_AUTH = "access_auth:"; + private static final String CLIENTID_TO_ACCESS = "clientid_to_access:"; + private static final String USERNAME_TO_ACCESS = "username_to_access:"; + private static final String REFRESH = "refresh:"; private static final String REFRESH_AUTH = "refresh_auth:"; private static final String ACCESS_TO_REFRESH = "access_to_refresh:"; - private static final String REFRESH = "refresh:"; private static final String REFRESH_TO_ACCESS = "refresh_to_access:"; - private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:"; - private static final String UNAME_TO_ACCESS = "uname_to_access:"; private final ObjectMapper objectMapper = new ObjectMapper(); private final StringRedisTemplate redisTemplate; private final ValueOperations opsForValue; - private final SetOperations opsForSet; - private AuthenticationKeyGenerator keyGenerator = x -> String.valueOf(UUID.randomUUID()); + private final ZSetOperations opsForZSet; + + //每个用户能登录的最大并发连接数,<=0表示不限制 + private int maximumSessions; public RedisTokenStore(StringRedisTemplate redisTemplate) { this.objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); @@ -48,20 +52,20 @@ public class RedisTokenStore implements TokenStore { this.redisTemplate = redisTemplate; this.opsForValue = redisTemplate.opsForValue(); - this.opsForSet = redisTemplate.opsForSet(); + this.opsForZSet = redisTemplate.opsForZSet(); } - public AuthenticationKeyGenerator getKeyGenerator() { - return keyGenerator; + public int getMaximumSessions() { + return maximumSessions; } - public void setKeyGenerator(AuthenticationKeyGenerator keyGenerator) { - this.keyGenerator = keyGenerator; + public void setMaximumSessions(int maximumSessions) { + this.maximumSessions = maximumSessions; } @Override public OAuth2Authentication readAuthentication(String token) { - return fromJSON(opsForValue.get(AUTH + token)); + return fromJSON(opsForValue.get(ACCESS_AUTH + token)); } @Override @@ -71,32 +75,52 @@ public class RedisTokenStore implements TokenStore { @Override public OAuth2AccessToken readAccessToken(String tokenValue) { - return fromJSON(opsForValue.get(ACCESS + tokenValue), OAuth2AccessToken.class); + OAuth2AccessToken accessToken = fromJSON(opsForValue.get(ACCESS + tokenValue), OAuth2AccessToken.class); + + //每次访问时自动续期,防止token失效 + if(Objects.nonNull(accessToken)) { + int expiresIn = accessToken.getExpiresIn(); + if(expiresIn > 0) { + redisTemplate.expire(ACCESS + tokenValue, expiresIn, TimeUnit.SECONDS); + redisTemplate.expire(ACCESS_AUTH + tokenValue, expiresIn, TimeUnit.SECONDS); + } + } + return accessToken; } @Override public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { - String key = keyGenerator.extractKey(authentication); - String tokenValue = opsForValue.get(AUTH_TO_ACCESS + key); - return readAccessToken(tokenValue); + String key = USERNAME_TO_ACCESS + getApprovalKey(authentication); + //找到最后一次生成的token + Set> tuples = opsForZSet.reverseRangeWithScores(key, 0, 1); + ZSetOperations.TypedTuple tuple = CollectionUtils.lastElement(tuples); + if(Objects.nonNull(tuple)) { + return readAccessToken(tuple.getValue()); + } else { + return null; + } } @Override public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { - String key = keyGenerator.extractKey(authentication); OAuth2Request auth2Request = authentication.getOAuth2Request(); String tokenValue = token.getValue(); int expires = token.getExpiresIn(); - opsForValue.set(AUTH + tokenValue, toJSON(authentication), expires, TimeUnit.SECONDS); opsForValue.set(ACCESS + tokenValue, toJSON(token), expires, TimeUnit.SECONDS); - opsForValue.set(AUTH_TO_ACCESS + key, tokenValue, expires, TimeUnit.SECONDS); - opsForSet.add(CLIENT_ID_TO_ACCESS + auth2Request.getClientId(), tokenValue); + opsForValue.set(ACCESS_AUTH + tokenValue, toJSON(authentication), expires, TimeUnit.SECONDS); if(!authentication.isClientOnly()) { - opsForSet.add(UNAME_TO_ACCESS + getApprovalKey(authentication), tokenValue); + String key = USERNAME_TO_ACCESS + getApprovalKey(authentication); + opsForZSet.add(key, tokenValue, System.currentTimeMillis()); + checkMaximumSessions(key); + } else { + String key = CLIENTID_TO_ACCESS + auth2Request.getClientId(); + opsForZSet.add(key, tokenValue, System.currentTimeMillis()); } OAuth2RefreshToken refreshToken = token.getRefreshToken(); if(Objects.nonNull(refreshToken) && Objects.nonNull(refreshToken.getValue())) { + storeRefreshToken(refreshToken, authentication); + String refreshToAccessKey = REFRESH_TO_ACCESS + refreshToken.getValue(); String accessToRefreshKey = ACCESS_TO_REFRESH + tokenValue; @@ -148,32 +172,33 @@ public class RedisTokenStore implements TokenStore { public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { String refreshToAccessKey = REFRESH_TO_ACCESS + refreshToken.getValue(); String tokenValue = opsForValue.get(refreshToAccessKey); - redisTemplate.delete(refreshToAccessKey); - redisTemplate.delete(ACCESS + tokenValue); + if(BooleanUtils.isTrue(redisTemplate.delete(refreshToAccessKey))) { + redisTemplate.delete(ACCESS + tokenValue); + } } @Override public Collection findTokensByClientId(String clientId) { - return findTokens(CLIENT_ID_TO_ACCESS + clientId); + return findTokens(CLIENTID_TO_ACCESS + clientId); } @Override public Collection findTokensByClientIdAndUserName(String clientId, String userName) { - return findTokens(UNAME_TO_ACCESS + getApprovalKey(clientId, userName)); + return findTokens(USERNAME_TO_ACCESS + getApprovalKey(clientId, userName)); } private Collection findTokens(String key) { List accessTokens = new LinkedList<>(); - BoundSetOperations boundSetOps = redisTemplate.boundSetOps(key); - Set members = boundSetOps.members(); - if(CollectionUtils.isNotEmpty(members)) { - members.forEach(member -> { - OAuth2AccessToken accessToken = readAccessToken(member); + Set tokenValues = opsForZSet.rangeByLex(key, RedisZSetCommands.Range.unbounded()); + if(!CollectionUtils.isEmpty(tokenValues)) { + tokenValues.forEach(tokenValue -> { + OAuth2AccessToken accessToken = readAccessToken(tokenValue); if(Objects.nonNull(accessToken)) { accessTokens.add(accessToken); } else { - boundSetOps.remove(member); + //没有找到token,说明已经失效,从列表中删除 + opsForZSet.remove(key, tokenValue); } }); } @@ -181,6 +206,49 @@ public class RedisTokenStore implements TokenStore { return accessTokens; } + /*** + * 检查最大并发会话数限制,当超过最大并发回话时,删除最先申请的token + * @param key username_to_access:clientid:username + */ + private void checkMaximumSessions(String key) { + SortedMap tokenMap = new TreeMap<>(); + Set> tuples = opsForZSet.rangeWithScores(key, 0, -1); + + //检查不存在的accessToken,先清除 + if(!CollectionUtils.isEmpty(tuples)) { + Iterator> iterator = tuples.iterator(); + while(iterator.hasNext()) { + ZSetOperations.TypedTuple tuple = iterator.next(); + if(Objects.nonNull(tuple.getValue())) { + String tokenValue = tuple.getValue(); + if(BooleanUtils.isNotTrue(redisTemplate.hasKey(ACCESS + tokenValue))) { + //accessToken已经不存在了,从清单中删除 + opsForZSet.remove(key, tokenValue); + iterator.remove(); + } else { + if(Objects.nonNull(tuple.getScore())) { + tokenMap.put(tuple.getScore(), tokenValue); + } + } + } + } + } + + //对现存的accessToken排序 + List> tokenTuples = new ArrayList<>(tokenMap.entrySet()); + if(maximumSessions > 0) { + //删除最旧的token + for(int i = 0; i < tokenTuples.size() - maximumSessions; ++i) { + Map.Entry entry = tokenTuples.get(i); + String tokenValue = entry.getValue(); + + redisTemplate.delete(ACCESS + tokenValue); + redisTemplate.delete(ACCESS_AUTH + tokenValue); + opsForZSet.remove(key, tokenValue); + } + } + } + private T fromJSON(String json, Class clazz) { if(Objects.isNull(json)) return null; diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 42ec23b..ca8c9fb 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -16,8 +16,9 @@ spring: repositories: enabled: false redis: - host: redis.seqdata.cn + host: db.rtelec.cn port: 6379 + password: seq@2015 security: oauth2: client: diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java index 378dc18..57e539f 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java @@ -1,13 +1,18 @@ package cn.seqdata.gateway; +import java.io.IOException; + import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.NonNull; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.RemoteTokenServices; +import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; @@ -31,7 +36,15 @@ public class AuthClientConfiguration { @Bean @LoadBalanced public RestTemplate restTemplate() { - return new RestTemplate(); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { + public void handleError(@NonNull ClientHttpResponse response) throws IOException { + if(response.getRawStatusCode() != 400) { + super.handleError(response); + } + } + }); + return restTemplate; } /** -- Gitee From cdb202898672ecb6f71a7de91d901d58db5ee961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 24 Nov 2020 15:42:00 +0800 Subject: [PATCH 105/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0swagger.enabled?= =?UTF-8?q?=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/SwaggerConfiguration.java | 2 ++ ...itional-spring-configuration-metadata.json | 24 +++++++++++++++++++ .../src/main/resources/bootstrap.yml | 9 +++++++ 3 files changed, 35 insertions(+) create mode 100644 seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java index 52873e5..4258f2a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2; import java.util.Collections; import java.util.Objects; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.util.AntPathMatcher; @@ -20,6 +21,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; * Date: 2020-02-04 01:36 */ @Configuration +@ConditionalOnProperty(value = "swagger.enabled", matchIfMissing = true) @EnableSwagger2 public class SwaggerConfiguration { diff --git a/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000..6df1ec8 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,24 @@ +{ + "properties": [ + { + "name": "spring.cloud.gateway.logging.enabled", + "type": "java.lang.String", + "description": "Description for spring.cloud.gateway.logging.enabled." + }, + { + "name": "spring.cloud.gateway.path-matcher.enabled", + "type": "java.lang.String", + "description": "Description for spring.cloud.gateway.path-matcher.enabled." + }, + { + "name": "spring.cloud.gateway.hmac-verify.enabled", + "type": "java.lang.String", + "description": "Description for spring.cloud.gateway.hmac-verify.enabled." + }, + { + "name": "spring.cloud.gateway.ignoring", + "type": "java.lang.String", + "description": "Description for spring.cloud.gateway.ignoring." + } + ] +} \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index 704d5dc..bc8aa47 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -4,6 +4,15 @@ spring: discovery: locator: enabled: true + logging: + enabled: true + path-matcher: + enabled: true + hmac-verify: + enabled: false + ignoring: + - /authc/** + - /authz/** nacos: discovery: server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} -- Gitee From f87d8e2d4d648aab34bf0b4dfa5e3ec2264b05bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 24 Nov 2020 15:42:25 +0800 Subject: [PATCH 106/228] =?UTF-8?q?gateway=E5=A2=9E=E5=8A=A0ignoring?= =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/gateway/GatewayConfiguration.java | 6 ++++-- .../cn/seqdata/gateway/GatewayProperties.java | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index 3bebaa4..0814620 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.core.RabbitTemplate; 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.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; @@ -24,6 +25,7 @@ import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; */ @Slf4j @Configuration +@EnableConfigurationProperties(GatewayProperties.class) public class GatewayConfiguration { @Bean @@ -45,8 +47,8 @@ public class GatewayConfiguration { } @Bean - public AllowlistPredicate allowlistPredicate() { - return new AllowlistPredicate(); + public AllowlistPredicate allowlistPredicate(GatewayProperties properties) { + return new AllowlistPredicate(properties.getIgnoring()); } @Bean diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java new file mode 100644 index 0000000..008755d --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java @@ -0,0 +1,16 @@ +package cn.seqdata.gateway; + +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author jrxian + * @date 2020-11-24 15:37 + */ +@lombok.Getter +@lombok.Setter +@ConfigurationProperties("spring.cloud.gateway") +public class GatewayProperties { + private List ignoring; +} -- Gitee From 3a48d3e746cb3b781cac5bef1efc5a532ca60257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 25 Nov 2020 15:52:20 +0800 Subject: [PATCH 107/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0ServiceSecurityUtils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 8 +-- .../oauth2/controller/ConnectController.java | 4 +- .../controller/OAuth2CodeController.java | 6 +- .../oauth2/provider/OAuth2Provider.java | 15 +++-- .../oauth2/service/DefaultTokenServices.java | 7 ++- .../cn/seqdata/oauth2/util/CastFunction.java | 22 +++++++ .../cn/seqdata/oauth2/util/SecurityUtils.java | 38 +++++------- .../oauth2/util/ServiceSecurityUtils.java | 61 +++++++++++++++++++ 8 files changed, 119 insertions(+), 42 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index d783478..cb22535 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -2,7 +2,6 @@ package cn.seqdata.oauth2; import lombok.AllArgsConstructor; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; @@ -41,7 +40,7 @@ public class AuthzServerConfiguration { @Bean public TokenStore tokenStore(StringRedisTemplate redisTemplate) { RedisTokenStore tokenStore = new RedisTokenStore(redisTemplate); - tokenStore.setMaximumSessions(3); + tokenStore.setMaximumSessions(-1); return tokenStore; } @@ -54,9 +53,4 @@ public class AuthzServerConfiguration { public AuthorizationServerTokenServices tokenServices(TokenStore tokenStore, TokenEnhancer tokenEnhancer) { return new DefaultTokenServices(tokenStore, tokenEnhancer); } - - @Bean - public ApplicationEventPublisher applicationEventPublisher() { - return System.out::println; - } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 31f22b9..bcf0cea 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -10,6 +10,7 @@ import java.util.Optional; import javax.security.auth.login.AccountNotFoundException; import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; +import lombok.AllArgsConstructor; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; @@ -17,7 +18,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.web.bind.annotation.*; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; @@ -89,7 +89,7 @@ public class ConnectController { @GetMapping(value = "/{registrationId}") public ResponseEntity bind(@PathVariable String registrationId, HttpServletRequest request) { ClientRegistration registration = repository.findByRegistrationId(registrationId); - OAuth2Provider provider = SecurityUtils.provider(registration); + OAuth2Provider provider = OAuth2Provider.provider(registration); OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); //从 request 中分离出 accessToken diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 52c3c7e..c6646aa 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -1,5 +1,7 @@ package cn.seqdata.oauth2.controller; +import lombok.AllArgsConstructor; + import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -12,14 +14,12 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.provider.UserProfile; import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; -import cn.seqdata.oauth2.util.SecurityUtils; /** * Author: jrxian @@ -40,7 +40,7 @@ public class OAuth2CodeController { @RequestParam(OAuth2ParameterNames.CODE) String code, @RequestParam(value = OAuth2ParameterNames.STATE, required = false) String state) { ClientRegistration registration = repository.findByRegistrationId(registrationId); - OAuth2Provider provider = SecurityUtils.provider(registration); + OAuth2Provider provider = OAuth2Provider.provider(registration); OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); //从远程拉取用户信息,以 registrationId + remoteUser.name 作为唯一识别符 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java index 8d5bbbc..13c36fd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/provider/OAuth2Provider.java @@ -72,6 +72,12 @@ public enum OAuth2Provider { this.profile = profile; } + public abstract ClientRegistration.Builder getBuilder(String registrationId); + + public OAuth2Template createOAuth2Template(ClientRegistration registration) { + return new OAuth2Template(registration); + } + public static OAuth2Provider fromString(String providerId) { for(OAuth2Provider value : values()) { if(0 == StringUtils.compareIgnoreCase(value.name(), providerId)) { @@ -82,7 +88,7 @@ public enum OAuth2Provider { return DEFAULT; } - public static OAuth2Provider matcher(String url) { + public static OAuth2Provider match(String url) { for(OAuth2Provider value : values()) { if(StringUtils.contains(url, value.hostname)) { return value; @@ -92,9 +98,8 @@ public enum OAuth2Provider { return DEFAULT; } - public abstract ClientRegistration.Builder getBuilder(String registrationId); - - public OAuth2Template createOAuth2Template(ClientRegistration registration) { - return new OAuth2Template(registration); + public static OAuth2Provider provider(ClientRegistration registration) { + ClientRegistration.ProviderDetails details = registration.getProviderDetails(); + return OAuth2Provider.match(details.getTokenUri()); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java index 9baa02c..53056e0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java @@ -1,8 +1,8 @@ package cn.seqdata.oauth2.service; +import java.security.SecureRandom; import java.util.Date; import java.util.Objects; -import java.util.UUID; import lombok.RequiredArgsConstructor; import org.joda.time.DateTimeConstants; @@ -25,6 +25,8 @@ import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class DefaultTokenServices implements AuthorizationServerTokenServices { + static final SecureRandom numberGenerator = new SecureRandom(); + private boolean supportRefreshToken; private int accessTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_MINUTE; private int refreshTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_DAY; @@ -111,7 +113,8 @@ public class DefaultTokenServices implements AuthorizationServerTokenServices { } private String createTokenValue() { - return String.valueOf(UUID.randomUUID()); + return String.format("%08x%08x%08x%08x", (int) (System.currentTimeMillis() / 1000), + numberGenerator.nextInt(), numberGenerator.nextInt(), numberGenerator.nextInt()); } private Date expiration(int validitySeconds) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java new file mode 100644 index 0000000..a3dba3b --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java @@ -0,0 +1,22 @@ +package cn.seqdata.oauth2.util; + +import java.util.function.Function; +import lombok.AllArgsConstructor; + +/** + * @author jrxian + * @date 2020-11-25 15:29 + */ +@AllArgsConstructor +public class CastFunction implements Function { + private final Class clazz; + + @Override + public T apply(Object object) { + if(clazz.isInstance(object)) { + return clazz.cast(object); + } else { + return null; + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index e6634ec..58af1c0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -7,14 +7,12 @@ import java.util.stream.Collectors; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; import cn.seqdata.oauth2.DefaultRole; import cn.seqdata.oauth2.OAuth2Constants; -import cn.seqdata.oauth2.provider.OAuth2Provider; /** * Author: jrxian @@ -25,39 +23,33 @@ public final class SecurityUtils { private SecurityUtils() { } - public static OAuth2Provider provider(ClientRegistration registration) { - ClientRegistration.ProviderDetails details = registration.getProviderDetails(); - return OAuth2Provider.matcher(details.getTokenUri()); - } - /** * 根据用户获取oauth2的token */ public static String accessToken(Principal principal) { - if(principal instanceof OAuth2Authentication) { - Object details = ((OAuth2Authentication) principal).getDetails(); - if(details instanceof OAuth2AuthenticationDetails) { - return ((OAuth2AuthenticationDetails) details).getTokenValue(); - } - } - - return null; + return Optional.ofNullable(principal) + .map(new CastFunction<>(OAuth2Authentication.class)) + .map(OAuth2Authentication::getDetails) + .map(new CastFunction<>(OAuth2AuthenticationDetails.class)) + .map(OAuth2AuthenticationDetails::getTokenValue) + .orElse(null); } public static String clientId(Principal principal) { - if(principal instanceof OAuth2Authentication) { - OAuth2Request auth2Request = ((OAuth2Authentication) principal).getOAuth2Request(); - return auth2Request.getClientId(); - } - - return null; + return Optional.ofNullable(principal) + .map(new CastFunction<>(OAuth2Authentication.class)) + .map(OAuth2Authentication::getOAuth2Request) + .map(OAuth2Request::getClientId) + .orElse(null); } /** * 用户名 */ public static String username(Principal principal) { - return principal.getName(); + return Optional.ofNullable(principal) + .map(Principal::getName) + .orElse(null); } /** @@ -133,7 +125,7 @@ public final class SecurityUtils { if(value instanceof Number) { return ((Number) value).longValue(); } else { - return NumberUtils.toLong((String) value); + return NumberUtils.toLong(String.valueOf(value)); } } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java new file mode 100644 index 0000000..9f035a9 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java @@ -0,0 +1,61 @@ +package cn.seqdata.oauth2.util; + +import java.util.Collection; +import java.util.Set; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * @author jrxian + * @date 2020-11-25 15:11 + * 用于@Service层面获取当前登录用户基本信息,不需要参数Principal + */ +public final class ServiceSecurityUtils { + + private ServiceSecurityUtils() { + } + + public static Authentication principal() { + SecurityContext context = SecurityContextHolder.getContext(); + return context.getAuthentication(); + } + + public static String accessToken() { + return SecurityUtils.accessToken(principal()); + } + + public static String clientId() { + return SecurityUtils.accessToken(principal()); + } + + public static String username() { + return SecurityUtils.username(principal()); + } + + public static Long userId() { + return SecurityUtils.userId(principal()); + } + + public static Long deptId() { + return SecurityUtils.deptId(principal()); + } + + public static Long orgId() { + return SecurityUtils.orgId(principal()); + } + + public static boolean isSysAdmin() { + return SecurityUtils.isSysAdmin(principal()); + } + + public static Collection grantedAuthorities() { + return SecurityUtils.grantedAuthorities(principal()); + } + + public static Set authorities() { + return SecurityUtils.authorities(principal()); + } +} -- Gitee From 1166a34cc1a419d92a77dcb04b849ff5e38ce96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 26 Nov 2020 15:35:40 +0800 Subject: [PATCH 108/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81captcha=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/SwaggerConfiguration.java | 3 +- .../oauth2/service/DefaultTokenServices.java | 7 +- .../cn/seqdata/oauth2/util/CastFunction.java | 22 --- .../cn/seqdata/oauth2/util/SecurityUtils.java | 1 + seqdata-cloud-gateway/pom.xml | 89 ++++++----- .../seqdata/gateway/GatewayConfiguration.java | 7 + .../cn/seqdata/gateway/GatewayProperties.java | 3 + .../authc}/AllowlistController.java | 6 +- .../authc}/AnonymousService.java | 4 +- .../authc}/AuthClientConfiguration.java | 4 +- .../{ => authc}/BearerTokenResolver.java | 2 +- .../authc/CacheController.java} | 27 ++-- .../CachedResourceServerTokenServices.java | 4 +- .../DefaultBearerTokenResolver.java | 2 +- .../filter/authc/PathMatcherGlobalFilter.java | 6 +- .../{ => authc}/ResourceServerProperties.java | 2 +- .../filter/captcha/CaptchaController.java | 50 ++++++ .../filter/captcha/CaptchaException.java | 23 +++ .../filter/captcha/CaptchaFunction.java | 12 ++ .../filter/captcha/CaptchaGlobalFilter.java | 147 ++++++++++++++++++ .../filter/captcha/CaptchaProperties.java | 13 ++ .../gateway/filter/captcha/CaptchaType.java | 41 +++++ .../filter/logging/LoggingGlobalFilter.java | 8 +- .../filter/verify/HmacVerifyGlobalFilter.java | 58 +++---- ...itional-spring-configuration-metadata.json | 10 +- .../src/main/resources/application.yml | 9 ++ .../src/main/resources/bootstrap.yml | 9 -- 27 files changed, 420 insertions(+), 149 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/{controller => filter/authc}/AllowlistController.java (92%) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/{controller => filter/authc}/AnonymousService.java (96%) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/{ => filter/authc}/AuthClientConfiguration.java (95%) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/{ => authc}/BearerTokenResolver.java (83%) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/{controller/GatewayController.java => filter/authc/CacheController.java} (68%) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/{ => authc}/CachedResourceServerTokenServices.java (98%) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/{ => authc}/DefaultBearerTokenResolver.java (96%) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/{ => authc}/ResourceServerProperties.java (89%) create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaFunction.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaType.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java index 4258f2a..649b52c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java @@ -24,6 +24,7 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @ConditionalOnProperty(value = "swagger.enabled", matchIfMissing = true) @EnableSwagger2 public class SwaggerConfiguration { + private final AntPathMatcher matcher = new AntPathMatcher(); @Bean public Docket apiDocker() { @@ -40,8 +41,6 @@ public class SwaggerConfiguration { .forPaths(input -> { if(Objects.nonNull(input)) { String[] patterns = {"/router/**", "/rbac/**", "/perm/**"}; - AntPathMatcher matcher = new AntPathMatcher(); - for(String pattern : patterns) { if(matcher.match(pattern, input)) { return true; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java index 53056e0..ab4796e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java @@ -1,10 +1,10 @@ package cn.seqdata.oauth2.service; -import java.security.SecureRandom; import java.util.Date; import java.util.Objects; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.RandomStringUtils; import org.joda.time.DateTimeConstants; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.*; @@ -25,8 +25,6 @@ import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class DefaultTokenServices implements AuthorizationServerTokenServices { - static final SecureRandom numberGenerator = new SecureRandom(); - private boolean supportRefreshToken; private int accessTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_MINUTE; private int refreshTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_DAY; @@ -113,8 +111,7 @@ public class DefaultTokenServices implements AuthorizationServerTokenServices { } private String createTokenValue() { - return String.format("%08x%08x%08x%08x", (int) (System.currentTimeMillis() / 1000), - numberGenerator.nextInt(), numberGenerator.nextInt(), numberGenerator.nextInt()); + return String.format("%08x", (int) (System.currentTimeMillis() / 1000)) + RandomStringUtils.randomAlphanumeric(8); } private Date expiration(int validitySeconds) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java deleted file mode 100644 index a3dba3b..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/CastFunction.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.seqdata.oauth2.util; - -import java.util.function.Function; -import lombok.AllArgsConstructor; - -/** - * @author jrxian - * @date 2020-11-25 15:29 - */ -@AllArgsConstructor -public class CastFunction implements Function { - private final Class clazz; - - @Override - public T apply(Object object) { - if(clazz.isInstance(object)) { - return clazz.cast(object); - } else { - return null; - } - } -} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index 58af1c0..26fb8cb 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -11,6 +11,7 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import cn.seqdata.core.util.CastFunction; import cn.seqdata.oauth2.DefaultRole; import cn.seqdata.oauth2.OAuth2Constants; diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index 5f2678a..4b1625c 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -1,48 +1,53 @@ - 4.0.0 - - cn.seqdata.cloud - seqdata-cloud-parent - 2.2.1-SNAPSHOT - - seqdata-cloud-gateway - 网关,提供路由功能和其它一些通过用功能(防重放攻击、url过滤、统一日志等等) - - - org.springframework.cloud - spring-cloud-starter-gateway - - - org.springframework.cloud - spring-cloud-starter-netflix-hystrix - - - org.springframework.boot - spring-boot-configuration-processor - true - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + cn.seqdata.cloud + seqdata-cloud-parent + 2.2.1-SNAPSHOT + + seqdata-cloud-gateway + 网关,提供路由功能和其它一些通过用功能(防重放攻击、url过滤、统一日志等等) + + + com.github.whvcse + easy-captcha + 1.6.2 + - - cn.seqdata.cloud - seqdata-cloud-log - ${project.version} - - - cn.seqdata.cloud - seqdata-cloud-auth-client - ${project.version} - + + org.springframework.boot + spring-boot-starter-amqp + + + org.springframework.boot + spring-boot-starter-data-redis + - - - - org.springframework.boot - spring-boot-starter-amqp - - + spring-boot-configuration-processor + true + + + + cn.seqdata.cloud + seqdata-cloud-log + ${project.version} + + + cn.seqdata.cloud + seqdata-cloud-auth-client + ${project.version} + + diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index 0814620..b862c32 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -8,10 +8,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import cn.seqdata.gateway.filter.authc.AllowlistPredicate; import cn.seqdata.gateway.filter.authc.PathMatcherGlobalFilter; +import cn.seqdata.gateway.filter.captcha.CaptchaGlobalFilter; import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; import cn.seqdata.log.LogRecorder; @@ -46,6 +48,11 @@ public class GatewayConfiguration { return new LoggingGlobalFilter(logRecorder, tokenServices); } + @Bean + public CaptchaGlobalFilter captchaGlobalFilter(GatewayProperties properties, StringRedisTemplate redisTemplate) { + return new CaptchaGlobalFilter(properties.getCaptcha(), redisTemplate); + } + @Bean public AllowlistPredicate allowlistPredicate(GatewayProperties properties) { return new AllowlistPredicate(properties.getIgnoring()); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java index 008755d..ee1a06a 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java @@ -4,6 +4,8 @@ import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; +import cn.seqdata.gateway.filter.captcha.CaptchaProperties; + /** * @author jrxian * @date 2020-11-24 15:37 @@ -13,4 +15,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("spring.cloud.gateway") public class GatewayProperties { private List ignoring; + private List captcha; } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AllowlistController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistController.java similarity index 92% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AllowlistController.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistController.java index c0ac63a..f2da643 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AllowlistController.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistController.java @@ -1,11 +1,9 @@ -package cn.seqdata.gateway.controller; +package cn.seqdata.gateway.filter.authc; import java.util.Collection; - -import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; -import cn.seqdata.gateway.filter.authc.AllowlistPredicate; +import org.springframework.web.bind.annotation.*; /** * @author jrxian diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AnonymousService.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousService.java similarity index 96% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AnonymousService.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousService.java index 2414477..39ac1e3 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/AnonymousService.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousService.java @@ -1,12 +1,12 @@ -package cn.seqdata.gateway.controller; +package cn.seqdata.gateway.filter.authc; import java.util.Collections; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; +import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.client.DefaultRole; import cn.seqdata.oauth2.client.FeignAuthzClient; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AuthClientConfiguration.java similarity index 95% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AuthClientConfiguration.java index 57e539f..84d20ac 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/AuthClientConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AuthClientConfiguration.java @@ -1,4 +1,4 @@ -package cn.seqdata.gateway; +package cn.seqdata.gateway.filter.authc; import java.io.IOException; @@ -15,8 +15,6 @@ import org.springframework.security.oauth2.provider.token.RemoteTokenServices; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; -import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; -import cn.seqdata.gateway.filter.ResourceServerProperties; import cn.seqdata.gateway.swagger.OAuth2ClientProperties; import cn.seqdata.oauth2.client.DefaultRole; import cn.seqdata.oauth2.client.FeignAuthzClient; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/BearerTokenResolver.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/BearerTokenResolver.java similarity index 83% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/BearerTokenResolver.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/BearerTokenResolver.java index b90210f..d7a786c 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/BearerTokenResolver.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/BearerTokenResolver.java @@ -1,4 +1,4 @@ -package cn.seqdata.gateway.filter; +package cn.seqdata.gateway.filter.authc; import org.springframework.http.server.reactive.ServerHttpRequest; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/GatewayController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java similarity index 68% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/GatewayController.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java index 143cd59..e20959e 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/controller/GatewayController.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java @@ -1,10 +1,11 @@ -package cn.seqdata.gateway.controller; +package cn.seqdata.gateway.filter.authc; -import org.springframework.web.bind.annotation.*; import lombok.AllArgsConstructor; -import cn.seqdata.gateway.filter.CachedResourceServerTokenServices; -import cn.seqdata.oauth2.client.FeignAuthzClient; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; /** * @author jrxian @@ -13,18 +14,22 @@ import cn.seqdata.oauth2.client.FeignAuthzClient; @RestController @RequestMapping("/cache") @AllArgsConstructor -public class GatewayController { - private final FeignAuthzClient authzClient; +public class CacheController { private final CachedResourceServerTokenServices tokenServices; + @DeleteMapping("/authentication") + public void invalidateAuthentications() { + tokenServices.authenticationCache.invalidateAll(); + } + @DeleteMapping("/authentication/{key}") public void invalidateAuthentication(@PathVariable("key") String key) { tokenServices.authenticationCache.invalidate(key); } - @DeleteMapping("/authentication") - public void invalidateAuthentications() { - tokenServices.authenticationCache.invalidateAll(); + @DeleteMapping("/token") + public void invalidateTokens() { + tokenServices.accessTokenCache.invalidateAll(); } @DeleteMapping("/token/{key}") @@ -32,8 +37,4 @@ public class GatewayController { tokenServices.accessTokenCache.invalidate(key); } - @PostMapping("/token") - public void invalidateTokens() { - tokenServices.accessTokenCache.invalidateAll(); - } } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CachedResourceServerTokenServices.java similarity index 98% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CachedResourceServerTokenServices.java index 7eee501..0e80a1b 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/CachedResourceServerTokenServices.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CachedResourceServerTokenServices.java @@ -1,7 +1,8 @@ -package cn.seqdata.gateway.filter; +package cn.seqdata.gateway.filter.authc; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import lombok.AllArgsConstructor; import org.springframework.lang.NonNull; import org.springframework.security.core.AuthenticationException; @@ -12,7 +13,6 @@ import org.springframework.security.oauth2.provider.token.ResourceServerTokenSer import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import lombok.AllArgsConstructor; /** * 缓存token,避免高频访问授权服务器 diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/DefaultBearerTokenResolver.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/DefaultBearerTokenResolver.java similarity index 96% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/DefaultBearerTokenResolver.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/DefaultBearerTokenResolver.java index 4a8b59d..11beeb6 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/DefaultBearerTokenResolver.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/DefaultBearerTokenResolver.java @@ -1,4 +1,4 @@ -package cn.seqdata.gateway.filter; +package cn.seqdata.gateway.filter.authc; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java index a037d2e..d7010f2 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java @@ -2,6 +2,7 @@ package cn.seqdata.gateway.filter.authc; import java.util.Objects; import java.util.function.Predicate; +import lombok.AllArgsConstructor; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; @@ -17,11 +18,8 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; -import lombok.AllArgsConstructor; import reactor.core.publisher.Mono; -import cn.seqdata.gateway.filter.BearerTokenResolver; -import cn.seqdata.gateway.filter.DefaultBearerTokenResolver; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; /** @@ -53,7 +51,7 @@ public class PathMatcherGlobalFilter implements GlobalFilter, Ordered { //从授权服务器获取Authentication OAuth2Authentication authentication = tokenServices.loadAuthentication(accessToken); - //由appid发起的,非前端页面访问 + //前端页面访问,不是由appid发起的 if(!authentication.isClientOnly()) { Authentication userAuthentication = authentication.getUserAuthentication(); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/ResourceServerProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/ResourceServerProperties.java similarity index 89% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/ResourceServerProperties.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/ResourceServerProperties.java index ab9f3d7..91d4c1b 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/ResourceServerProperties.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/ResourceServerProperties.java @@ -1,4 +1,4 @@ -package cn.seqdata.gateway.filter; +package cn.seqdata.gateway.filter.authc; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java new file mode 100644 index 0000000..2672135 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java @@ -0,0 +1,50 @@ +package cn.seqdata.gateway.filter.captcha; + +import java.io.ByteArrayOutputStream; + +import org.apache.commons.lang3.RandomUtils; +import org.springframework.http.CacheControl; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import com.wf.captcha.base.Captcha; + +/** + * @author jrxian + * @date 2020-11-25 11:12 + */ +@RestController +@RequestMapping("/captcha") +public class CaptchaController { + + @GetMapping + public ResponseEntity captcha(String type, String uuid + , @RequestParam(defaultValue = "130") int width + , @RequestParam(defaultValue = "48") int height + , @RequestParam(defaultValue = "4") int len) { + + CaptchaType captchaType; + try { + captchaType = CaptchaType.valueOf(type); + } catch(RuntimeException ex) { + CaptchaType[] captchaTypes = CaptchaType.values(); + captchaType = captchaTypes[RandomUtils.nextInt(0, captchaTypes.length)]; + } + + Captcha captcha = captchaType.apply(width, height, len); + System.out.println(captcha.text()); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + captcha.out(os); + + return ResponseEntity.ok() + .contentType(MediaType.IMAGE_GIF) + .cacheControl(CacheControl.noCache()) + .header(HttpHeaders.PRAGMA, "No-cache") + .header(HttpHeaders.EXPIRES, "0") + .body(os.toByteArray()); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java new file mode 100644 index 0000000..4992942 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java @@ -0,0 +1,23 @@ +package cn.seqdata.gateway.filter.captcha; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +/** + * @author jrxian + * @date 2020-11-26 14:45 + */ +public class CaptchaException extends ResponseStatusException { + + public CaptchaException() { + super(HttpStatus.PRECONDITION_REQUIRED); + } + + public CaptchaException(HttpStatus status) { + super(status); + } + + public CaptchaException(HttpStatus status, String reason) { + super(status, reason); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaFunction.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaFunction.java new file mode 100644 index 0000000..acccb0a --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaFunction.java @@ -0,0 +1,12 @@ +package cn.seqdata.gateway.filter.captcha; + +import com.wf.captcha.base.Captcha; + +/** + * @author jrxian + * @date 2020-11-25 12:18 + */ +@FunctionalInterface +interface CaptchaFunction { + Captcha apply(int w, int h, int l); +} \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java new file mode 100644 index 0000000..d599961 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java @@ -0,0 +1,147 @@ +package cn.seqdata.gateway.filter.captcha; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.codec.Charsets; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.server.PathContainer; +import org.springframework.http.server.RequestPath; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.CollectionUtils; +import org.springframework.web.server.ServerWebExchange; +import com.google.common.hash.Hashing; +import reactor.core.publisher.Mono; + +/** + * @author jrxian + * @date 2020-11-24 17:11 + */ +public class CaptchaGlobalFilter implements GlobalFilter, Ordered { + public static final String xCaptcha = "X-Captcha-"; + //挑战 + public static final String xCaptchaChallenge = xCaptcha + "Challenge"; + //响应 + public static final String xCaptchaResponse = xCaptcha + "Response"; + + private final AntPathMatcher pathMatcher = new AntPathMatcher(); + private final List properties; + private final StringRedisTemplate redisTemplate; + private final ValueOperations opsForValue; + + public CaptchaGlobalFilter(List properties, StringRedisTemplate redisTemplate) { + this.properties = properties; + this.redisTemplate = redisTemplate; + this.opsForValue = redisTemplate.opsForValue(); + } + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + doCaptchaFilters(exchange); + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return -80; + } + + private void doCaptchaFilters(ServerWebExchange exchange) { + ServerHttpRequest request = exchange.getRequest(); + ServerHttpResponse response = exchange.getResponse(); + + //只针对GET方法 + if(!HttpMethod.GET.equals(request.getMethod())) { + return; + } + //没有规则 + if(CollectionUtils.isEmpty(properties)) { + return; + } + + //从所有的检查列表中依次检查 + String path = path(request); + for(CaptchaProperties property : properties) { + if(pathMatcher.match(property.getPath(), path)) { + doCaptchaFilter(request, property, path); + break;//只要有任何一个符合条件,检查后直接退出 + } + } + } + + private void doCaptchaFilter(ServerHttpRequest request, CaptchaProperties captcha, String path) { + byte[] address = address(request); + String key = key(address, path); + + //检查是否需要输入验证码,计数器+1,定时器复位 + Long hits = opsForValue.increment(key); + redisTemplate.expire(key, captcha.getSeconds(), TimeUnit.SECONDS); + + //计数器达到阈值 + if(Objects.isNull(hits) || hits < captcha.getHits()) { + return; + } + + //不包含验证码,挑战失败 + HttpHeaders headers = request.getHeaders(); + String challenge = headers.getFirst(xCaptchaChallenge); + String response = headers.getFirst(xCaptchaResponse); + if(Objects.isNull(challenge) || Objects.isNull(response)) { + throw new CaptchaException(); + } + + //回答必须和标准答案一致 + String answer = opsForValue.get(challenge); + if(!StringUtils.equalsIgnoreCase(answer, response)) { + throw new CaptchaException(); + } + } + + /** + * 请求路径 + */ + private String path(ServerHttpRequest request) { + return Optional.of(request) + .map(ServerHttpRequest::getPath) + .map(RequestPath::contextPath) + .map(PathContainer::value) + .orElse(null); + } + + /** + * 客户端地址 + */ + private byte[] address(ServerHttpRequest request) { + return Optional.of(request) + .map(ServerHttpRequest::getRemoteAddress) + .map(InetSocketAddress::getAddress) + .map(InetAddress::getAddress) + .orElse(ArrayUtils.EMPTY_BYTE_ARRAY); + } + + /** + * 用于决定是否检查的redisKey,由hash(remoteAddress+path)计算而得 + */ + private String key(byte[] address, String path) { + return Hashing.murmur3_32() + .newHasher() + .putBytes(address) + .putString(path, Charsets.ISO_8859_1) + .hash() + .toString(); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java new file mode 100644 index 0000000..d893ddb --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java @@ -0,0 +1,13 @@ +package cn.seqdata.gateway.filter.captcha; + +/** + * @author jrxian + * @date 2020-11-25 19:07 + */ +@lombok.Getter +@lombok.Setter +public class CaptchaProperties { + private String path; + private int hits; + private int seconds; +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaType.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaType.java new file mode 100644 index 0000000..2dd430d --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaType.java @@ -0,0 +1,41 @@ +package cn.seqdata.gateway.filter.captcha; + +import com.wf.captcha.*; +import com.wf.captcha.base.Captcha; + +/** + * @author jrxian + * @date 2020-11-25 11:50 + */ +public enum CaptchaType implements CaptchaFunction { + spec { + @Override + public Captcha apply(int w, int h, int l) { + return new SpecCaptcha(w, h, l); + } + }, + gif { + @Override + public Captcha apply(int w, int h, int l) { + return new GifCaptcha(w, h, l); + } + }, + chinese { + @Override + public Captcha apply(int w, int h, int l) { + return new ChineseCaptcha(w, h, l / 2); + } + }, + chinesegif { + @Override + public Captcha apply(int w, int h, int l) { + return new ChineseGifCaptcha(w, h, l / 2); + } + }, + arithmetic { + @Override + public Captcha apply(int w, int h, int l) { + return new ArithmeticCaptcha(w, h, l / 2); + } + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java index 76e2d62..850a9a2 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -3,6 +3,8 @@ package cn.seqdata.gateway.filter.logging; import java.net.InetSocketAddress; import java.util.Objects; import java.util.StringJoiner; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; @@ -15,12 +17,10 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Mono; -import cn.seqdata.gateway.filter.BearerTokenResolver; -import cn.seqdata.gateway.filter.DefaultBearerTokenResolver; +import cn.seqdata.gateway.filter.authc.BearerTokenResolver; +import cn.seqdata.gateway.filter.authc.DefaultBearerTokenResolver; import cn.seqdata.log.LogRecorder; import cn.seqdata.log.url.UrlRecord; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java index 3eece88..079fcba 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/verify/HmacVerifyGlobalFilter.java @@ -50,35 +50,6 @@ public class HmacVerifyGlobalFilter implements GlobalFilter, Ordered { this.secret = secret; } - private static String hmac(String algorithm, byte[] secret, String path, - Map headers, MultiValueMap queries) { - Mac mac = HmacUtils.getInitializedMac(algorithm, secret); - - HmacUtils.updateHmac(mac, path); - updateHmac(mac, headers); - updateHmac(mac, queries); - - return Base64.encodeBase64String(mac.doFinal()); - } - - private static void updateHmac(Mac mac, Collection values) { - new TreeSet<>(values).forEach(v -> HmacUtils.updateHmac(mac, v)); - } - - private static void updateHmac(Mac mac, Map values) { - new TreeMap<>(values).forEach((k, v) -> { - HmacUtils.updateHmac(mac, k); - HmacUtils.updateHmac(mac, v); - }); - } - - private static void updateHmac(Mac mac, MultiValueMap values) { - new TreeMap<>(values).forEach((k, v) -> { - HmacUtils.updateHmac(mac, k); - updateHmac(mac, v); - }); - } - @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); @@ -107,4 +78,33 @@ public class HmacVerifyGlobalFilter implements GlobalFilter, Ordered { public int getOrder() { return -80; } + + private static String hmac(String algorithm, byte[] secret, String path, + Map headers, MultiValueMap queries) { + Mac mac = HmacUtils.getInitializedMac(algorithm, secret); + + HmacUtils.updateHmac(mac, path); + updateHmac(mac, headers); + updateHmac(mac, queries); + + return Base64.encodeBase64String(mac.doFinal()); + } + + private static void updateHmac(Mac mac, Collection values) { + new TreeSet<>(values).forEach(v -> HmacUtils.updateHmac(mac, v)); + } + + private static void updateHmac(Mac mac, Map values) { + new TreeMap<>(values).forEach((k, v) -> { + HmacUtils.updateHmac(mac, k); + HmacUtils.updateHmac(mac, v); + }); + } + + private static void updateHmac(Mac mac, MultiValueMap values) { + new TreeMap<>(values).forEach((k, v) -> { + HmacUtils.updateHmac(mac, k); + updateHmac(mac, v); + }); + } } diff --git a/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 6df1ec8..21f8615 100644 --- a/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,5 +1,10 @@ { "properties": [ + { + "name": "spring.cloud.gateway.ignoring", + "type": "java.lang.String", + "description": "Description for spring.cloud.gateway.ignoring." + }, { "name": "spring.cloud.gateway.logging.enabled", "type": "java.lang.String", @@ -14,11 +19,6 @@ "name": "spring.cloud.gateway.hmac-verify.enabled", "type": "java.lang.String", "description": "Description for spring.cloud.gateway.hmac-verify.enabled." - }, - { - "name": "spring.cloud.gateway.ignoring", - "type": "java.lang.String", - "description": "Description for spring.cloud.gateway.ignoring." } ] } \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 3ce33dc..c1e6972 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -12,6 +12,15 @@ spring: allowed-origins: "*" allowed-headers: "*" allowed-methods: GET, PUT, POST, PATCH, DELETE + logging: + enabled: true + path-matcher: + enabled: true + hmac-verify: + enabled: false + ignoring: + - /authc/** + - /authz/** security: oauth2: client: diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index bc8aa47..704d5dc 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -4,15 +4,6 @@ spring: discovery: locator: enabled: true - logging: - enabled: true - path-matcher: - enabled: true - hmac-verify: - enabled: false - ignoring: - - /authc/** - - /authz/** nacos: discovery: server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} -- Gitee From 6429cbd2913c07989c3958918ef391214d664873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 26 Nov 2020 16:18:18 +0800 Subject: [PATCH 109/228] =?UTF-8?q?=E8=B0=83=E6=95=B4ignoring=E5=9C=A8prop?= =?UTF-8?q?erties=E4=B8=AD=E7=9A=84=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 6 ++++++ .../seqdata/gateway/GatewayConfiguration.java | 12 ++++++----- .../filter/authc/AllowlistPredicate.java | 21 ++++++++++++------- .../authc/IgnoringProperties.java} | 14 +++++-------- .../filter/captcha/CaptchaGlobalFilter.java | 8 +++---- .../gateway/filter/captcha/CaptchaItem.java | 13 ++++++++++++ .../filter/captcha/CaptchaProperties.java | 14 ++++++------- ...itional-spring-configuration-metadata.json | 5 +++++ .../src/main/resources/application.yml | 8 +++---- 9 files changed, 65 insertions(+), 36 deletions(-) rename seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/{GatewayProperties.java => filter/authc/IgnoringProperties.java} (31%) create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaItem.java diff --git a/seqdata-cloud-authc/src/main/resources/application.yml b/seqdata-cloud-authc/src/main/resources/application.yml index e930461..fab68ae 100644 --- a/seqdata-cloud-authc/src/main/resources/application.yml +++ b/seqdata-cloud-authc/src/main/resources/application.yml @@ -14,6 +14,12 @@ security: prefer-token-info: false token-info-uri: http://authz/oauth/check_token user-info-uri: http://authz/user/info + ignoring: + - /**/*.html + - /**/*.js + permit-all: + - /type/** + - /code/** client: client-id: client client-secret: secret diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index b862c32..0a8961c 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -12,8 +12,10 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; import cn.seqdata.gateway.filter.authc.AllowlistPredicate; +import cn.seqdata.gateway.filter.authc.IgnoringProperties; import cn.seqdata.gateway.filter.authc.PathMatcherGlobalFilter; import cn.seqdata.gateway.filter.captcha.CaptchaGlobalFilter; +import cn.seqdata.gateway.filter.captcha.CaptchaProperties; import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; import cn.seqdata.log.LogRecorder; @@ -27,7 +29,7 @@ import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; */ @Slf4j @Configuration -@EnableConfigurationProperties(GatewayProperties.class) +@EnableConfigurationProperties({IgnoringProperties.class, CaptchaProperties.class}) public class GatewayConfiguration { @Bean @@ -49,13 +51,13 @@ public class GatewayConfiguration { } @Bean - public CaptchaGlobalFilter captchaGlobalFilter(GatewayProperties properties, StringRedisTemplate redisTemplate) { - return new CaptchaGlobalFilter(properties.getCaptcha(), redisTemplate); + public CaptchaGlobalFilter captchaGlobalFilter(CaptchaProperties properties, StringRedisTemplate redisTemplate) { + return new CaptchaGlobalFilter(properties, redisTemplate); } @Bean - public AllowlistPredicate allowlistPredicate(GatewayProperties properties) { - return new AllowlistPredicate(properties.getIgnoring()); + public AllowlistPredicate allowlistPredicate(IgnoringProperties properties) { + return new AllowlistPredicate(properties); } @Bean diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java index d146244..65da015 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java @@ -6,9 +6,11 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Predicate; +import org.apache.commons.lang3.ArrayUtils; import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.AntPathMatcher; +import org.springframework.util.CollectionUtils; import org.springframework.util.PathMatcher; /** @@ -17,24 +19,29 @@ import org.springframework.util.PathMatcher; */ public class AllowlistPredicate implements Predicate { protected static final PathMatcher pathMatcher = new AntPathMatcher(); + private static final String[] defAllowlist = {"/authz/**" + , "/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/v2/api-docs/**"}; public final Set allowlist = new HashSet<>(); public final Set anonymous = new HashSet<>(); public AllowlistPredicate() { //authz: 授权服务器是否需要token由授权服务器决定,网关不做前置拦截 - allowlist.addAll(Arrays.asList("/authz/**", "/**/anonymous/**" - , "/favicon.ico", "/webjars/**" - , "/swagger-ui.html", "/swagger-resources/**", "/*/v2/api-docs/**")); + allowlist.addAll(Arrays.asList(defAllowlist)); } - public AllowlistPredicate(Collection allowlist) { + public AllowlistPredicate(String... allowlist) { this(); - this.allowlist.addAll(allowlist); + if(ArrayUtils.isNotEmpty(allowlist)) { + this.allowlist.addAll(Arrays.asList(allowlist)); + } } - public AllowlistPredicate(String... allowlist) { - this(Arrays.asList(allowlist)); + public AllowlistPredicate(Collection allowlist) { + this(); + if(!CollectionUtils.isEmpty(allowlist)) { + this.allowlist.addAll(allowlist); + } } @Override diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/IgnoringProperties.java similarity index 31% rename from seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java rename to seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/IgnoringProperties.java index ee1a06a..6d41ea9 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayProperties.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/IgnoringProperties.java @@ -1,19 +1,15 @@ -package cn.seqdata.gateway; +package cn.seqdata.gateway.filter.authc; -import java.util.List; +import java.util.ArrayList; import org.springframework.boot.context.properties.ConfigurationProperties; -import cn.seqdata.gateway.filter.captcha.CaptchaProperties; - /** * @author jrxian - * @date 2020-11-24 15:37 + * @date 2020-11-26 15:38 */ @lombok.Getter @lombok.Setter -@ConfigurationProperties("spring.cloud.gateway") -public class GatewayProperties { - private List ignoring; - private List captcha; +@ConfigurationProperties(prefix = "security.oauth2.resource.ignoring") +public class IgnoringProperties extends ArrayList { } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java index d599961..5c3082c 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java @@ -39,11 +39,11 @@ public class CaptchaGlobalFilter implements GlobalFilter, Ordered { public static final String xCaptchaResponse = xCaptcha + "Response"; private final AntPathMatcher pathMatcher = new AntPathMatcher(); - private final List properties; + private final List properties; private final StringRedisTemplate redisTemplate; private final ValueOperations opsForValue; - public CaptchaGlobalFilter(List properties, StringRedisTemplate redisTemplate) { + public CaptchaGlobalFilter(List properties, StringRedisTemplate redisTemplate) { this.properties = properties; this.redisTemplate = redisTemplate; this.opsForValue = redisTemplate.opsForValue(); @@ -75,7 +75,7 @@ public class CaptchaGlobalFilter implements GlobalFilter, Ordered { //从所有的检查列表中依次检查 String path = path(request); - for(CaptchaProperties property : properties) { + for(CaptchaItem property : properties) { if(pathMatcher.match(property.getPath(), path)) { doCaptchaFilter(request, property, path); break;//只要有任何一个符合条件,检查后直接退出 @@ -83,7 +83,7 @@ public class CaptchaGlobalFilter implements GlobalFilter, Ordered { } } - private void doCaptchaFilter(ServerHttpRequest request, CaptchaProperties captcha, String path) { + private void doCaptchaFilter(ServerHttpRequest request, CaptchaItem captcha, String path) { byte[] address = address(request); String key = key(address, path); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaItem.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaItem.java new file mode 100644 index 0000000..b3087d5 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaItem.java @@ -0,0 +1,13 @@ +package cn.seqdata.gateway.filter.captcha; + +/** + * @author jrxian + * @date 2020-11-25 19:07 + */ +@lombok.Getter +@lombok.Setter +public class CaptchaItem { + private String path; + private int hits; + private int seconds; +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java index d893ddb..8306eb6 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaProperties.java @@ -1,13 +1,13 @@ package cn.seqdata.gateway.filter.captcha; +import java.util.LinkedList; + +import org.springframework.boot.context.properties.ConfigurationProperties; + /** * @author jrxian - * @date 2020-11-25 19:07 + * @date 2020-11-26 15:43 */ -@lombok.Getter -@lombok.Setter -public class CaptchaProperties { - private String path; - private int hits; - private int seconds; +@ConfigurationProperties(prefix = "captcha") +public class CaptchaProperties extends LinkedList { } diff --git a/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 21f8615..3c6fecc 100644 --- a/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/seqdata-cloud-gateway/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -19,6 +19,11 @@ "name": "spring.cloud.gateway.hmac-verify.enabled", "type": "java.lang.String", "description": "Description for spring.cloud.gateway.hmac-verify.enabled." + }, + { + "name": "security.oauth2.resource.ignoring", + "type": "java.lang.String", + "description": "Description for security.oauth2.resource.ignoring." } ] } \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index c1e6972..d98a20c 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -18,9 +18,6 @@ spring: enabled: true hmac-verify: enabled: false - ignoring: - - /authc/** - - /authz/** security: oauth2: client: @@ -28,4 +25,7 @@ security: client-secret: secret resource: token-info-uri: http://authz/oauth/check_token - prefer-token-info: true \ No newline at end of file + prefer-token-info: true + ignoring: + - /authc/** + - /authz/** \ No newline at end of file -- Gitee From 2352a5c789b4b78e0e9069d8562ac391524077d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 27 Nov 2020 14:13:36 +0800 Subject: [PATCH 110/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9nacos=E7=9A=84yml?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 12 ++++++++++++ .../src/main/resources/bootstrap.yml | 14 +++++++++----- .../cn/seqdata/gateway/GatewayConfiguration.java | 4 ++-- .../gateway/filter/captcha/CaptchaController.java | 13 ++++++++++++- .../filter/captcha/CaptchaGlobalFilter.java | 2 -- .../filter/logging/LoggingGlobalFilter.java | 2 +- .../src/main/resources/bootstrap.yml | 6 +++++- 7 files changed, 41 insertions(+), 12 deletions(-) diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 34df85b..19f730f 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -63,6 +63,12 @@ ${project.version} + + com.alibaba + druid-spring-boot-starter + 1.2.3 + runtime + com.microsoft.sqlserver mssql-jdbc @@ -73,5 +79,11 @@ mysql-connector-java runtime + + com.oracle + ojdbc6 + 11.2.0.3 + runtime + diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index 2e5a3be..571aeaf 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -1,16 +1,20 @@ spring: cloud: - gateway: - discovery: - locator: - enabled: true nacos: discovery: server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} - group: ${NACOS_GROUP:DEFAULT_GROUP} cluster-name: ${NACOS_CLUSTER_NAME:DEFAULT} + namespace: ${NACOS_NAMESPACE:public} + group: ${NACOS_GROUP:DEFAULT_GROUP} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} + cluster-name: ${spring.cloud.nacos.discovery.cluster-name} + namespace: ${spring.cloud.nacos.discovery.cluster-name} + group: ${spring.cloud.nacos.discovery.group} file-extension: ${NACOS_FILE_EXT:yml} + shared-configs: + - data_id: common.${spring.cloud.nacos.config.file-extension} + group: ${spring.cloud.nacos.discovery.group} + refresh: true server: port: 0 diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index 0a8961c..d06d3a9 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -19,8 +19,8 @@ import cn.seqdata.gateway.filter.captcha.CaptchaProperties; import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; import cn.seqdata.log.LogRecorder; -import cn.seqdata.log.RabbitRecorder; -import cn.seqdata.log.url.UrlRecord; +import cn.seqdata.log.UrlRecord; +import cn.seqdata.log.rabbit.RabbitRecorder; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; /** diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java index 2672135..1fdf971 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaController.java @@ -1,8 +1,11 @@ package cn.seqdata.gateway.filter.captcha; import java.io.ByteArrayOutputStream; +import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.RandomUtils; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import org.springframework.http.CacheControl; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -20,6 +23,13 @@ import com.wf.captcha.base.Captcha; @RestController @RequestMapping("/captcha") public class CaptchaController { + private final StringRedisTemplate redisTemplate; + private final ValueOperations opsForValue; + + public CaptchaController(StringRedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + this.opsForValue = redisTemplate.opsForValue(); + } @GetMapping public ResponseEntity captcha(String type, String uuid @@ -36,7 +46,8 @@ public class CaptchaController { } Captcha captcha = captchaType.apply(width, height, len); - System.out.println(captcha.text()); + opsForValue.set(uuid, captcha.text()); + redisTemplate.expire(uuid, 1, TimeUnit.MINUTES); ByteArrayOutputStream os = new ByteArrayOutputStream(); captcha.out(os); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java index 5c3082c..aa42fbd 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java @@ -18,7 +18,6 @@ import org.springframework.data.redis.core.ValueOperations; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.server.PathContainer; -import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.AntPathMatcher; @@ -117,7 +116,6 @@ public class CaptchaGlobalFilter implements GlobalFilter, Ordered { private String path(ServerHttpRequest request) { return Optional.of(request) .map(ServerHttpRequest::getPath) - .map(RequestPath::contextPath) .map(PathContainer::value) .orElse(null); } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java index 850a9a2..379c383 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -22,7 +22,7 @@ import reactor.core.publisher.Mono; import cn.seqdata.gateway.filter.authc.BearerTokenResolver; import cn.seqdata.gateway.filter.authc.DefaultBearerTokenResolver; import cn.seqdata.log.LogRecorder; -import cn.seqdata.log.url.UrlRecord; +import cn.seqdata.log.UrlRecord; /** * Author: jrxian diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index 704d5dc..913be9f 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -7,8 +7,12 @@ spring: nacos: discovery: server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} - group: ${NACOS_GROUP:DEFAULT_GROUP} cluster-name: ${NACOS_CLUSTER_NAME:DEFAULT} + namespace: ${NACOS_NAMESPACE:public} + group: ${NACOS_GROUP:DEFAULT_GROUP} config: server-addr: ${spring.cloud.nacos.discovery.server-addr} + cluster-name: ${spring.cloud.nacos.discovery.cluster-name} + namespace: ${spring.cloud.nacos.discovery.cluster-name} + group: ${spring.cloud.nacos.discovery.group} file-extension: ${NACOS_FILE_EXT:yml} \ No newline at end of file -- Gitee From 5f671dee771f95ad8eba60c1f264471ee2c8854a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 27 Nov 2020 14:41:45 +0800 Subject: [PATCH 111/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9nacos=E7=9A=84yml?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/src/main/resources/application.yml | 2 +- seqdata-cloud-authz/src/main/resources/bootstrap.yml | 7 +++---- seqdata-cloud-gateway/src/main/resources/application.yml | 2 +- seqdata-cloud-gateway/src/main/resources/bootstrap.yml | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index ca8c9fb..8416381 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -2,7 +2,7 @@ spring: application: name: authz profiles: - active: jackson, jpa, log + active: logging, jackson, jpa datasource: driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://dev.voltmao.com:1433;DatabaseName=data_space_base diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index 571aeaf..0e669a4 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -9,12 +9,11 @@ spring: config: server-addr: ${spring.cloud.nacos.discovery.server-addr} cluster-name: ${spring.cloud.nacos.discovery.cluster-name} - namespace: ${spring.cloud.nacos.discovery.cluster-name} + namespace: ${spring.cloud.nacos.discovery.namespace} group: ${spring.cloud.nacos.discovery.group} file-extension: ${NACOS_FILE_EXT:yml} shared-configs: - - data_id: common.${spring.cloud.nacos.config.file-extension} - group: ${spring.cloud.nacos.discovery.group} - refresh: true + - group: ${spring.cloud.nacos.discovery.group} + data_id: common.${spring.cloud.nacos.config.file-extension} server: port: 0 diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index d98a20c..0db7b67 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -2,7 +2,7 @@ spring: application: name: gateway profiles: - active: jackson, log + active: logging, jackson cloud: gateway: globalcors: diff --git a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml index 913be9f..469b3a2 100644 --- a/seqdata-cloud-gateway/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-gateway/src/main/resources/bootstrap.yml @@ -13,6 +13,6 @@ spring: config: server-addr: ${spring.cloud.nacos.discovery.server-addr} cluster-name: ${spring.cloud.nacos.discovery.cluster-name} - namespace: ${spring.cloud.nacos.discovery.cluster-name} + namespace: ${spring.cloud.nacos.discovery.namespace} group: ${spring.cloud.nacos.discovery.group} file-extension: ${NACOS_FILE_EXT:yml} \ No newline at end of file -- Gitee From 40a58e1d1786f00ecb26de7e7398b2382f40cf69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 27 Nov 2020 16:23:03 +0800 Subject: [PATCH 112/228] =?UTF-8?q?=E8=B0=83=E6=95=B4SecurityUtils?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/util/SecurityUtils.java | 3 +- .../oauth2/util/ServiceSecurityUtils.java | 61 ------------------- 2 files changed, 2 insertions(+), 62 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index 26fb8cb..e32818c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -106,7 +106,8 @@ public final class SecurityUtils { } private static Object details(Principal principal) { - return Optional.ofNullable((OAuth2Authentication) principal) + return Optional.ofNullable(principal) + .map(new CastFunction<>(OAuth2Authentication.class)) .map(OAuth2Authentication::getUserAuthentication) .map(Authentication::getDetails) .orElse(null); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java deleted file mode 100644 index 9f035a9..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/ServiceSecurityUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.seqdata.oauth2.util; - -import java.util.Collection; -import java.util.Set; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * @author jrxian - * @date 2020-11-25 15:11 - * 用于@Service层面获取当前登录用户基本信息,不需要参数Principal - */ -public final class ServiceSecurityUtils { - - private ServiceSecurityUtils() { - } - - public static Authentication principal() { - SecurityContext context = SecurityContextHolder.getContext(); - return context.getAuthentication(); - } - - public static String accessToken() { - return SecurityUtils.accessToken(principal()); - } - - public static String clientId() { - return SecurityUtils.accessToken(principal()); - } - - public static String username() { - return SecurityUtils.username(principal()); - } - - public static Long userId() { - return SecurityUtils.userId(principal()); - } - - public static Long deptId() { - return SecurityUtils.deptId(principal()); - } - - public static Long orgId() { - return SecurityUtils.orgId(principal()); - } - - public static boolean isSysAdmin() { - return SecurityUtils.isSysAdmin(principal()); - } - - public static Collection grantedAuthorities() { - return SecurityUtils.grantedAuthorities(principal()); - } - - public static Set authorities() { - return SecurityUtils.authorities(principal()); - } -} -- Gitee From 939c6b8e1cf20b2e9e67a4c76f539ce071049342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 27 Nov 2020 16:36:55 +0800 Subject: [PATCH 113/228] =?UTF-8?q?SecurityUtils=E6=94=AF=E6=8C=81name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/util/SecurityUtils.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java index e32818c..0574f7a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java @@ -53,6 +53,15 @@ public final class SecurityUtils { .orElse(null); } + /** + * 用户名 + */ + public static String name(Principal principal) { + return Optional.ofNullable(detailValue(details(principal), OAuth2Constants.NAME)) + .map(Object::toString) + .orElse(null); + } + /** * 用户编号 */ -- Gitee From ade1e67687494bbf499a47997215476229a446b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 28 Nov 2020 10:35:56 +0800 Subject: [PATCH 114/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E7=9A=84=E5=BC=BA=E5=BA=A6=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/WebSecurityConfiguration.java | 3 +- .../cn/seqdata/oauth2/util/SecurityUtils.java | 142 ------------------ .../gateway/filter/authc/CacheController.java | 11 +- 3 files changed, 7 insertions(+), 149 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java index 86e8f1c..eb1186e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -35,6 +35,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); + AuthenticationManager authenticationManager = super.authenticationManagerBean(); + return authenticationManager; } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java deleted file mode 100644 index 0574f7a..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/util/SecurityUtils.java +++ /dev/null @@ -1,142 +0,0 @@ -package cn.seqdata.oauth2.util; - -import java.security.Principal; -import java.util.*; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; - -import cn.seqdata.core.util.CastFunction; -import cn.seqdata.oauth2.DefaultRole; -import cn.seqdata.oauth2.OAuth2Constants; - -/** - * Author: jrxian - * Date: 2020-01-27 01:59 - */ -public final class SecurityUtils { - - private SecurityUtils() { - } - - /** - * 根据用户获取oauth2的token - */ - public static String accessToken(Principal principal) { - return Optional.ofNullable(principal) - .map(new CastFunction<>(OAuth2Authentication.class)) - .map(OAuth2Authentication::getDetails) - .map(new CastFunction<>(OAuth2AuthenticationDetails.class)) - .map(OAuth2AuthenticationDetails::getTokenValue) - .orElse(null); - } - - public static String clientId(Principal principal) { - return Optional.ofNullable(principal) - .map(new CastFunction<>(OAuth2Authentication.class)) - .map(OAuth2Authentication::getOAuth2Request) - .map(OAuth2Request::getClientId) - .orElse(null); - } - - /** - * 用户名 - */ - public static String username(Principal principal) { - return Optional.ofNullable(principal) - .map(Principal::getName) - .orElse(null); - } - - /** - * 用户名 - */ - public static String name(Principal principal) { - return Optional.ofNullable(detailValue(details(principal), OAuth2Constants.NAME)) - .map(Object::toString) - .orElse(null); - } - - /** - * 用户编号 - */ - public static Long userId(Principal principal) { - return toLong(detailValue(details(principal), OAuth2Constants.USER_ID)); - } - - /** - * 所属部门编号 - */ - public static Long deptId(Principal principal) { - return toLong(detailValue(details(principal), OAuth2Constants.DEPT_ID)); - } - - /** - * 所属单位编号 - */ - public static Long orgId(Principal principal) { - return toLong(detailValue(details(principal), OAuth2Constants.ORG_ID)); - } - - public static boolean isSysAdmin(Principal principal) { - return authorities(principal).contains(DefaultRole.sysadmin.name()); - } - - /** - * 拥有角色 - */ - public static Collection grantedAuthorities(Principal principal) { - return Optional.ofNullable((OAuth2Authentication) principal) - .map(OAuth2Authentication::getUserAuthentication) - .map(Authentication::getAuthorities) - .orElse(Collections.emptySet()); - } - - /** - * 拥有角色名称 - */ - public static Set authorities(Principal principal) { - return mapAuthorities(grantedAuthorities(principal)); - } - - /** - * 所有角色全部转换为小写 - */ - private static Set mapAuthorities(Collection authorities) { - return authorities.stream() - .map(GrantedAuthority::getAuthority) - .map(String::toLowerCase) - .collect(Collectors.toSet()); - } - - private static Object details(Principal principal) { - return Optional.ofNullable(principal) - .map(new CastFunction<>(OAuth2Authentication.class)) - .map(OAuth2Authentication::getUserAuthentication) - .map(Authentication::getDetails) - .orElse(null); - } - - private static Object detailValue(Object details, String key) { - if(details instanceof Map) { - return ((Map) details).get(key); - } else { - return null; - } - } - - private static Long toLong(Object value) { - if(Objects.isNull(value)) return null; - - if(value instanceof Number) { - return ((Number) value).longValue(); - } else { - return NumberUtils.toLong(String.valueOf(value)); - } - } -} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java index e20959e..490f7d7 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java @@ -2,9 +2,9 @@ package cn.seqdata.gateway.filter.authc; import lombok.AllArgsConstructor; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** @@ -17,24 +17,23 @@ import org.springframework.web.bind.annotation.RestController; public class CacheController { private final CachedResourceServerTokenServices tokenServices; - @DeleteMapping("/authentication") + @RequestMapping(value = "/authentication", method = {RequestMethod.GET, RequestMethod.DELETE}) public void invalidateAuthentications() { tokenServices.authenticationCache.invalidateAll(); } - @DeleteMapping("/authentication/{key}") + @RequestMapping(value = "/authentication/{key}", method = {RequestMethod.GET, RequestMethod.DELETE}) public void invalidateAuthentication(@PathVariable("key") String key) { tokenServices.authenticationCache.invalidate(key); } - @DeleteMapping("/token") + @RequestMapping(value = "/token", method = {RequestMethod.GET, RequestMethod.DELETE}) public void invalidateTokens() { tokenServices.accessTokenCache.invalidateAll(); } - @DeleteMapping("/token/{key}") + @RequestMapping(value = "/token/{key}", method = {RequestMethod.GET, RequestMethod.DELETE}) public void invalidateToken(@PathVariable("key") String key) { tokenServices.accessTokenCache.invalidate(key); } - } -- Gitee From 5f9c03fd1cbf7dc4f851a5020a8ab623741c4baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 28 Nov 2020 14:39:43 +0800 Subject: [PATCH 115/228] =?UTF-8?q?=E8=B0=83=E8=AF=95=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 12 +++- .../oauth2/controller/CurrentController.java | 3 +- .../oauth2/controller/PasswordController.java | 69 +++++++++++++++++++ .../filter/authc/AuthClientConfiguration.java | 3 +- .../gateway/filter/authc/CacheController.java | 43 +++++++++--- 5 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 19f730f..e045478 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -80,9 +80,15 @@ runtime - com.oracle - ojdbc6 - 11.2.0.3 + com.oracle.ojdbc + ojdbc8 + 19.3.0.0 + runtime + + + com.oracle.database.nls + orai18n + 19.3.0.0 runtime diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index a02afd1..89aa7c2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.Objects; import java.util.Optional; import java.util.function.Function; +import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -27,7 +28,6 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; /** * Author: jrxian @@ -134,6 +134,7 @@ public class CurrentController { /** * 修改密码 */ + @Deprecated @PatchMapping(value = "/password") public void updatePassword(Principal principal, @RequestBody UserAccountParam param) throws BindException { UserAccount entity = getUserAccount(principal); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java new file mode 100644 index 0000000..e85444f --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -0,0 +1,69 @@ +package cn.seqdata.oauth2.controller; + +import java.security.Principal; +import lombok.AllArgsConstructor; + +import io.swagger.annotations.ApiOperation; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsPasswordService; +import org.springframework.security.provisioning.UserDetailsManager; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; + +import cn.seqdata.oauth2.util.SecurityUtils; + +/** + * @author jrxian + * @date 2020/11/28 11:07 + */ +@RestController +@RequestMapping("/password") +@AllArgsConstructor +public class PasswordController { + private final AuthenticationManager authenticationManager; + private final UserDetailsManager userDetailsManager; + private final UserDetailsPasswordService passwordService; + + @ApiOperation("用户登录后修改自己的密码") + @PostMapping("/self") + public void changePassword(String oldPassword, String newPassword) { + userDetailsManager.changePassword(oldPassword, newPassword); + } + + @ApiOperation("用户不登录直接修改自己的密码") + @PostMapping + public void updatePassword(String username, String oldPassword, String newPassword) { + UserDetails userDetails = userDetailsManager.loadUserByUsername(username); + Authentication authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword); + try { + //验证旧密码是否正确 + authenticate = authenticationManager.authenticate(authenticate); + } catch(BadCredentialsException ex) { + //必须吃掉这个异常,密码可能已经过期 + } + SecurityContext context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(authenticate); + userDetailsManager.changePassword(oldPassword, newPassword); + SecurityContextHolder.clearContext(); + } + + @ApiOperation("管理员修改其它用户密码") + @PostMapping("/admin") + public void updatePassword(Principal principal, String username, String password) { + if(SecurityUtils.isSysAdmin(principal)) { + UserDetails userDetails = userDetailsManager.loadUserByUsername(username); + passwordService.updatePassword(userDetails, password); + } else { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, "只有系统管理员才能修改其他用户密码"); + } + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AuthClientConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AuthClientConfiguration.java index 84d20ac..1ccb615 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AuthClientConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AuthClientConfiguration.java @@ -19,7 +19,6 @@ import cn.seqdata.gateway.swagger.OAuth2ClientProperties; import cn.seqdata.oauth2.client.DefaultRole; import cn.seqdata.oauth2.client.FeignAuthzClient; import cn.seqdata.oauth2.client.security.EnhanceUserAuthenticationConverter; -import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; import cn.seqdata.oauth2.client.security.PathPermissionEvaluatorImpl; /** @@ -77,7 +76,7 @@ public class AuthClientConfiguration { } @Bean - public PathPermissionEvaluator permissionEvaluator(FeignAuthzClient authzClient) { + public PathPermissionEvaluatorImpl permissionEvaluator(FeignAuthzClient authzClient) { return new PathPermissionEvaluatorImpl("url", authzClient); } } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java index 490f7d7..6eaf66f 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CacheController.java @@ -1,11 +1,12 @@ package cn.seqdata.gateway.filter.authc; +import java.util.Map; import lombok.AllArgsConstructor; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import cn.seqdata.oauth2.client.security.PathPermission; +import cn.seqdata.oauth2.client.security.PathPermissionEvaluatorImpl; /** * @author jrxian @@ -16,24 +17,50 @@ import org.springframework.web.bind.annotation.RestController; @AllArgsConstructor public class CacheController { private final CachedResourceServerTokenServices tokenServices; + private final PathPermissionEvaluatorImpl permissionEvaluator; - @RequestMapping(value = "/authentication", method = {RequestMethod.GET, RequestMethod.DELETE}) + @DeleteMapping(value = "/authentication") public void invalidateAuthentications() { tokenServices.authenticationCache.invalidateAll(); } - @RequestMapping(value = "/authentication/{key}", method = {RequestMethod.GET, RequestMethod.DELETE}) + @DeleteMapping(value = "/authentication/{key}") public void invalidateAuthentication(@PathVariable("key") String key) { tokenServices.authenticationCache.invalidate(key); } - @RequestMapping(value = "/token", method = {RequestMethod.GET, RequestMethod.DELETE}) + @DeleteMapping(value = "/token") public void invalidateTokens() { tokenServices.accessTokenCache.invalidateAll(); } - @RequestMapping(value = "/token/{key}", method = {RequestMethod.GET, RequestMethod.DELETE}) + @DeleteMapping(value = "/token/{key}") public void invalidateToken(@PathVariable("key") String key) { tokenServices.accessTokenCache.invalidate(key); } + + @GetMapping(value = "/authorities") + public Map getAuthorities() { + return permissionEvaluator.idCache.asMap(); + } + + @DeleteMapping(value = "/authorities") + public void invalidateAuthoritie() { + permissionEvaluator.idCache.invalidateAll(); + } + + @GetMapping(value = "/authoritiy/{id}/permissions") + public Map getPermissions(@PathVariable("id") long id) { + return permissionEvaluator.permCache.getUnchecked(id); + } + + @DeleteMapping(value = "/authoritiy/{id}/permissions") + public void invalidatePermissions(@PathVariable("id") long id) { + permissionEvaluator.permCache.invalidate(id); + } + + @DeleteMapping(value = "/permissions") + public void invalidatePermissions() { + permissionEvaluator.permCache.invalidateAll(); + } } -- Gitee From c40dfac507923a4e1ad3cedc3b32791813eca5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 28 Nov 2020 16:11:30 +0800 Subject: [PATCH 116/228] =?UTF-8?q?token=E4=B8=AD=E6=94=BE=E5=85=A5additio?= =?UTF-8?q?nalInformation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java index 825cfe7..7f78b3c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java @@ -30,12 +30,11 @@ public class DefaultTokenEnhancer implements TokenEnhancer { public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { if(accessToken instanceof DefaultOAuth2AccessToken) { Map attributes = new HashMap<>(); - ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); String clientId = SecurityUtils.clientId(authentication); String username = SecurityUtils.username(authentication); - Optional userOptional = userService.loadUser(clientId, username); + Optional userOptional = userService.loadUser(clientId, username); if(userOptional.isPresent()) { UserAccount account = userOptional.get(); User user = userRepo.getOne(Objects.requireNonNull(account.getId())); @@ -45,6 +44,8 @@ public class DefaultTokenEnhancer implements TokenEnhancer { attributes.put(OAuth2Constants.DEPT_ID, user.getDeptId()); attributes.put(OAuth2Constants.ORG_ID, user.getOrgId()); } + + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); } return accessToken; -- Gitee From 8a4ddd18132a62f50f53171d2bd20975c55b378c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 28 Nov 2020 21:55:32 +0800 Subject: [PATCH 117/228] =?UTF-8?q?=E6=94=AF=E6=8C=81javatime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authc/pom.xml | 11 +++++ .../oauth2/controller/Jdk8Controller.java | 47 +++++++++++++++++++ .../oauth2/controller/JodaController.java | 3 ++ .../src/main/resources/application.yml | 5 +- .../src/main/resources/bootstrap.yml | 16 +++++++ 5 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/Jdk8Controller.java create mode 100644 seqdata-cloud-authc/src/main/resources/bootstrap.yml diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml index a16199b..40143d7 100644 --- a/seqdata-cloud-authc/pom.xml +++ b/seqdata-cloud-authc/pom.xml @@ -8,6 +8,7 @@ 2.2.1.RELEASE seqdata-cloud-authc + 2.2.1-SNAPSHOT joda-time @@ -17,10 +18,20 @@ org.springframework.data spring-data-jpa + + cn.seqdata.cloud + seqdata-cloud-core + 2.2.1-SNAPSHOT + cn.seqdata.cloud seqdata-cloud-starter 2.2.1-SNAPSHOT + + javax.persistence + javax.persistence-api + 2.2 + diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/Jdk8Controller.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/Jdk8Controller.java new file mode 100644 index 0000000..db9aca8 --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/Jdk8Controller.java @@ -0,0 +1,47 @@ +package cn.seqdata.oauth2.controller; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.security.PermitAll; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author jrxian + * @date 2020/11/28 19:42 + */ +@PermitAll +@RestController +@RequestMapping("/jdk8") +public class Jdk8Controller { + + @GetMapping + public Map jdk8() { + Map values = new HashMap<>(); + values.put("date", LocalDate.now()); + values.put("time", LocalTime.now()); + values.put("localdatetime", LocalDateTime.now()); + return values; + } + + @GetMapping("/date") + public LocalDate date(@RequestParam(required = false) LocalDate key) { + return key; + } + + @GetMapping("/time") + public LocalTime time(@RequestParam(required = false) LocalTime key) { + return key; + } + + @GetMapping("/localdatetime") + public LocalDateTime localdatetime(@RequestParam(required = false) LocalDateTime key) { + return key; + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java index 120f303..08836cc 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java @@ -1,5 +1,7 @@ package cn.seqdata.oauth2.controller; +import javax.annotation.security.PermitAll; + import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; @@ -13,6 +15,7 @@ import org.springframework.web.bind.annotation.RestController; * @author jrxian * @date 2020-08-20 16:42 */ +@PermitAll @RestController @RequestMapping("/joda") public class JodaController { diff --git a/seqdata-cloud-authc/src/main/resources/application.yml b/seqdata-cloud-authc/src/main/resources/application.yml index fab68ae..a5ec962 100644 --- a/seqdata-cloud-authc/src/main/resources/application.yml +++ b/seqdata-cloud-authc/src/main/resources/application.yml @@ -14,12 +14,11 @@ security: prefer-token-info: false token-info-uri: http://authz/oauth/check_token user-info-uri: http://authz/user/info - ignoring: - - /**/*.html - - /**/*.js permit-all: - /type/** - /code/** + - /joda/** + - /jdk8/** client: client-id: client client-secret: secret diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..8636e73 --- /dev/null +++ b/seqdata-cloud-authc/src/main/resources/bootstrap.yml @@ -0,0 +1,16 @@ +spring: + cloud: + nacos: + discovery: + server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} + cluster-name: ${NACOS_CLUSTER_NAME:DEFAULT} + namespace: ${NACOS_NAMESPACE:public} + group: ${NACOS_GROUP:DEFAULT_GROUP} + config: + server-addr: ${spring.cloud.nacos.discovery.server-addr} + cluster-name: ${spring.cloud.nacos.discovery.cluster-name} + namespace: ${spring.cloud.nacos.discovery.namespace} + group: ${spring.cloud.nacos.discovery.group} + file-extension: ${NACOS_FILE_EXT:yml} +server: + port: 0 \ No newline at end of file -- Gitee From c5e0048788583692b50920accbd8672ce2cf34e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sat, 28 Nov 2020 22:53:57 +0800 Subject: [PATCH 118/228] =?UTF-8?q?=E8=B0=83=E8=AF=95=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java | 2 +- .../java/cn/seqdata/oauth2/controller/PasswordController.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 5746326..ea693f2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -21,7 +21,7 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.authorizeRequests() .antMatchers("/oauth2/**", "/rbac/**", "/perm/**" - , "/connect/**", "/oauth/code/**", "/wxapp/**") + , "/connect/**", "/oauth/code/**", "/password/**", "/wxapp/**") .permitAll(); super.configure(http); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index e85444f..1d1fcfd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -1,6 +1,7 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; +import javax.annotation.security.PermitAll; import lombok.AllArgsConstructor; import io.swagger.annotations.ApiOperation; @@ -40,6 +41,7 @@ public class PasswordController { } @ApiOperation("用户不登录直接修改自己的密码") + @PermitAll @PostMapping public void updatePassword(String username, String oldPassword, String newPassword) { UserDetails userDetails = userDetailsManager.loadUserByUsername(username); @@ -50,7 +52,7 @@ public class PasswordController { } catch(BadCredentialsException ex) { //必须吃掉这个异常,密码可能已经过期 } - SecurityContext context = SecurityContextHolder.createEmptyContext(); + SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication(authenticate); userDetailsManager.changePassword(oldPassword, newPassword); SecurityContextHolder.clearContext(); -- Gitee From 02720397b1f0632b22fb9fc9fa41b7fa3661a266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 29 Nov 2020 00:59:45 +0800 Subject: [PATCH 119/228] =?UTF-8?q?=E8=B0=83=E8=AF=95=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/controller/PasswordController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 1d1fcfd..dfd0777 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -1,13 +1,14 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; +import java.util.Collections; import javax.annotation.security.PermitAll; import lombok.AllArgsConstructor; import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; @@ -49,8 +50,9 @@ public class PasswordController { try { //验证旧密码是否正确 authenticate = authenticationManager.authenticate(authenticate); - } catch(BadCredentialsException ex) { - //必须吃掉这个异常,密码可能已经过期 + } catch(CredentialsExpiredException ex) { + //必须吃掉这个异常,密码已经过期 + authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword, Collections.emptyList()); } SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication(authenticate); -- Gitee From 7347ddc046b58254dffe1b2a6c7a9d7dc2564f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 30 Nov 2020 00:23:33 +0800 Subject: [PATCH 120/228] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E7=9A=84=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 6 --- .../seqdata/oauth2/SwaggerConfiguration.java | 20 ++------- .../oauth2/controller/CurrentController.java | 18 ++++---- .../oauth2/controller/PasswordController.java | 44 ++++++++++++------- .../oauth2/service/DefaultTokenEnhancer.java | 22 ++++------ .../seqdata/oauth2/service/UserService.java | 17 +++---- .../src/main/resources/application.yml | 2 +- .../gateway/swagger/SwaggerProvider.java | 24 +++++----- 8 files changed, 63 insertions(+), 90 deletions(-) diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index e045478..775efc0 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -63,12 +63,6 @@ ${project.version} - - com.alibaba - druid-spring-boot-starter - 1.2.3 - runtime - com.microsoft.sqlserver mssql-jdbc diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java index 649b52c..e4e75b7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/SwaggerConfiguration.java @@ -1,13 +1,12 @@ package cn.seqdata.oauth2; import java.util.Collections; -import java.util.Objects; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.util.AntPathMatcher; import springfox.documentation.builders.OAuthBuilder; +import springfox.documentation.builders.PathSelectors; import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant; import springfox.documentation.service.SecurityReference; @@ -24,7 +23,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @ConditionalOnProperty(value = "swagger.enabled", matchIfMissing = true) @EnableSwagger2 public class SwaggerConfiguration { - private final AntPathMatcher matcher = new AntPathMatcher(); @Bean public Docket apiDocker() { @@ -38,19 +36,7 @@ public class SwaggerConfiguration { .reference("OAuth2") .scopes(new AuthorizationScope[]{}) .build())) - .forPaths(input -> { - if(Objects.nonNull(input)) { - String[] patterns = {"/router/**", "/rbac/**", "/perm/**"}; - for(String pattern : patterns) { - if(matcher.match(pattern, input)) { - return true; - } - } - } - - return false; - }) - .build())) - ; + .forPaths(PathSelectors.any()) + .build())); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 89aa7c2..91e43cd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -150,8 +150,8 @@ public class CurrentController { private UserAccount getUserAccount(Principal principal) { String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); - Optional user = userService.loadUser(clientId, username); - return user.orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); + return userService.loadUser(clientId, username) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); } private void checkField(BindException ex, UserAccountParam param, String fieldName, @@ -163,14 +163,12 @@ public class CurrentController { private void checkExist(BindException ex, UserAccountParam param, String fieldName, UserAccount entity, Function getter, Function> querier) { - String value = getter.apply(param); - Optional optional = querier.apply(value); - if(optional.isPresent()) { - UserAccount account = optional.get(); - if(!Objects.equals(entity.getId(), account.getId())) { - ex.addError(new FieldError(objectName, fieldName, fieldName + "已经被使用")); - } - } + querier.apply(getter.apply(param)) + .ifPresent(account -> { + if(!Objects.equals(entity.getId(), account.getId())) { + ex.addError(new FieldError(objectName, fieldName, fieldName + "已经被使用")); + } + }); } private void checkErrors(BindException ex) throws BindException { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index dfd0777..d010f00 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -1,12 +1,11 @@ package cn.seqdata.oauth2.controller; -import java.security.Principal; import java.util.Collections; import javax.annotation.security.PermitAll; import lombok.AllArgsConstructor; import io.swagger.annotations.ApiOperation; -import org.springframework.http.HttpStatus; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -15,13 +14,13 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsPasswordService; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.UserDetailsManager; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.server.ResponseStatusException; -import cn.seqdata.oauth2.util.SecurityUtils; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; /** * @author jrxian @@ -31,15 +30,11 @@ import cn.seqdata.oauth2.util.SecurityUtils; @RequestMapping("/password") @AllArgsConstructor public class PasswordController { + private final PasswordEncoder passwordEncoder; private final AuthenticationManager authenticationManager; private final UserDetailsManager userDetailsManager; private final UserDetailsPasswordService passwordService; - - @ApiOperation("用户登录后修改自己的密码") - @PostMapping("/self") - public void changePassword(String oldPassword, String newPassword) { - userDetailsManager.changePassword(oldPassword, newPassword); - } + private final UserAccountRepo accountRepo; @ApiOperation("用户不登录直接修改自己的密码") @PermitAll @@ -60,14 +55,29 @@ public class PasswordController { SecurityContextHolder.clearContext(); } + @ApiOperation("用户登录后修改自己的密码") + @PostMapping("/self") + @PreAuthorize("isAuthenticated()") + public void changePassword(String oldPassword, String newPassword) { + userDetailsManager.changePassword(oldPassword, newPassword); + } + @ApiOperation("管理员修改其它用户密码") @PostMapping("/admin") - public void updatePassword(Principal principal, String username, String password) { - if(SecurityUtils.isSysAdmin(principal)) { - UserDetails userDetails = userDetailsManager.loadUserByUsername(username); - passwordService.updatePassword(userDetails, password); - } else { - throw new ResponseStatusException(HttpStatus.FORBIDDEN, "只有系统管理员才能修改其他用户密码"); - } + @PreAuthorize("hasAuthority('sysadmin')") + public void updatePassword(String username, String password) { + UserDetails userDetails = userDetailsManager.loadUserByUsername(username); + passwordService.updatePassword(userDetails, passwordEncoder.encode(password)); + } + + @ApiOperation("解锁密码") + @PostMapping("/unlock") + @PreAuthorize("hasAuthority('sysadmin')") + public void unlockPassword(String username) { + accountRepo.findByUsername(username) + .ifPresent(account -> { + account.setLockExpireDate(null); + accountRepo.save(account); + }); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java index 7f78b3c..6e250b7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java @@ -3,7 +3,6 @@ package cn.seqdata.oauth2.service; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.Optional; import lombok.AllArgsConstructor; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; @@ -13,7 +12,6 @@ import org.springframework.security.oauth2.provider.token.TokenEnhancer; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.rbac.User; -import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.util.SecurityUtils; @@ -33,17 +31,15 @@ public class DefaultTokenEnhancer implements TokenEnhancer { String clientId = SecurityUtils.clientId(authentication); String username = SecurityUtils.username(authentication); - - Optional userOptional = userService.loadUser(clientId, username); - if(userOptional.isPresent()) { - UserAccount account = userOptional.get(); - User user = userRepo.getOne(Objects.requireNonNull(account.getId())); - attributes.put(OAuth2Constants.NAME, user.getName()); - attributes.put(OAuth2Constants.USER_ID, user.getId()); - attributes.put(OAuth2Constants.POST_ID, user.getPostId()); - attributes.put(OAuth2Constants.DEPT_ID, user.getDeptId()); - attributes.put(OAuth2Constants.ORG_ID, user.getOrgId()); - } + userService.loadUser(clientId, username) + .ifPresent(account -> { + User user = userRepo.getOne(Objects.requireNonNull(account.getId())); + attributes.put(OAuth2Constants.NAME, user.getName()); + attributes.put(OAuth2Constants.USER_ID, user.getId()); + attributes.put(OAuth2Constants.POST_ID, user.getPostId()); + attributes.put(OAuth2Constants.DEPT_ID, user.getDeptId()); + attributes.put(OAuth2Constants.ORG_ID, user.getOrgId()); + }); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(attributes); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 867b7cc..9c4361b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -3,9 +3,9 @@ package cn.seqdata.oauth2.service; import java.security.Principal; import java.util.Objects; import java.util.Optional; +import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; @@ -56,20 +56,17 @@ public class UserService { //@Transactional //不要加事务,不然User和UserAccount不能保存 public void saveAsUser(String clientId, UserProfile profile) { UserDetailId entityId = new UserDetailId(clientId, profile.getName()); - Optional optional = userDetailRepo.findById(entityId); UserDetail userDetail = new UserDetail(entityId); //用户不存在 + Optional optional = userDetailRepo.findById(entityId); if(!optional.isPresent()) { if(profile instanceof WechatUserProfile) { //查找微信的unionid,如果找到,视为同一个用户 String unionId = ((WechatUserProfile) profile).getUnionId(); if(Objects.nonNull(unionId)) { - Optional accountOptional = accountRepo.findByUnionId(unionId); - if(accountOptional.isPresent()) { - UserAccount account = accountOptional.get(); - userDetail.setUserId(account.getId()); - } + accountRepo.findByUnionId(unionId) + .ifPresent(account -> userDetail.setUserId(account.getId())); } } @@ -92,11 +89,7 @@ public class UserService { //获取当前用户身份 String localClientId = SecurityUtils.clientId(principal); String localUsername = SecurityUtils.username(principal); - Optional userOptional = loadUser(localClientId, localUsername); - if(userOptional.isPresent()) { - UserAccount user = userOptional.get(); - bindUser(user.getId(), registrationId, profile); - } + loadUser(localClientId, localUsername).ifPresent(user -> bindUser(user.getId(), registrationId, profile)); } else { //当用户不存在时自动创建 saveAsUser(registrationId, profile); diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 8416381..b677c0f 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -18,7 +18,7 @@ spring: redis: host: db.rtelec.cn port: 6379 - password: seq@2015 + password: seq2015 security: oauth2: client: diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java index 73aa2f9..170ca3c 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java @@ -6,16 +6,15 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; -import lombok.SneakyThrows; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; @@ -47,21 +46,18 @@ public class SwaggerProvider implements SwaggerResourcesProvider { //过滤掉网关本身 .filter(route -> !StringUtils.equals(routeId(route), serviceId)) .forEach(route -> { - Optional optional = route.getPredicates() + route.getPredicates() .stream() .filter(predicateDefinition -> "Path".equals(predicateDefinition.getName())) - .findFirst(); - - if(optional.isPresent()) { - String routeId = routeId(route); - - PredicateDefinition predicate = optional.get(); - Map args = predicate.getArgs(); - String pattern = args.get("pattern"); - String location = StringUtils.replace(pattern, "/**", "/v2/api-docs"); + .findFirst() + .ifPresent(predicate -> { + String routeId = routeId(route); + Map args = predicate.getArgs(); + String pattern = args.get("pattern"); + String location = StringUtils.replace(pattern, "/**", "/v2/api-docs"); - swaggerResources.add(swaggerResource(routeId, location)); - } + swaggerResources.add(swaggerResource(routeId, location)); + }); }); return swaggerResources; -- Gitee From e81a5ad0427cd669f83846c35cc03f0f56eecc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 30 Nov 2020 12:00:44 +0800 Subject: [PATCH 121/228] =?UTF-8?q?=E8=A7=84=E8=8C=83=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 91e43cd..646f00e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -131,22 +131,6 @@ public class CurrentController { accountRepo.save(entity); } - /** - * 修改密码 - */ - @Deprecated - @PatchMapping(value = "/password") - public void updatePassword(Principal principal, @RequestBody UserAccountParam param) throws BindException { - UserAccount entity = getUserAccount(principal); - - BindException ex = new BindException(param, objectName); - checkField(ex, param, OAuth2Constants.PASSWORD, UserAccountParam::getPassword); - checkErrors(ex); - - entity.setPassword(param.getPassword()); - accountRepo.save(entity); - } - private UserAccount getUserAccount(Principal principal) { String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); -- Gitee From a357cdf8f4e25c63bedfe0a91aa946ed3c2e9253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 3 Dec 2020 09:44:17 +0800 Subject: [PATCH 122/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0JasyptConfiguration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{Jdk8Controller.java => JavaTimeController.java} | 8 +++++--- seqdata-cloud-gateway/src/main/docker/Dockerfile | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) rename seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/{Jdk8Controller.java => JavaTimeController.java} (89%) diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/Jdk8Controller.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JavaTimeController.java similarity index 89% rename from seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/Jdk8Controller.java rename to seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JavaTimeController.java index db9aca8..ab7629e 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/Jdk8Controller.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JavaTimeController.java @@ -1,5 +1,6 @@ package cn.seqdata.oauth2.controller; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -18,14 +19,15 @@ import org.springframework.web.bind.annotation.RestController; */ @PermitAll @RestController -@RequestMapping("/jdk8") -public class Jdk8Controller { - +@RequestMapping("/javatime") +public class JavaTimeController { + @GetMapping public Map jdk8() { Map values = new HashMap<>(); values.put("date", LocalDate.now()); values.put("time", LocalTime.now()); + values.put("instant", Instant.now()); values.put("localdatetime", LocalDateTime.now()); return values; } diff --git a/seqdata-cloud-gateway/src/main/docker/Dockerfile b/seqdata-cloud-gateway/src/main/docker/Dockerfile index 5895a5e..f6a5758 100644 --- a/seqdata-cloud-gateway/src/main/docker/Dockerfile +++ b/seqdata-cloud-gateway/src/main/docker/Dockerfile @@ -1,4 +1,6 @@ FROM openjdk:8-jdk-alpine +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories +RUN apk add --update ttf-dejavu fontconfig && rm -rf /var/cache/apk/* VOLUME /tmp ADD seqdata-cloud-gateway-2.2.1-SNAPSHOT.jar myapp.jar ENV TZ=Asia/Shanghai -- Gitee From da5c8bbdcd410028a2ffc6db913c702d2cbd9ca4 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Thu, 3 Dec 2020 11:40:53 +0800 Subject: [PATCH 123/228] =?UTF-8?q?=E5=90=8C=E6=AD=A5dev=E5=88=86=E6=94=AF?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=9A=E6=8C=89=E9=92=AE=E5=92=8Crouter?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E3=80=81=E6=97=A5=E5=BF=97=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/service/RouterService.java | 20 ++++++++++++++----- .../src/main/resources/application.yml | 7 +++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java index 119c74d..91d490d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java @@ -22,6 +22,7 @@ import cn.seqdata.oauth2.repos.perm.ModulePermRepo; import cn.seqdata.oauth2.repos.perm.SysPermRepo; import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; import cn.seqdata.oauth2.util.RouterUtils; +import cn.seqdata.oauth2.util.SecurityUtils; import lombok.AllArgsConstructor; /** @@ -52,11 +53,20 @@ public class RouterService { private Collection fetchPermissions( JpaPermRepository repository, PermissionType permType, Principal principal, Sort sort) { - return repository.findAll(sort) - .stream() - .filter(x -> BooleanUtils.isNotFalse(x.getVisible())) - .filter(new PermissionPredicate(permissionRepo, permType, principal)) - .collect(Collectors.toList()); + boolean sysAdmin = SecurityUtils.isSysAdmin(principal); + if(sysAdmin) { + //超级管理员忽略visible不可见 + return repository.findAll(sort) + .stream() + .filter(new PermissionPredicate(permissionRepo, permType, principal)) + .collect(Collectors.toList()); + } else { + return repository.findAll(sort) + .stream() + .filter(x -> BooleanUtils.isNotFalse(x.getVisible())) + .filter(new PermissionPredicate(permissionRepo, permType, principal)) + .collect(Collectors.toList()); + } } public Collection topRouters(Principal principal, Sort sort) { diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 0db7b67..de3474d 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -18,6 +18,13 @@ spring: enabled: true hmac-verify: enabled: false + rabbitmq: + host: rabbitmq.com + username: mq + password: mq + syslog: + exchange: mqex + routeKey: mqrk security: oauth2: client: -- Gitee From 69bb751634e63026e6a2e2511ddd9c9567f0452c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 3 Dec 2020 19:46:42 +0800 Subject: [PATCH 124/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 8 ++--- .../oauth2/controller/ConnectController.java | 35 +++++++----------- .../oauth2/controller/CurrentController.java | 13 +++---- .../oauth2/controller/WxAppController.java | 25 ++++++++++--- .../oauth2/jackson/AuthorityConverter.java | 1 - .../MobileNonceAuthenticationProvider.java | 9 ++--- .../seqdata/oauth2/service/RouterService.java | 24 +++++-------- .../seqdata/oauth2/service/TokenService.java | 36 ++++++++++++------- .../seqdata/oauth2/service/UserService.java | 30 ++++++++-------- .../cn/seqdata/oauth2/wxapp/WxAppService.java | 5 +-- .../src/main/resources/application.yml | 7 ---- 11 files changed, 96 insertions(+), 97 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index 29048f5..e5d1dc7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -26,13 +26,13 @@ import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.security.provisioning.UserDetailsManager; import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.mobile.MobileTokenGranter; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; +import cn.seqdata.oauth2.service.JpaUserDetailsManager; /** * Author: jrxian @@ -43,7 +43,7 @@ import cn.seqdata.oauth2.service.JpaClientDetailsService; @AllArgsConstructor public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { private final AuthenticationManager authenticationManager; - private final UserDetailsManager userDetailsService; + private final JpaUserDetailsManager userDetailsManager; private final AuthorizationServerTokenServices tokenServices; private final TokenStore tokenStore; private final TokenEnhancer tokenEnhancer; @@ -67,7 +67,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu endpoints .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) .authenticationManager(authenticationManager) - .userDetailsService(userDetailsService) + .userDetailsService(userDetailsManager) .tokenServices(tokenServices) .tokenStore(tokenStore) .tokenEnhancer(tokenEnhancer) @@ -91,7 +91,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu //自定义的手机验证码登录 //!!!必须在用户名密码验证前面 if(Objects.nonNull(mobileNonceHandler)) { - MobileNonceAuthenticationProvider mobileNonceProvider = new MobileNonceAuthenticationProvider(userDetailsService, mobileNonceHandler); + MobileNonceAuthenticationProvider mobileNonceProvider = new MobileNonceAuthenticationProvider(userDetailsManager, mobileNonceHandler); tokenGranters.add(new MobileTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory)); } //用户名密码验证 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index bcf0cea..90cc4c7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -6,7 +6,6 @@ import java.security.Principal; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.Optional; import javax.security.auth.login.AccountNotFoundException; import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; @@ -51,28 +50,18 @@ public class ConnectController { String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); - - Optional optional = userDetailRepo - .findById(new UserDetailId(clientId, username)) - .map(UserDetail::getUser); - - if(!optional.isPresent()) { - optional = accountRepo.findByUsernameOrMobileOrEmail(username, username, username); - } - - if(optional.isPresent()) { - UserAccount account = optional.get(); - - connectors.put(OAuth2Constants.USERNAME, account.getUsername()); - connectors.put(OAuth2Constants.MOBILE, account.getMobile()); - connectors.put(OAuth2Constants.EMAIL, account.getEmail()); - - userDetailRepo.findByUserId(Objects.requireNonNull(account.getId())) - .stream() - .map(UserDetail::getId) - .filter(Objects::nonNull) - .forEach(x -> connectors.put(x.getClient(), x.getPrincipal())); - } + userService.loadUser(clientId, username) + .ifPresent(account -> { + connectors.put(OAuth2Constants.USERNAME, account.getUsername()); + connectors.put(OAuth2Constants.MOBILE, account.getMobile()); + connectors.put(OAuth2Constants.EMAIL, account.getEmail()); + + userDetailRepo.findByUserId(Objects.requireNonNull(account.getId())) + .stream() + .map(UserDetail::getId) + .filter(Objects::nonNull) + .forEach(x -> connectors.put(x.getClient(), x.getPrincipal())); + }); return connectors; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 646f00e..fa3d258 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -23,9 +23,9 @@ import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.params.UserAccountParam; -import cn.seqdata.oauth2.repos.perm.ActionPermRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; +import cn.seqdata.oauth2.service.AuthorityService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; @@ -42,10 +42,10 @@ public class CurrentController { private final UserAccountRepo accountRepo; private final UserRepo userRepo; - private final ActionPermRepo actionPermRepo; - private final UserService userService; private final TokenStore tokenStore; + private final UserService userService; private final ConsumerTokenServices tokenServices; + private final AuthorityService authorityService; @ApiOperation("通行证") @GetMapping("/principal") @@ -59,9 +59,10 @@ public class CurrentController { String clientId = SecurityUtils.clientId(principal); String username = SecurityUtils.username(principal); return userService.loadUser(clientId, username) - .map(x -> { - User user = userRepo.getOne(Objects.requireNonNull(x.getId())); - user.setAuthorities(x.getAuthorities()); + .map(UserAccount::getId) + .map(id -> { + User user = userRepo.getOne(Objects.requireNonNull(id)); + user.setAuthorities(authorityService.loadAuthorities(id)); return user; }); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 8e9209f..3080292 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -2,9 +2,9 @@ package cn.seqdata.oauth2.controller; import java.io.IOException; import java.security.Principal; -import java.util.Collections; import java.util.HashMap; import java.util.Map; +import lombok.AllArgsConstructor; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.common.OAuth2AccessToken; @@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.DefaultRole; import cn.seqdata.oauth2.service.TokenService; @@ -22,6 +21,7 @@ import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.wechat.WechatUserProfile; import cn.seqdata.oauth2.wxapp.WxAppService; import cn.seqdata.wxapp.message.Code2SessionResponse; +import cn.seqdata.wxapp.pojo.PhoneInfo; import cn.seqdata.wxapp.pojo.UserInfo; import cn.seqdata.wxapp.util.WxAppUtils; @@ -47,8 +47,7 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); WxAppUtils.toAttributes(attributes, userInfo); - SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(DefaultRole.anonymous.name()); - DefaultOAuth2User remoteUser = new DefaultOAuth2User(Collections.singletonList(defaultAuthority), attributes, WxAppUtils.WXAPP_ATTR_KEY); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); @@ -62,7 +61,23 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(DefaultRole.anonymous.name()); - DefaultOAuth2User remoteUser = new DefaultOAuth2User(Collections.singleton(defaultAuthority), attributes, WxAppUtils.WXAPP_ATTR_KEY); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, WxAppUtils.WXAPP_ATTR_KEY); + WechatUserProfile profile = new WechatUserProfile(remoteUser); + userService.updateUser(id, profile, principal); + + return tokenService.createToken(id, remoteUser); + } + + @GetMapping("/mobile") + public OAuth2AccessToken mobile(@PathVariable("id") String id, String code, String iv, String encryptedData, Principal principal) throws IOException { + Code2SessionResponse response = wxappService.signin(id, code); + String decryptedData = WxAppUtils.decryptData(response.getSession_key(), iv, encryptedData); + PhoneInfo phoneInfo = objectMapper.readValue(decryptedData, PhoneInfo.class); + + Map attributes = new HashMap<>(); + WxAppUtils.toAttributes(attributes, response); + WxAppUtils.toAttributes(attributes, phoneInfo); + DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, "purePhoneNumber"); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java index ead9182..5fd1ddf 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/jackson/AuthorityConverter.java @@ -25,5 +25,4 @@ public final class AuthorityConverter { .map(GrantedAuthority::getAuthority) .collect(Collectors.toList()); } - } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java index 982683c..97214db 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java @@ -1,6 +1,7 @@ package cn.seqdata.oauth2.mobile; import java.util.Optional; +import lombok.AllArgsConstructor; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.security.authentication.AuthenticationServiceException; @@ -9,8 +10,8 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.service.JpaUserDetailsManager; /** * Author: jrxian @@ -18,13 +19,13 @@ import lombok.AllArgsConstructor; */ @AllArgsConstructor public class MobileNonceAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { - private final UserDetailsService userDetailsService; + private final JpaUserDetailsManager userDetailsManager; private final MobileNonceHandler mobileNonceHandler; @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { try { - return Optional.ofNullable(userDetailsService.loadUserByUsername(username)) + return Optional.ofNullable(userDetailsManager.loadUserByMobile(username)) .orElseThrow(() -> new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation")); } catch(AuthenticationServiceException ex) { throw ex; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java index 91d490d..ff16edd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RouterService.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.Objects; import java.util.function.BiPredicate; import java.util.stream.Collectors; +import lombok.AllArgsConstructor; import org.apache.commons.lang3.BooleanUtils; import org.springframework.data.domain.Sort; @@ -23,7 +24,6 @@ import cn.seqdata.oauth2.repos.perm.SysPermRepo; import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; import cn.seqdata.oauth2.util.RouterUtils; import cn.seqdata.oauth2.util.SecurityUtils; -import lombok.AllArgsConstructor; /** * @author jrxian @@ -51,22 +51,14 @@ public class RouterService { return fetchPermissions(actionPermRepo, PermissionType.action, principal, sort); } - private Collection fetchPermissions( - JpaPermRepository repository, PermissionType permType, Principal principal, Sort sort) { + private Collection fetchPermissions(JpaPermRepository repository, + PermissionType permType, Principal principal, Sort sort) { boolean sysAdmin = SecurityUtils.isSysAdmin(principal); - if(sysAdmin) { - //超级管理员忽略visible不可见 - return repository.findAll(sort) - .stream() - .filter(new PermissionPredicate(permissionRepo, permType, principal)) - .collect(Collectors.toList()); - } else { - return repository.findAll(sort) - .stream() - .filter(x -> BooleanUtils.isNotFalse(x.getVisible())) - .filter(new PermissionPredicate(permissionRepo, permType, principal)) - .collect(Collectors.toList()); - } + return repository.findAll(sort) + .stream() + .filter(x -> sysAdmin || BooleanUtils.isNotFalse(x.getVisible())) + .filter(new PermissionPredicate(permissionRepo, permType, principal)) + .collect(Collectors.toList()); } public Collection topRouters(Principal principal, Sort sort) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java index 4e0e1aa..f1f9556 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java @@ -2,7 +2,7 @@ package cn.seqdata.oauth2.service; import java.util.Collection; import java.util.Collections; -import java.util.Objects; +import lombok.AllArgsConstructor; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; @@ -12,7 +12,9 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Request; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.stereotype.Service; -import lombok.AllArgsConstructor; + +import cn.seqdata.oauth2.DefaultRole; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; /** * Author: jrxian @@ -25,12 +27,6 @@ public class TokenService { private final UserService userService; private final AuthorityService authorityService; - public static OAuth2Request createOAuth2Request(String clientId, Collection authorities) { - return new OAuth2Request(Collections.emptyMap(), clientId, - authorities, true, Collections.emptySet(), - Collections.emptySet(), null, null, null); - } - /** * 为第三方认证的用户创建本系统的 token */ @@ -39,15 +35,29 @@ public class TokenService { } public OAuth2Authentication createOAuth2Authentication(String clientId, OAuth2User principal) { - Collection authorities = loadAuthorities(clientId, principal); + Collection authorities = loadAuthorities(clientId, principal.getName()); OAuth2Request oAuth2Request = createOAuth2Request(clientId, authorities); OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal, authorities, clientId); return new OAuth2Authentication(oAuth2Request, authenticationToken); } - public Collection loadAuthorities(String clientId, OAuth2User principal) { - return userService.loadUser(clientId, principal.getName()) - .map(user -> authorityService.loadAuthorities(Objects.requireNonNull(user.getId()))) - .orElse(Collections.emptySet()); + public Collection loadAuthorities(String clientId, String username) { + return userService.loadUser(clientId, username) + .map(UserAccount::getId) + .map(authorityService::loadAuthorities) + .orElse(DefaultRole.NO_ROLES); + } + + public static OAuth2Request createOAuth2Request(String clientId, Collection authorities) { + return new OAuth2Request(Collections.emptyMap(), + clientId, + authorities, + true, + Collections.emptySet(), + Collections.emptySet(), + null, + Collections.emptySet(), + Collections.emptyMap() + ); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 9c4361b..5bc309d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -7,6 +7,7 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; +import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.User; @@ -25,26 +26,22 @@ import cn.seqdata.oauth2.wechat.WechatUserProfile; @Service @AllArgsConstructor public class UserService { - private final AuthorityService authorityService; private final UserAccountRepo accountRepo; private final UserRepo userRepo; private final UserDetailRepo userDetailRepo; public Optional loadUser(String clientId, String username) { - UserDetailId entityId = new UserDetailId(clientId, username); - //先从oauth_user_details中查找用户 - Optional optional = userDetailRepo.findById(entityId) - .map(UserDetail::getUser); - - //未找到,再从oauth_user中查找用户 - if(!optional.isPresent()) { - optional = accountRepo.findByUsernameOrMobileOrEmail(username, username, username); - } - - if(optional.isPresent()) { - UserAccount user = optional.get(); - long userId = Objects.requireNonNull(user.getId()); - user.setAuthorities(authorityService.loadAuthorities(userId)); + Optional optional; + + if(OAuth2Constants.USERNAME.equalsIgnoreCase(clientId)) { + optional = accountRepo.findByUsername(username); + } else if(OAuth2Constants.MOBILE.equalsIgnoreCase(clientId)) { + optional = accountRepo.findByMobile(username); + } else if(OAuth2Constants.EMAIL.equalsIgnoreCase(clientId)) { + optional = accountRepo.findByEmail(username); + } else { + optional = userDetailRepo.findById(new UserDetailId(clientId, username)) + .map(UserDetail::getUser); } return optional; @@ -89,7 +86,8 @@ public class UserService { //获取当前用户身份 String localClientId = SecurityUtils.clientId(principal); String localUsername = SecurityUtils.username(principal); - loadUser(localClientId, localUsername).ifPresent(user -> bindUser(user.getId(), registrationId, profile)); + loadUser(localClientId, localUsername).map(UserAccount::getId) + .ifPresent(id -> bindUser(id, registrationId, profile)); } else { //当用户不存在时自动创建 saveAsUser(registrationId, profile); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index ba95e2d..e20d480 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -1,12 +1,13 @@ package cn.seqdata.oauth2.wxapp; import java.util.concurrent.TimeUnit; +import lombok.AllArgsConstructor; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import lombok.AllArgsConstructor; import cn.seqdata.wxapp.WxAppException; import cn.seqdata.wxapp.WxAppFeignClient; @@ -29,7 +30,7 @@ public class WxAppService { .expireAfterWrite(1, TimeUnit.HOURS) .build(new CacheLoader() { @Override - public String load(String id) { + public String load(@NonNull String id) { AccessTokenRequest request = new AccessTokenRequest(); ClientProperties clientProps = provider.apply(id); request.setAppid(clientProps.getClientId()); diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index de3474d..0db7b67 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -18,13 +18,6 @@ spring: enabled: true hmac-verify: enabled: false - rabbitmq: - host: rabbitmq.com - username: mq - password: mq - syslog: - exchange: mqex - routeKey: mqrk security: oauth2: client: -- Gitee From 5fae9acd3c706c16f9906f96c86d7ef737352d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 7 Dec 2020 11:10:52 +0800 Subject: [PATCH 125/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/WxAppController.java | 27 ++++++++++++++----- .../seqdata/oauth2/service/UserService.java | 16 +++++++---- .../oauth2/wechat/WechatUserProfile.java | 5 ++++ .../cn/seqdata/oauth2/wxapp/WxAppService.java | 6 ++--- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 3080292..af07dd4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -16,6 +16,8 @@ import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.oauth2.DefaultRole; +import cn.seqdata.oauth2.OAuth2Constants; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.wechat.WechatUserProfile; @@ -38,10 +40,13 @@ public class WxAppController { private final UserService userService; private final TokenService tokenService; + /** + * 获取用户信息并且创建rbac_user_info + */ @GetMapping("/signup") public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData, Principal principal) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); - String decryptedData = WxAppUtils.decryptData(response.getSession_key(), iv, encryptedData); + String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); UserInfo userInfo = objectMapper.readValue(decryptedData, UserInfo.class); Map attributes = new HashMap<>(); @@ -54,6 +59,9 @@ public class WxAppController { return tokenService.createToken(id, remoteUser); } + /** + * 获取code换取openid登录 + */ @GetMapping("/signin") public OAuth2AccessToken signin(@PathVariable("id") String id, String code, Principal principal) { Code2SessionResponse response = wxappService.signin(id, code); @@ -68,19 +76,24 @@ public class WxAppController { return tokenService.createToken(id, remoteUser); } - @GetMapping("/mobile") - public OAuth2AccessToken mobile(@PathVariable("id") String id, String code, String iv, String encryptedData, Principal principal) throws IOException { + /** + * 获取微信手机号,绑定到现有账号 + */ + @GetMapping("/phone") + public OAuth2AccessToken phone(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); - String decryptedData = WxAppUtils.decryptData(response.getSession_key(), iv, encryptedData); + String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); PhoneInfo phoneInfo = objectMapper.readValue(decryptedData, PhoneInfo.class); Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); WxAppUtils.toAttributes(attributes, phoneInfo); - DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, "purePhoneNumber"); - WechatUserProfile profile = new WechatUserProfile(remoteUser); - userService.updateUser(id, profile, principal); + userService.loadUser(OAuth2Constants.MOBILE, phoneInfo.purePhoneNumber) + .map(UserAccount::getId) + .ifPresent(userId -> userService.bindUser(userId, id, response.openid)); + + DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, WxAppUtils.WXAPP_ATTR_KEY); return tokenService.createToken(id, remoteUser); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 5bc309d..1e70e6a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -87,10 +87,12 @@ public class UserService { String localClientId = SecurityUtils.clientId(principal); String localUsername = SecurityUtils.username(principal); loadUser(localClientId, localUsername).map(UserAccount::getId) - .ifPresent(id -> bindUser(id, registrationId, profile)); + .ifPresent(userId -> bindUser(userId, registrationId, profile)); } else { //当用户不存在时自动创建 - saveAsUser(registrationId, profile); + if(Objects.nonNull(profile.getName())) { + saveAsUser(registrationId, profile); + } } } @@ -98,15 +100,19 @@ public class UserService { * 将第三方认证的用户和本系统的用户绑定 */ //@Transactional //不要加事务 - public void bindUser(Long userId, String clientId, UserProfile profile) { + public void bindUser(long userId, String clientId, UserProfile profile) { //如果是微信绑定,更新account的union_id if(profile instanceof WechatUserProfile) { - UserAccount account = accountRepo.getOne(Objects.requireNonNull(userId)); + UserAccount account = accountRepo.getOne(userId); account.setUnionId(((WechatUserProfile) profile).getUnionId()); accountRepo.save(account); } - UserDetailId entityId = new UserDetailId(clientId, profile.getName()); + bindUser(userId, clientId, profile.getName()); + } + + public void bindUser(long userId, String clientId, String username) { + UserDetailId entityId = new UserDetailId(clientId, username); UserDetail entity = new UserDetail(entityId); entity.setUserId(userId); userDetailRepo.save(entity); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java index 0d39f8c..edfc203 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatUserProfile.java @@ -21,6 +21,11 @@ public class WechatUserProfile extends UserProfile { return getAttribute("openid"); } + @Override + public String getMobile() { + return getAttribute("purePhoneNumber"); + } + public String getUnionId() { return getAttribute("unionid"); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index e20d480..06d3157 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -49,9 +49,9 @@ public class WxAppService { public Code2SessionResponse signin(String id, String code) { Code2SessionRequest request = new Code2SessionRequest(); ClientProperties clientProps = provider.apply(id); - request.setAppid(clientProps.getClientId()); - request.setSecret(clientProps.getClientSecret()); - request.setJs_code(code); + request.appid = clientProps.getClientId(); + request.secret = clientProps.getClientSecret(); + request.js_code = code; Code2SessionResponse response = feignClient.code2session(request); return WxAppException.check(response); -- Gitee From cfa54b0b24fd1b7a6d5788851869d9310572af55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 7 Dec 2020 17:09:48 +0800 Subject: [PATCH 126/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authc/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml index 40143d7..bb610f5 100644 --- a/seqdata-cloud-authc/pom.xml +++ b/seqdata-cloud-authc/pom.xml @@ -18,11 +18,6 @@ org.springframework.data spring-data-jpa - - cn.seqdata.cloud - seqdata-cloud-core - 2.2.1-SNAPSHOT - cn.seqdata.cloud seqdata-cloud-starter -- Gitee From af5e5f896f7654c308e98678d871f1ab48d03117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 8 Dec 2020 11:53:51 +0800 Subject: [PATCH 127/228] =?UTF-8?q?=E5=A4=84=E7=90=86username&password?= =?UTF-8?q?=E7=99=BB=E5=BD=95/authz/current/user=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 4 +-- .../oauth2/controller/CurrentController.java | 8 ++--- .../oauth2/service/DefaultTokenEnhancer.java | 12 ++++---- .../seqdata/oauth2/service/UserService.java | 29 ++++++++++--------- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 90cc4c7..e1a0b80 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -48,9 +48,9 @@ public class ConnectController { public Map list(Principal principal) { Map connectors = new HashMap<>(); - String clientId = SecurityUtils.clientId(principal); + String grantType = SecurityUtils.grantType(principal); String username = SecurityUtils.username(principal); - userService.loadUser(clientId, username) + userService.loadUser(grantType, username) .ifPresent(account -> { connectors.put(OAuth2Constants.USERNAME, account.getUsername()); connectors.put(OAuth2Constants.MOBILE, account.getMobile()); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index fa3d258..57900a4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -56,9 +56,9 @@ public class CurrentController { @ApiOperation("用户信息") @GetMapping("/user") public Optional info(Principal principal) { - String clientId = SecurityUtils.clientId(principal); + String grantType = SecurityUtils.grantType(principal); String username = SecurityUtils.username(principal); - return userService.loadUser(clientId, username) + return userService.loadUser(grantType, username) .map(UserAccount::getId) .map(id -> { User user = userRepo.getOne(Objects.requireNonNull(id)); @@ -133,9 +133,9 @@ public class CurrentController { } private UserAccount getUserAccount(Principal principal) { - String clientId = SecurityUtils.clientId(principal); + String grantType = SecurityUtils.grantType(principal); String username = SecurityUtils.username(principal); - return userService.loadUser(clientId, username) + return userService.loadUser(grantType, username) .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java index 6e250b7..4adc99a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenEnhancer.java @@ -2,7 +2,6 @@ package cn.seqdata.oauth2.service; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import lombok.AllArgsConstructor; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; @@ -11,7 +10,7 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import cn.seqdata.oauth2.OAuth2Constants; -import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.util.SecurityUtils; @@ -29,11 +28,12 @@ public class DefaultTokenEnhancer implements TokenEnhancer { if(accessToken instanceof DefaultOAuth2AccessToken) { Map attributes = new HashMap<>(); - String clientId = SecurityUtils.clientId(authentication); + String grantType = SecurityUtils.grantType(authentication); String username = SecurityUtils.username(authentication); - userService.loadUser(clientId, username) - .ifPresent(account -> { - User user = userRepo.getOne(Objects.requireNonNull(account.getId())); + userService.loadUser(grantType, username) + .map(UserAccount::getId) + .map(userRepo::getOne) + .ifPresent(user -> { attributes.put(OAuth2Constants.NAME, user.getName()); attributes.put(OAuth2Constants.USER_ID, user.getId()); attributes.put(OAuth2Constants.POST_ID, user.getPostId()); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 1e70e6a..702cf9f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -30,21 +30,22 @@ public class UserService { private final UserRepo userRepo; private final UserDetailRepo userDetailRepo; - public Optional loadUser(String clientId, String username) { - Optional optional; - - if(OAuth2Constants.USERNAME.equalsIgnoreCase(clientId)) { - optional = accountRepo.findByUsername(username); - } else if(OAuth2Constants.MOBILE.equalsIgnoreCase(clientId)) { - optional = accountRepo.findByMobile(username); - } else if(OAuth2Constants.EMAIL.equalsIgnoreCase(clientId)) { - optional = accountRepo.findByEmail(username); + /** + * 用户名密码登录:grantType='password' + * 第三方登录:grantType=clientId + */ + public Optional loadUser(String grantType, String username) { + if(OAuth2Constants.PASSWORD.equalsIgnoreCase(grantType)) { + return accountRepo.findByUsername(username); + } else if(OAuth2Constants.MOBILE.equalsIgnoreCase(grantType)) { + return accountRepo.findByMobile(username); + } else if(OAuth2Constants.EMAIL.equalsIgnoreCase(grantType)) { + return accountRepo.findByEmail(username); } else { - optional = userDetailRepo.findById(new UserDetailId(clientId, username)) + UserDetailId entityId = new UserDetailId(grantType, username); + return userDetailRepo.findById(entityId) .map(UserDetail::getUser); } - - return optional; } /** @@ -84,9 +85,9 @@ public class UserService { public void updateUser(String registrationId, UserProfile profile, Principal principal) { if(Objects.nonNull(principal)) { //获取当前用户身份 - String localClientId = SecurityUtils.clientId(principal); + String localGrantType = SecurityUtils.grantType(principal); String localUsername = SecurityUtils.username(principal); - loadUser(localClientId, localUsername).map(UserAccount::getId) + loadUser(localGrantType, localUsername).map(UserAccount::getId) .ifPresent(userId -> bindUser(userId, registrationId, profile)); } else { //当用户不存在时自动创建 -- Gitee From 493893ac8768f0dcd109744513c6996510cdcb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 10 Dec 2020 11:57:22 +0800 Subject: [PATCH 128/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=82=AE=E7=AE=B1?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 21 ++++++---- ...iguration.java => NonceConfiguration.java} | 35 +++++++++++----- .../oauth2/controller/ConnectController.java | 17 +++++--- .../{service => nonce}/AliyunSmsService.java | 6 +-- .../oauth2/nonce/EmailNonceHandler.java | 41 +++++++++++++++++++ .../oauth2/nonce/MobileNonceHandler.java | 36 ++++++++++++++++ .../NonceAuthenticationFilter.java} | 13 +++--- .../NonceAuthenticationProvider.java} | 19 ++++++--- .../NonceHandler.java} | 10 +++-- .../NonceHandlerImpl.java} | 32 +++++++++------ .../NonceTokenGranter.java} | 14 +++---- 11 files changed, 177 insertions(+), 67 deletions(-) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{MobileConfiguration.java => NonceConfiguration.java} (54%) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{service => nonce}/AliyunSmsService.java (98%) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{mobile/MobileNonceAuthenticationFilter.java => nonce/NonceAuthenticationFilter.java} (86%) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{mobile/MobileNonceAuthenticationProvider.java => nonce/NonceAuthenticationProvider.java} (71%) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{mobile/MobileNonceHandler.java => nonce/NonceHandler.java} (42%) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{service/MobileNonceService.java => nonce/NonceHandlerImpl.java} (65%) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{mobile/MobileTokenGranter.java => nonce/NonceTokenGranter.java} (84%) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index e5d1dc7..f17423d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -27,9 +27,9 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; -import cn.seqdata.oauth2.mobile.MobileNonceAuthenticationProvider; -import cn.seqdata.oauth2.mobile.MobileNonceHandler; -import cn.seqdata.oauth2.mobile.MobileTokenGranter; +import cn.seqdata.oauth2.nonce.NonceAuthenticationProvider; +import cn.seqdata.oauth2.nonce.NonceHandler; +import cn.seqdata.oauth2.nonce.NonceTokenGranter; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; @@ -47,8 +47,8 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu private final AuthorizationServerTokenServices tokenServices; private final TokenStore tokenStore; private final TokenEnhancer tokenEnhancer; - private final MobileNonceHandler mobileNonceHandler; private final ClientDetailRepo clientDetailRepo; + private final List nonceHandlers; @Override public void configure(AuthorizationServerSecurityConfigurer security) { @@ -88,12 +88,15 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory)); tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); - //自定义的手机验证码登录 + + //自定义的手机/邮箱验证码登录 //!!!必须在用户名密码验证前面 - if(Objects.nonNull(mobileNonceHandler)) { - MobileNonceAuthenticationProvider mobileNonceProvider = new MobileNonceAuthenticationProvider(userDetailsManager, mobileNonceHandler); - tokenGranters.add(new MobileTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory)); - } + NonceAuthenticationProvider mobileNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByMobile, userDetailsManager, nonceHandlers); + tokenGranters.add(new NonceTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.MOBILE)); + + NonceAuthenticationProvider emailNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByEmail, userDetailsManager, nonceHandlers); + tokenGranters.add(new NonceTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.EMAIL)); + //用户名密码验证 if(Objects.nonNull(authenticationManager)) { tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java similarity index 54% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java index bf4b66b..28aad03 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/MobileConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java @@ -6,13 +6,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.AuthenticationException; -import cn.seqdata.oauth2.mobile.MobileNonceHandler; +import cn.seqdata.oauth2.nonce.AliyunSmsService; +import cn.seqdata.oauth2.nonce.EmailNonceHandler; +import cn.seqdata.oauth2.nonce.MobileNonceHandler; +import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; -import cn.seqdata.oauth2.service.AliyunSmsService; -import cn.seqdata.oauth2.service.MobileNonceService; /** * Author: jrxian @@ -20,7 +22,7 @@ import cn.seqdata.oauth2.service.MobileNonceService; */ @Configuration @EnableConfigurationProperties({AliyunProperties.class, AliyunSmsProperties.class}) -public class MobileConfiguration { +public class NonceConfiguration { @Bean @ConditionalOnProperty(value = "aliyun.sms.enabled") @@ -30,22 +32,33 @@ public class MobileConfiguration { @Bean @ConditionalOnBean(AliyunSmsService.class) - public MobileNonceHandler aliyunMobileNonceService(UserAccountRepo accountRepo, AliyunSmsService smsService) { - return new MobileNonceService(accountRepo, smsService); + public NonceHandler mobileNonceService(UserAccountRepo accountRepo, AliyunSmsService smsService) { + return new MobileNonceHandler(accountRepo, smsService); } @Bean - @ConditionalOnMissingBean(AliyunSmsService.class) - public MobileNonceHandler anonymousMobileNonceHandler() { - return new MobileNonceHandler() { + @ConditionalOnBean(JavaMailSender.class) + public NonceHandler emailNonceService(UserAccountRepo accountRepo, JavaMailSender mailSender) { + return new EmailNonceHandler(accountRepo, mailSender); + } + + @Bean + @ConditionalOnMissingBean({AliyunSmsService.class, JavaMailSender.class}) + public NonceHandler anonymousMobileNonceHandler() { + return new NonceHandler() { + @Override + public boolean support(String username) { + return true; + } + @Override public void nonce(String mobile, int expire) { - throw new AuthenticationServiceException("未实现短信验证码登录功能"); + throw new AuthenticationServiceException("未实现验证码登录功能"); } @Override public void check(String mobile, int nonce) throws AuthenticationException { - throw new AuthenticationServiceException("未实现短信验证码登录功能"); + throw new AuthenticationServiceException("未实现验证码登录功能"); } }; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index e1a0b80..245beb7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -4,6 +4,7 @@ import java.net.URI; import java.security.GeneralSecurityException; import java.security.Principal; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; import javax.security.auth.login.AccountNotFoundException; @@ -22,7 +23,7 @@ import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.UserAccount; -import cn.seqdata.oauth2.mobile.MobileNonceHandler; +import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; @@ -42,7 +43,7 @@ public class ConnectController { private final UserAccountRepo accountRepo; private final UserService userService; private final UserDetailRepo userDetailRepo; - private final MobileNonceHandler mobileNonceHandler; + private final List nonceHandlers; @GetMapping public Map list(Principal principal) { @@ -69,10 +70,14 @@ public class ConnectController { /** * 向手机发送一次性验证码 */ - @GetMapping(value = "/mobile") - public ResponseEntity mobile(String mobile, HttpServletRequest request) { - mobileNonceHandler.nonce(mobile, 5 * DateTimeConstants.SECONDS_PER_MINUTE); - return ResponseEntity.ok("验证码已经发送到您手机,5分钟内输入有效!"); + @GetMapping(value = "/nonce") + public ResponseEntity nonce(String username, HttpServletRequest request) { + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) { + nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); + } + } + return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); } @GetMapping(value = "/{registrationId}") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AliyunSmsService.java similarity index 98% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AliyunSmsService.java index 97866ca..43e5b7b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AliyunSmsService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AliyunSmsService.java @@ -1,8 +1,10 @@ -package cn.seqdata.oauth2.service; +package cn.seqdata.oauth2.nonce; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; @@ -15,8 +17,6 @@ import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ServerException; import com.aliyuncs.profile.DefaultProfile; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; import cn.seqdata.oauth2.AliyunProperties; import cn.seqdata.oauth2.AliyunSmsProperties; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java new file mode 100644 index 0000000..fe0e392 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java @@ -0,0 +1,41 @@ +package cn.seqdata.oauth2.nonce; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.mail.MailSender; +import org.springframework.mail.SimpleMailMessage; + +import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; + +/** + * @author jrxian + * @date 2020-06-21 21:26 + */ +public class EmailNonceHandler extends NonceHandlerImpl { + private final MailSender mailSender; + + public EmailNonceHandler(UserAccountRepo accountRepo, MailSender mailSender) { + super(accountRepo); + this.mailSender = mailSender; + } + + @Override + public boolean support(String username) { + return StringUtils.containsAny(username, '@'); + } + + @Override + protected Optional findUserAccount(String username) { + return accountRepo.findByEmail(username); + } + + @Override + protected void sendNonce(String username, int nonce) { + SimpleMailMessage mailMessage = new SimpleMailMessage(); + mailMessage.setTo(username); + mailMessage.setSubject(String.format("验证码%06d", nonce)); + mailSender.send(mailMessage); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java new file mode 100644 index 0000000..120696a --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java @@ -0,0 +1,36 @@ +package cn.seqdata.oauth2.nonce; + +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; + +import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; + +/** + * @author jrxian + * @date 2020-06-21 21:26 + */ +public class MobileNonceHandler extends NonceHandlerImpl { + private final AliyunSmsService smsService; + + public MobileNonceHandler(UserAccountRepo accountRepo, AliyunSmsService smsService) { + super(accountRepo); + this.smsService = smsService; + } + + @Override + public boolean support(String username) { + return !StringUtils.containsAny(username, '@'); + } + + @Override + protected Optional findUserAccount(String username) { + return accountRepo.findByMobile(username); + } + + @Override + protected void sendNonce(String username, int nonce) { + smsService.accept(username, nonce); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationFilter.java similarity index 86% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationFilter.java index 2450873..abe7de1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationFilter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationFilter.java @@ -1,4 +1,4 @@ -package cn.seqdata.oauth2.mobile; +package cn.seqdata.oauth2.nonce; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -20,12 +20,13 @@ import cn.seqdata.oauth2.OAuth2Constants; * Author: jrxian * Date: 2020-01-22 12:06 */ -public class MobileNonceAuthenticationFilter extends AbstractAuthenticationProcessingFilter { - +public class NonceAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + private final String grantType; private boolean postOnly = true; - public MobileNonceAuthenticationFilter() { - super(new AntPathRequestMatcher("/mobile", HttpMethod.POST.name())); + public NonceAuthenticationFilter(String grantType) { + super(new AntPathRequestMatcher("/" + grantType, HttpMethod.POST.name())); + this.grantType = grantType; } public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { @@ -45,7 +46,7 @@ public class MobileNonceAuthenticationFilter extends AbstractAuthenticationProce protected String obtainUsername(HttpServletRequest request) { String username = request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); if(StringUtils.isBlank(username)) { - username = request.getParameter(OAuth2Constants.MOBILE); + username = request.getParameter(grantType); } return ObjectUtils.defaultIfNull(username, StringUtils.EMPTY); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationProvider.java similarity index 71% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationProvider.java index 97214db..6d13179 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceAuthenticationProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationProvider.java @@ -1,6 +1,8 @@ -package cn.seqdata.oauth2.mobile; +package cn.seqdata.oauth2.nonce; +import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; import lombok.AllArgsConstructor; import org.apache.commons.lang3.math.NumberUtils; @@ -18,14 +20,15 @@ import cn.seqdata.oauth2.service.JpaUserDetailsManager; * Date: 2020-01-26 00:52 */ @AllArgsConstructor -public class MobileNonceAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { +public class NonceAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { + private final BiFunction userDetailsLoader; private final JpaUserDetailsManager userDetailsManager; - private final MobileNonceHandler mobileNonceHandler; + private final List nonceHandlers; @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { try { - return Optional.ofNullable(userDetailsManager.loadUserByMobile(username)) + return Optional.ofNullable(userDetailsLoader.apply(userDetailsManager, username)) .orElseThrow(() -> new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation")); } catch(AuthenticationServiceException ex) { throw ex; @@ -36,8 +39,12 @@ public class MobileNonceAuthenticationProvider extends AbstractUserDetailsAuthen @Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { - String mobile = String.valueOf(authentication.getPrincipal()); + String username = String.valueOf(authentication.getPrincipal()); int nonce = NumberUtils.toInt(String.valueOf(authentication.getCredentials()), -1); - mobileNonceHandler.check(mobile, nonce); + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) { + nonceHandler.check(username, nonce); + } + } } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java similarity index 42% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java index 59c8022..66304b4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java @@ -1,17 +1,19 @@ -package cn.seqdata.oauth2.mobile; +package cn.seqdata.oauth2.nonce; /** * Author: jrxian * Date: 2020-01-26 10:28 */ -public interface MobileNonceHandler { +public interface NonceHandler { + boolean support(String username); + /** * 生成一次性验证码 */ - void nonce(String mobile, int expire); + void nonce(String username, int expire); /** * 检查一次性验证码 */ - void check(String mobile, int nonce); + void check(String username, int nonce); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java similarity index 65% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index f135753..bfa7f08 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/MobileNonceService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -1,6 +1,8 @@ -package cn.seqdata.oauth2.service; +package cn.seqdata.oauth2.nonce; import java.util.Objects; +import java.util.Optional; +import lombok.AllArgsConstructor; import org.apache.commons.lang3.RandomUtils; import org.joda.time.DateTime; @@ -8,11 +10,10 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.www.NonceExpiredException; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ServerWebInputException; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.rbac.UserAccount; -import cn.seqdata.oauth2.mobile.MobileNonceHandler; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; /** @@ -20,14 +21,18 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; * @date 2020-06-21 21:26 */ @AllArgsConstructor -public class MobileNonceService implements MobileNonceHandler { - private final UserAccountRepo accountRepo; - private final AliyunSmsService smsService; +public abstract class NonceHandlerImpl implements NonceHandler { + protected final UserAccountRepo accountRepo; + + protected abstract Optional findUserAccount(String username); + + protected abstract void sendNonce(String username, int nonce); + @Transactional @Override - public void nonce(String mobile, int expire) { - UserAccount account = accountRepo.findByMobile(mobile) - .orElseThrow(() -> new ServerWebInputException("无法用此手机号登录")); + public void nonce(String username, int expire) { + UserAccount account = findUserAccount(username) + .orElseThrow(() -> new ServerWebInputException("无法用此账号登录")); int nonce = RandomUtils.nextInt(100000, 999999); DateTime now = DateTime.now(); @@ -35,13 +40,14 @@ public class MobileNonceService implements MobileNonceHandler { account.setNonceExpire(now.plusSeconds(expire)); accountRepo.save(account); - smsService.accept(mobile, nonce); + sendNonce(username, nonce); } + @Transactional @Override - public void check(String mobile, int nonce) throws AuthenticationException { - UserAccount account = accountRepo.findByMobile(mobile) - .orElseThrow(() -> new UsernameNotFoundException("无法用此手机号登录")); + public void check(String username, int nonce) throws AuthenticationException { + UserAccount account = findUserAccount(username) + .orElseThrow(() -> new UsernameNotFoundException("无法用此账号登录")); try { DateTime expire = account.getNonceExpire(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java similarity index 84% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java index 2acead0..44fa268 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/mobile/MobileTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java @@ -1,4 +1,4 @@ -package cn.seqdata.oauth2.mobile; +package cn.seqdata.oauth2.nonce; import java.util.LinkedHashMap; import java.util.Map; @@ -14,21 +14,17 @@ import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import cn.seqdata.oauth2.OAuth2Constants; - /** * Author: jrxian * Date: 2020-01-26 00:16 */ -public class MobileTokenGranter extends AbstractTokenGranter { - public static final String GRANT_TYPE = OAuth2Constants.MOBILE; - +public class NonceTokenGranter extends AbstractTokenGranter { private final AuthenticationProvider authenticationProvider; - public MobileTokenGranter(AuthenticationProvider authenticationProvider, + public NonceTokenGranter(AuthenticationProvider authenticationProvider, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, - OAuth2RequestFactory requestFactory) { - super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + OAuth2RequestFactory requestFactory, String grantType) { + super(tokenServices, clientDetailsService, requestFactory, grantType); this.authenticationProvider = authenticationProvider; } -- Gitee From 10233f3bd4065735dd9da1a41d9c05782433b147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 10 Dec 2020 14:53:46 +0800 Subject: [PATCH 129/228] + username,mobile,email bind --- .../oauth2/controller/ConnectController.java | 41 ++++++++++++++++++- .../oauth2/controller/CurrentController.java | 24 +++++------ 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 245beb7..3c29c5d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -15,6 +15,7 @@ import lombok.AllArgsConstructor; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.web.bind.annotation.*; @@ -42,6 +43,7 @@ public class ConnectController { private final ClientRegistrationRepository repository; private final UserAccountRepo accountRepo; private final UserService userService; + private final PasswordEncoder passwordEncoder; private final UserDetailRepo userDetailRepo; private final List nonceHandlers; @@ -68,7 +70,7 @@ public class ConnectController { } /** - * 向手机发送一次性验证码 + * 向手机或邮箱发送一次性验证码 */ @GetMapping(value = "/nonce") public ResponseEntity nonce(String username, HttpServletRequest request) { @@ -80,6 +82,28 @@ public class ConnectController { return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); } + @GetMapping(value = "/password") + public void bindUsername(String username, String password, Principal principal) { + userService.loadUser(OAuth2Constants.PASSWORD, username) + .ifPresent(user -> { + if(passwordEncoder.matches(password, user.getPassword())) { + userService.bindUser(Objects.requireNonNull(user.getId()), + SecurityUtils.grantType(principal), + SecurityUtils.username(principal)); + } + }); + } + + @GetMapping(value = "/mobile") + public void bindMobile(String mobile, int nonce, Principal principal) { + bindNonce(OAuth2Constants.MOBILE, mobile, nonce, principal); + } + + @GetMapping(value = "/email") + public void bindEmail(String email, int nonce, Principal principal) { + bindNonce(OAuth2Constants.EMAIL, email, nonce, principal); + } + @GetMapping(value = "/{registrationId}") public ResponseEntity bind(@PathVariable String registrationId, HttpServletRequest request) { ClientRegistration registration = repository.findByRegistrationId(registrationId); @@ -104,7 +128,7 @@ public class ConnectController { .build(); } - @Transactional(rollbackOn = Exception.class) + @Transactional @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) throws GeneralSecurityException { String username = SecurityUtils.username(principal); @@ -118,4 +142,17 @@ public class ConnectController { userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); } } + + private void bindNonce(String grantType, String username, int nonce, Principal principal) { + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) { + nonceHandler.check(username, nonce); + userService.loadUser(grantType, username) + .map(UserAccount::getId) + .ifPresent(userId -> userService.bindUser(userId, + SecurityUtils.grantType(principal), + SecurityUtils.username(principal))); + } + } + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 57900a4..eb3edf4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -101,34 +101,34 @@ public class CurrentController { } /** - * 修改邮箱 + * 修改手机号 */ - @PatchMapping(value = "/email") - public void updateEmail(Principal principal, @RequestBody UserAccountParam param) throws BindException { + @PatchMapping(value = "/mobile") + public void updateMobile(Principal principal, @RequestBody UserAccountParam param) throws BindException { UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); - checkField(ex, param, OAuth2Constants.EMAIL, UserAccountParam::getEmail); - checkExist(ex, param, OAuth2Constants.EMAIL, entity, UserAccountParam::getEmail, accountRepo::findByEmail); + checkField(ex, param, OAuth2Constants.MOBILE, UserAccountParam::getMobile); + checkExist(ex, param, OAuth2Constants.MOBILE, entity, UserAccountParam::getMobile, accountRepo::findByMobile); checkErrors(ex); - entity.setEmail(param.getEmail()); + entity.setMobile(param.getMobile()); accountRepo.save(entity); } /** - * 修改手机号 + * 修改邮箱 */ - @PatchMapping(value = "/mobile") - public void updateMobile(Principal principal, @RequestBody UserAccountParam param) throws BindException { + @PatchMapping(value = "/email") + public void updateEmail(Principal principal, @RequestBody UserAccountParam param) throws BindException { UserAccount entity = getUserAccount(principal); BindException ex = new BindException(param, objectName); - checkField(ex, param, OAuth2Constants.MOBILE, UserAccountParam::getMobile); - checkExist(ex, param, OAuth2Constants.MOBILE, entity, UserAccountParam::getMobile, accountRepo::findByMobile); + checkField(ex, param, OAuth2Constants.EMAIL, UserAccountParam::getEmail); + checkExist(ex, param, OAuth2Constants.EMAIL, entity, UserAccountParam::getEmail, accountRepo::findByEmail); checkErrors(ex); - entity.setMobile(param.getMobile()); + entity.setEmail(param.getEmail()); accountRepo.save(entity); } -- Gitee From 8413f0915fd47c867210daac57a16e6295408b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 11 Dec 2020 09:53:35 +0800 Subject: [PATCH 130/228] +PointcutAdvisor --- .../seqdata/oauth2/AuditPointcutAdvisor.java | 58 +++++++++++++++++ .../oauth2/controller/JodaController.java | 3 +- .../src/main/resources/bootstrap.yml | 2 +- .../oauth2/controller/ConnectController.java | 11 +++- seqdata-cloud-gateway/pom.xml | 5 -- .../seqdata/gateway/GatewayConfiguration.java | 6 +- .../filter/logging/JdbcUrlRecorder.java | 14 +++++ .../gateway/filter/logging/LogRecorder.java | 10 +++ .../filter/logging/LoggingGlobalFilter.java | 2 - .../filter/logging/RabbitRecorder.java | 20 ++++++ .../gateway/filter/logging/UrlRecord.java | 62 +++++++++++++++++++ 11 files changed, 180 insertions(+), 13 deletions(-) create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/JdbcUrlRecorder.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LogRecorder.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/RabbitRecorder.java create mode 100644 seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java new file mode 100644 index 0000000..6dd437c --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java @@ -0,0 +1,58 @@ +package cn.seqdata.oauth2; + +import java.lang.reflect.Method; + +import org.aopalliance.aop.Advice; +import org.aopalliance.intercept.MethodInterceptor; +import org.springframework.aop.ClassFilter; +import org.springframework.aop.MethodMatcher; +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AbstractPointcutAdvisor; +import org.springframework.aop.support.annotation.AnnotationClassFilter; +import org.springframework.aop.support.annotation.AnnotationMethodMatcher; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author jrxian + * @date 2020/12/10 21:46 + */ +@Component +public class AuditPointcutAdvisor extends AbstractPointcutAdvisor { + @NonNull + @Override + public Pointcut getPointcut() { + return new Pointcut() { + @NonNull + @Override + public ClassFilter getClassFilter() { + return new AnnotationClassFilter(RestController.class); + } + + @NonNull + @Override + public MethodMatcher getMethodMatcher() { + return new AnnotationMethodMatcher(RequestMapping.class, true); + } + }; + } + + @NonNull + @Override + public Advice getAdvice() { + return (MethodInterceptor) invocation -> { + Method method = invocation.getMethod(); + Object[] arguments = invocation.getArguments(); + System.err.println(invocation); + try { + Object returnObj = invocation.proceed(); + return returnObj; + } catch(Exception ex) { + System.err.println(ex.getClass()); + throw ex; + } + }; + } +} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java index 08836cc..c4ff631 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java @@ -21,7 +21,8 @@ import org.springframework.web.bind.annotation.RestController; public class JodaController { @GetMapping("/date") public LocalDate date(@RequestParam(required = false) LocalDate key) { - return key; + throw new RuntimeException(); +// return key; } @GetMapping("/time") diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml index 8636e73..a3f8627 100644 --- a/seqdata-cloud-authc/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authc/src/main/resources/bootstrap.yml @@ -13,4 +13,4 @@ spring: group: ${spring.cloud.nacos.discovery.group} file-extension: ${NACOS_FILE_EXT:yml} server: - port: 0 \ No newline at end of file + port: 8080 \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 3c29c5d..3d6b340 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -82,8 +82,11 @@ public class ConnectController { return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); } + /** + * 通过用户名密码验证绑定第三方账号 + */ @GetMapping(value = "/password") - public void bindUsername(String username, String password, Principal principal) { + public void bindPassword(String username, String password, Principal principal) { userService.loadUser(OAuth2Constants.PASSWORD, username) .ifPresent(user -> { if(passwordEncoder.matches(password, user.getPassword())) { @@ -94,11 +97,17 @@ public class ConnectController { }); } + /** + * 通过手机号验证绑定第三方账号 + */ @GetMapping(value = "/mobile") public void bindMobile(String mobile, int nonce, Principal principal) { bindNonce(OAuth2Constants.MOBILE, mobile, nonce, principal); } + /** + * 通过邮箱验证绑定第三方账号 + */ @GetMapping(value = "/email") public void bindEmail(String email, int nonce, Principal principal) { bindNonce(OAuth2Constants.EMAIL, email, nonce, principal); diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index 4b1625c..bf8ff2b 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -39,11 +39,6 @@ true - - cn.seqdata.cloud - seqdata-cloud-log - ${project.version} - cn.seqdata.cloud seqdata-cloud-auth-client diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index d06d3a9..d849399 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -16,11 +16,11 @@ import cn.seqdata.gateway.filter.authc.IgnoringProperties; import cn.seqdata.gateway.filter.authc.PathMatcherGlobalFilter; import cn.seqdata.gateway.filter.captcha.CaptchaGlobalFilter; import cn.seqdata.gateway.filter.captcha.CaptchaProperties; +import cn.seqdata.gateway.filter.logging.LogRecorder; import cn.seqdata.gateway.filter.logging.LoggingGlobalFilter; +import cn.seqdata.gateway.filter.logging.RabbitRecorder; +import cn.seqdata.gateway.filter.logging.UrlRecord; import cn.seqdata.gateway.filter.verify.HmacVerifyGlobalFilter; -import cn.seqdata.log.LogRecorder; -import cn.seqdata.log.UrlRecord; -import cn.seqdata.log.rabbit.RabbitRecorder; import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; /** diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/JdbcUrlRecorder.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/JdbcUrlRecorder.java new file mode 100644 index 0000000..2f60886 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/JdbcUrlRecorder.java @@ -0,0 +1,14 @@ +package cn.seqdata.gateway.filter.logging; + +import org.springframework.jdbc.core.support.JdbcDaoSupport; + +/** + * Author: jrxian + * Date: 2020-02-13 09:32 + */ +public class JdbcUrlRecorder extends JdbcDaoSupport implements LogRecorder { + + @Override + public void accept(UrlRecord record) { + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LogRecorder.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LogRecorder.java new file mode 100644 index 0000000..8f42d3a --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LogRecorder.java @@ -0,0 +1,10 @@ +package cn.seqdata.gateway.filter.logging; + +import java.util.function.Consumer; + +/** + * Author: jrxian + * Date: 2020-02-13 09:31 + */ +public interface LogRecorder extends Consumer { +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java index 379c383..47f0d85 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -21,8 +21,6 @@ import reactor.core.publisher.Mono; import cn.seqdata.gateway.filter.authc.BearerTokenResolver; import cn.seqdata.gateway.filter.authc.DefaultBearerTokenResolver; -import cn.seqdata.log.LogRecorder; -import cn.seqdata.log.UrlRecord; /** * Author: jrxian diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/RabbitRecorder.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/RabbitRecorder.java new file mode 100644 index 0000000..64fe600 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/RabbitRecorder.java @@ -0,0 +1,20 @@ +package cn.seqdata.gateway.filter.logging; + +import org.springframework.amqp.rabbit.core.RabbitTemplate; + +/** + * Author: jrxian + * Date: 2020-02-15 02:22 + */ +public class RabbitRecorder implements LogRecorder { + private final RabbitTemplate rabbitTemplate; + + public RabbitRecorder(RabbitTemplate rabbitTemplate) { + this.rabbitTemplate = rabbitTemplate; + } + + @Override + public void accept(T record) { + rabbitTemplate.convertAndSend(record); + } +} diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java new file mode 100644 index 0000000..2fd9d30 --- /dev/null +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java @@ -0,0 +1,62 @@ +package cn.seqdata.gateway.filter.logging; + +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +/** + * Author: jrxian + * Date: 2020-02-13 08:51 + */ +@lombok.Getter +@lombok.Setter +@lombok.Builder +@lombok.ToString +@NoArgsConstructor +@AllArgsConstructor +public class UrlRecord implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 开始时间 + */ + private long start; + /** + * 结束时间 + */ + private long end; + + /** + * 客户端主机 + */ + private String host; + /** + * 客户端端口 + */ + private int port; + + /** + * 请求方法,如GET/POST... + */ + private String method; + /** + * 请求路径 + */ + private String path; + /** + * 请求参数 + */ + private String queryParams; + /** + * 访问令牌(如果有) + */ + private String token; + /** + * 访问用户名(如果有) + */ + private String principal; + /** + * 返回状态码(HttpStatus) + */ + private int code; +} -- Gitee From 6e9429a3c60ef926beab36047a8c7921f2f1133b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 11 Dec 2020 10:54:32 +0800 Subject: [PATCH 131/228] =?UTF-8?q?=E8=B0=83=E6=95=B4UserInfo=E7=9A=84?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java | 7 +++++++ .../java/cn/seqdata/oauth2/controller/JodaController.java | 3 +-- .../java/cn/seqdata/oauth2/controller/WxAppController.java | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java index 6dd437c..2596c43 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java @@ -14,6 +14,10 @@ import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +import cn.seqdata.oauth2.client.util.ServiceSecurityUtils; /** * @author jrxian @@ -43,6 +47,9 @@ public class AuditPointcutAdvisor extends AbstractPointcutAdvisor { @Override public Advice getAdvice() { return (MethodInterceptor) invocation -> { + RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); + System.err.println(ServiceSecurityUtils.userId()); + Method method = invocation.getMethod(); Object[] arguments = invocation.getArguments(); System.err.println(invocation); diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java index c4ff631..08836cc 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/JodaController.java @@ -21,8 +21,7 @@ import org.springframework.web.bind.annotation.RestController; public class JodaController { @GetMapping("/date") public LocalDate date(@RequestParam(required = false) LocalDate key) { - throw new RuntimeException(); -// return key; + return key; } @GetMapping("/time") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index af07dd4..9dc060b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -50,8 +50,8 @@ public class WxAppController { UserInfo userInfo = objectMapper.readValue(decryptedData, UserInfo.class); Map attributes = new HashMap<>(); - WxAppUtils.toAttributes(attributes, response); WxAppUtils.toAttributes(attributes, userInfo); + WxAppUtils.toAttributes(attributes, response); DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); @@ -86,8 +86,8 @@ public class WxAppController { PhoneInfo phoneInfo = objectMapper.readValue(decryptedData, PhoneInfo.class); Map attributes = new HashMap<>(); - WxAppUtils.toAttributes(attributes, response); WxAppUtils.toAttributes(attributes, phoneInfo); + WxAppUtils.toAttributes(attributes, response); userService.loadUser(OAuth2Constants.MOBILE, phoneInfo.purePhoneNumber) .map(UserAccount::getId) -- Gitee From 59873c9a97139e19d49a2448d45af84cfbfbb5c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 13 Dec 2020 11:48:44 +0800 Subject: [PATCH 132/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authc/pom.xml | 2 +- .../seqdata/oauth2/AuditPointcutAdvisor.java | 65 ------------------- .../oauth2/AuthcServerApplication.java | 3 + .../oauth2/controller/SyslogController.java | 33 ++++++++++ .../src/main/resources/bootstrap.yml | 2 +- .../oauth2/AuthzServerConfiguration.java | 5 +- .../oauth2/controller/WxAppController.java | 2 - .../src/main/resources/application.yml | 8 +++ 8 files changed, 49 insertions(+), 71 deletions(-) delete mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java create mode 100644 seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SyslogController.java diff --git a/seqdata-cloud-authc/pom.xml b/seqdata-cloud-authc/pom.xml index bb610f5..1cab73c 100644 --- a/seqdata-cloud-authc/pom.xml +++ b/seqdata-cloud-authc/pom.xml @@ -21,7 +21,7 @@ cn.seqdata.cloud seqdata-cloud-starter - 2.2.1-SNAPSHOT + ${project.version} javax.persistence diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java deleted file mode 100644 index 2596c43..0000000 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuditPointcutAdvisor.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.seqdata.oauth2; - -import java.lang.reflect.Method; - -import org.aopalliance.aop.Advice; -import org.aopalliance.intercept.MethodInterceptor; -import org.springframework.aop.ClassFilter; -import org.springframework.aop.MethodMatcher; -import org.springframework.aop.Pointcut; -import org.springframework.aop.support.AbstractPointcutAdvisor; -import org.springframework.aop.support.annotation.AnnotationClassFilter; -import org.springframework.aop.support.annotation.AnnotationMethodMatcher; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.request.RequestAttributes; -import org.springframework.web.context.request.RequestContextHolder; - -import cn.seqdata.oauth2.client.util.ServiceSecurityUtils; - -/** - * @author jrxian - * @date 2020/12/10 21:46 - */ -@Component -public class AuditPointcutAdvisor extends AbstractPointcutAdvisor { - @NonNull - @Override - public Pointcut getPointcut() { - return new Pointcut() { - @NonNull - @Override - public ClassFilter getClassFilter() { - return new AnnotationClassFilter(RestController.class); - } - - @NonNull - @Override - public MethodMatcher getMethodMatcher() { - return new AnnotationMethodMatcher(RequestMapping.class, true); - } - }; - } - - @NonNull - @Override - public Advice getAdvice() { - return (MethodInterceptor) invocation -> { - RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); - System.err.println(ServiceSecurityUtils.userId()); - - Method method = invocation.getMethod(); - Object[] arguments = invocation.getArguments(); - System.err.println(invocation); - try { - Object returnObj = invocation.proceed(); - return returnObj; - } catch(Exception ex) { - System.err.println(ex.getClass()); - throw ex; - } - }; - } -} diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java index 29cd75a..d0ad91e 100644 --- a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/AuthcServerApplication.java @@ -4,12 +4,15 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import cn.seqdata.syslog.EnableSyslog; + /** * Author: jrxian * Date: 2020-01-20 00:37 */ @SpringBootApplication @EnableDiscoveryClient +@EnableSyslog public class AuthcServerApplication { public static void main(String[] args) { diff --git a/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SyslogController.java b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SyslogController.java new file mode 100644 index 0000000..f2c4921 --- /dev/null +++ b/seqdata-cloud-authc/src/main/java/cn/seqdata/oauth2/controller/SyslogController.java @@ -0,0 +1,33 @@ +package cn.seqdata.oauth2.controller; + +import javax.annotation.security.PermitAll; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import cn.seqdata.oauth2.client.entity.NamedObject; +import cn.seqdata.syslog.Syslog; + +/** + * @author jrxian + * @date 2020/12/12 15:50 + */ +@Api("系统日志") +@PermitAll +@RestController +@RequestMapping("/syslog") +public class SyslogController { + + @ApiOperation("获取数据") + @Syslog(value = "用户`#{details['name']}`登录#{errorCode==null?'成功':'失败'}") + @GetMapping("/{id}") + public long get(@PathVariable("id") long id) { + return id; + } + + @ApiOperation("保存数据") + @PutMapping + public void put(NamedObject object) { + } +} diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml index a3f8627..8636e73 100644 --- a/seqdata-cloud-authc/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authc/src/main/resources/bootstrap.yml @@ -13,4 +13,4 @@ spring: group: ${spring.cloud.nacos.discovery.group} file-extension: ${NACOS_FILE_EXT:yml} server: - port: 8080 \ No newline at end of file + port: 0 \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index cb22535..abe10a6 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -13,6 +13,7 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; +import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.DefaultTokenEnhancer; import cn.seqdata.oauth2.service.DefaultTokenServices; @@ -38,9 +39,9 @@ public class AuthzServerConfiguration { } @Bean - public TokenStore tokenStore(StringRedisTemplate redisTemplate) { + public TokenStore tokenStore(StringRedisTemplate redisTemplate, PasswordPolicy passwordPolicy) { RedisTokenStore tokenStore = new RedisTokenStore(redisTemplate); - tokenStore.setMaximumSessions(-1); + tokenStore.setMaximumSessions(passwordPolicy.getMaximumSessions()); return tokenStore; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 9dc060b..e89f75c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -6,7 +6,6 @@ import java.util.HashMap; import java.util.Map; import lombok.AllArgsConstructor; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.web.bind.annotation.GetMapping; @@ -68,7 +67,6 @@ public class WxAppController { Map attributes = new HashMap<>(); WxAppUtils.toAttributes(attributes, response); - SimpleGrantedAuthority defaultAuthority = new SimpleGrantedAuthority(DefaultRole.anonymous.name()); DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, WxAppUtils.WXAPP_ATTR_KEY); WechatUserProfile profile = new WechatUserProfile(remoteUser); userService.updateUser(id, profile, principal); diff --git a/seqdata-cloud-gateway/src/main/resources/application.yml b/seqdata-cloud-gateway/src/main/resources/application.yml index 0db7b67..079f904 100644 --- a/seqdata-cloud-gateway/src/main/resources/application.yml +++ b/seqdata-cloud-gateway/src/main/resources/application.yml @@ -1,3 +1,11 @@ +logging: + level: + root: info + cn.seqdata: debug + com.netflix: warn + com.alibaba.nacos: warn + org.springframework: warn + io.swagger.models.parameters.AbstractSerializableParameter: error spring: application: name: gateway -- Gitee From 9ad72fc02896553e821b4e1da20121fea340ea18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 13 Dec 2020 22:41:28 +0800 Subject: [PATCH 133/228] =?UTF-8?q?=E5=AE=8C=E5=96=84authz=E7=9A=84?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 5 ++ .../seqdata/oauth2/BizlogConfiguration.java | 75 +++++++++++++++++++ .../oauth2/WebSecurityConfiguration.java | 13 +--- .../oauth2/controller/ConnectController.java | 25 ++++--- .../oauth2/controller/CurrentController.java | 33 ++++---- .../oauth2/controller/PasswordController.java | 13 +++- .../oauth2/controller/WxAppController.java | 9 +++ 7 files changed, 132 insertions(+), 41 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 775efc0..9e3a7bc 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -62,6 +62,11 @@ seqdata-cloud-oauth2 ${project.version} + + cn.seqdata.cloud + seqdata-cloud-syslog + ${project.version} + com.microsoft.sqlserver diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java new file mode 100644 index 0000000..cc2ebdc --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -0,0 +1,75 @@ +package cn.seqdata.oauth2; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; +import org.springframework.security.authentication.event.AuthenticationSuccessEvent; +import org.springframework.security.authentication.event.LogoutSuccessEvent; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; + +import cn.seqdata.syslog.DefaultSyslogResolver; +import cn.seqdata.syslog.EnableSyslog; +import cn.seqdata.syslog.SyslogConsumer; +import cn.seqdata.syslog.SyslogRecord; + +/** + * @author jrxian + * @date 2020/12/13 13:24 + */ +@Configuration +@EnableSyslog +public class BizlogConfiguration { + @Value("${spring.application.name}") + private String appName; + +// @Bean +// public SyslogConsumer jdbcSyslogConsumer(JdbcTemplate jdbcTemplate) { +// JdbcSyslogConsumer syslogConsumer = new JdbcSyslogConsumer(); +// syslogConsumer.setJdbcTemplate(jdbcTemplate); +// return syslogConsumer; +// } + + @Bean + public ApplicationListener authenticationSuccessListener(SyslogConsumer consumer) { + return event -> { + SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) + .eventId("login") + .eventName("登录"); + consumer.accept(builder.build()); + }; + } + + @Bean + public ApplicationListener authenticationFailureListener(SyslogConsumer consumer) { + return event -> { + AuthenticationException exception = event.getException(); + Class exceptionClass = exception.getClass(); + SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) + .eventId("login") + .eventName("登录") + .errorCode(exceptionClass.getSimpleName()); + consumer.accept(builder.build()); + }; + } + + @Bean + public ApplicationListener logoutSuccessListener(SyslogConsumer consumer) { + return event -> { + SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) + .eventId("logout") + .eventName("注销"); + consumer.accept(builder.build()); + }; + } + + private SyslogRecord.SyslogRecordBuilder logBuilder(Authentication authentication) { + return SyslogRecord.builder() + .eventTime(System.currentTimeMillis()) + .projectId(appName) + .sourceIp(DefaultSyslogResolver.remoteAddr()) + .username(DefaultSyslogResolver.username(authentication)); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java index eb1186e..bf2fd5b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/WebSecurityConfiguration.java @@ -3,7 +3,6 @@ package cn.seqdata.oauth2; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -23,19 +22,9 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/v2/api-docs/**", "/webjars/**", "/plugins/**"); } - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .sessionManagement() - .maximumSessions(1); - - super.configure(http); - } - @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { - AuthenticationManager authenticationManager = super.authenticationManagerBean(); - return authenticationManager; + return super.authenticationManagerBean(); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 3d6b340..a38fe84 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -12,6 +12,8 @@ import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; import lombok.AllArgsConstructor; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -31,11 +33,13 @@ import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; +import cn.seqdata.syslog.Syslog; /** * Author: jrxian * Date: 2020-01-24 01:13 */ +@Api("第三方账号绑定") @RestController @RequestMapping("/connect") @AllArgsConstructor @@ -72,6 +76,8 @@ public class ConnectController { /** * 向手机或邮箱发送一次性验证码 */ + @Syslog + @ApiOperation("发送验证码") @GetMapping(value = "/nonce") public ResponseEntity nonce(String username, HttpServletRequest request) { for(NonceHandler nonceHandler : nonceHandlers) { @@ -82,9 +88,8 @@ public class ConnectController { return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); } - /** - * 通过用户名密码验证绑定第三方账号 - */ + @Syslog + @ApiOperation("通过密码绑定") @GetMapping(value = "/password") public void bindPassword(String username, String password, Principal principal) { userService.loadUser(OAuth2Constants.PASSWORD, username) @@ -97,22 +102,22 @@ public class ConnectController { }); } - /** - * 通过手机号验证绑定第三方账号 - */ + @Syslog + @ApiOperation("通过手机号绑定") @GetMapping(value = "/mobile") public void bindMobile(String mobile, int nonce, Principal principal) { bindNonce(OAuth2Constants.MOBILE, mobile, nonce, principal); } - /** - * 通过邮箱验证绑定第三方账号 - */ + @Syslog + @ApiOperation("通过邮箱绑定") @GetMapping(value = "/email") public void bindEmail(String email, int nonce, Principal principal) { bindNonce(OAuth2Constants.EMAIL, email, nonce, principal); } + @Syslog + @ApiOperation("第三方账号绑定") @GetMapping(value = "/{registrationId}") public ResponseEntity bind(@PathVariable String registrationId, HttpServletRequest request) { ClientRegistration registration = repository.findByRegistrationId(registrationId); @@ -137,6 +142,8 @@ public class ConnectController { .build(); } + @Syslog + @ApiOperation("第三方账号解绑") @Transactional @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) throws GeneralSecurityException { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index eb3edf4..6fa5ac7 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -28,12 +28,13 @@ import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.AuthorityService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; +import cn.seqdata.syslog.Syslog; /** * Author: jrxian * Date: 2020-01-27 17:16 */ -@Api("当前登录用户信息") +@Api("当前登录用户") @RestController @RequestMapping("/current") @AllArgsConstructor @@ -67,27 +68,27 @@ public class CurrentController { }); } - @ApiOperation("令牌信息") + @ApiOperation("令牌") @GetMapping("/token") public OAuth2AccessToken token(Principal principal) { return tokenStore.readAccessToken(SecurityUtils.accessToken(principal)); } + @ApiOperation("角色") + @GetMapping("/authorities") + public Collection authorities(Principal principal) { + return SecurityUtils.authorities(principal); + } + + @Syslog @ApiOperation("注销") @DeleteMapping public void logout(Principal principal) { tokenServices.revokeToken(SecurityUtils.accessToken(principal)); } - @ApiOperation("拥有角色") - @GetMapping("/authorities") - public Collection authorities(Principal principal) { - return SecurityUtils.authorities(principal); - } - - /** - * 修改显示名称 - */ + @Syslog + @ApiOperation("修改显示名称") @PatchMapping(value = "/name") public void updateName(Principal principal, @RequestBody UserAccountParam param) throws BindException { UserAccount entity = getUserAccount(principal); @@ -100,9 +101,8 @@ public class CurrentController { accountRepo.save(entity); } - /** - * 修改手机号 - */ + @Syslog + @ApiOperation("修改手机号") @PatchMapping(value = "/mobile") public void updateMobile(Principal principal, @RequestBody UserAccountParam param) throws BindException { UserAccount entity = getUserAccount(principal); @@ -116,9 +116,8 @@ public class CurrentController { accountRepo.save(entity); } - /** - * 修改邮箱 - */ + @Syslog + @ApiOperation("修改邮箱") @PatchMapping(value = "/email") public void updateEmail(Principal principal, @RequestBody UserAccountParam param) throws BindException { UserAccount entity = getUserAccount(principal); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index d010f00..bac8fda 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -4,6 +4,7 @@ import java.util.Collections; import javax.annotation.security.PermitAll; import lombok.AllArgsConstructor; +import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationManager; @@ -21,11 +22,13 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.syslog.Syslog; /** * @author jrxian * @date 2020/11/28 11:07 */ +@Api("密码管理") @RestController @RequestMapping("/password") @AllArgsConstructor @@ -36,7 +39,8 @@ public class PasswordController { private final UserDetailsPasswordService passwordService; private final UserAccountRepo accountRepo; - @ApiOperation("用户不登录直接修改自己的密码") + @Syslog + @ApiOperation("密码过期修改") @PermitAll @PostMapping public void updatePassword(String username, String oldPassword, String newPassword) { @@ -55,14 +59,16 @@ public class PasswordController { SecurityContextHolder.clearContext(); } - @ApiOperation("用户登录后修改自己的密码") + @Syslog + @ApiOperation("自己修改密码") @PostMapping("/self") @PreAuthorize("isAuthenticated()") public void changePassword(String oldPassword, String newPassword) { userDetailsManager.changePassword(oldPassword, newPassword); } - @ApiOperation("管理员修改其它用户密码") + @Syslog + @ApiOperation("管理员修改密码") @PostMapping("/admin") @PreAuthorize("hasAuthority('sysadmin')") public void updatePassword(String username, String password) { @@ -70,6 +76,7 @@ public class PasswordController { passwordService.updatePassword(userDetails, passwordEncoder.encode(password)); } + @Syslog @ApiOperation("解锁密码") @PostMapping("/unlock") @PreAuthorize("hasAuthority('sysadmin')") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index e89f75c..bbf0ca8 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -6,6 +6,8 @@ import java.util.HashMap; import java.util.Map; import lombok.AllArgsConstructor; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.web.bind.annotation.GetMapping; @@ -21,6 +23,7 @@ import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.wechat.WechatUserProfile; import cn.seqdata.oauth2.wxapp.WxAppService; +import cn.seqdata.syslog.Syslog; import cn.seqdata.wxapp.message.Code2SessionResponse; import cn.seqdata.wxapp.pojo.PhoneInfo; import cn.seqdata.wxapp.pojo.UserInfo; @@ -30,6 +33,7 @@ import cn.seqdata.wxapp.util.WxAppUtils; * @author jrxian * @date 2020-06-22 09:17 */ +@Api("微信小程序") @RestController @RequestMapping("/wxapp/{id}") @AllArgsConstructor @@ -42,6 +46,8 @@ public class WxAppController { /** * 获取用户信息并且创建rbac_user_info */ + @Syslog + @ApiOperation("小程序注册") @GetMapping("/signup") public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData, Principal principal) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); @@ -61,6 +67,8 @@ public class WxAppController { /** * 获取code换取openid登录 */ + @Syslog + @ApiOperation("小程序登录") @GetMapping("/signin") public OAuth2AccessToken signin(@PathVariable("id") String id, String code, Principal principal) { Code2SessionResponse response = wxappService.signin(id, code); @@ -77,6 +85,7 @@ public class WxAppController { /** * 获取微信手机号,绑定到现有账号 */ + @ApiOperation("小程序手机号登录") @GetMapping("/phone") public OAuth2AccessToken phone(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); -- Gitee From c865d49edbe9c226885c1408d5cf7fe66ee007c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 14 Dec 2020 01:26:19 +0800 Subject: [PATCH 134/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=94=81=E5=AE=9A=E8=B4=A6=E5=8F=B7=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AccountLockConfiguration.java | 58 +++++++++++++++++++ .../AuthorizationServerConfiguration.java | 11 +++- .../seqdata/oauth2/BizlogConfiguration.java | 6 +- .../oauth2/controller/PasswordController.java | 10 +--- .../oauth2/service/AccountLockService.java | 32 ++++++++++ 5 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java new file mode 100644 index 0000000..34b3b61 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java @@ -0,0 +1,58 @@ +package cn.seqdata.oauth2; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import org.joda.time.DateTime; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent; +import org.springframework.security.authentication.event.AuthenticationSuccessEvent; +import org.springframework.security.core.Authentication; + +import cn.seqdata.oauth2.service.AccountLockService; + +/** + * @author jrxian + * @date 2020/12/13 23:44 + * 密码连续错误超过3次,锁定5分钟 + */ +@Configuration +@lombok.AllArgsConstructor +public class AccountLockConfiguration { + private final StringRedisTemplate redisTemplate; + private final AccountLockService lockService; + + @Bean + public ApplicationListener lockAuthcSuccessListener() { + return event -> { + Authentication authentication = event.getAuthentication(); + String username = authentication.getName(); + redisTemplate.delete(lockName(username)); + lockService.unlock(username); + }; + } + + @Bean + public ApplicationListener lockAuthcBadCredentialsListener() { + return event -> { + Authentication authentication = event.getAuthentication(); + String username = authentication.getName(); + String lockName = lockName(username); + ValueOperations opsForValue = redisTemplate.opsForValue(); + Long hits = opsForValue.increment(lockName); + if(Objects.nonNull(hits) && hits > 3) { + redisTemplate.expire(lockName, 5, TimeUnit.MINUTES); + DateTime occur = DateTime.now(); + lockService.lock(username, occur.plusMinutes(5)); + } + }; + } + + public static String lockName(String username) { + return "lock:" + username; + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index f17423d..12f795e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -8,6 +8,8 @@ import lombok.AllArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; @@ -54,7 +56,14 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu public void configure(AuthorizationServerSecurityConfigurer security) { security .tokenKeyAccess("isAuthenticated()") - .checkTokenAccess("isAuthenticated()"); + .checkTokenAccess("isAuthenticated()") + .addObjectPostProcessor(new ObjectPostProcessor() { + @Override + public O postProcess(O provider) { + provider.setForcePrincipalAsString(true); + return provider; + } + }); } @Override diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index cc2ebdc..4458c31 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -33,7 +33,7 @@ public class BizlogConfiguration { // } @Bean - public ApplicationListener authenticationSuccessListener(SyslogConsumer consumer) { + public ApplicationListener syslogAuthcSuccessListener(SyslogConsumer consumer) { return event -> { SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) .eventId("login") @@ -43,7 +43,7 @@ public class BizlogConfiguration { } @Bean - public ApplicationListener authenticationFailureListener(SyslogConsumer consumer) { + public ApplicationListener syslogAuthcFailureListener(SyslogConsumer consumer) { return event -> { AuthenticationException exception = event.getException(); Class exceptionClass = exception.getClass(); @@ -56,7 +56,7 @@ public class BizlogConfiguration { } @Bean - public ApplicationListener logoutSuccessListener(SyslogConsumer consumer) { + public ApplicationListener syslogLogoutSuccessListener(SyslogConsumer consumer) { return event -> { SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) .eventId("logout") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index bac8fda..8c3e9d2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.syslog.Syslog; /** @@ -37,7 +37,7 @@ public class PasswordController { private final AuthenticationManager authenticationManager; private final UserDetailsManager userDetailsManager; private final UserDetailsPasswordService passwordService; - private final UserAccountRepo accountRepo; + private final AccountLockService lockService; @Syslog @ApiOperation("密码过期修改") @@ -81,10 +81,6 @@ public class PasswordController { @PostMapping("/unlock") @PreAuthorize("hasAuthority('sysadmin')") public void unlockPassword(String username) { - accountRepo.findByUsername(username) - .ifPresent(account -> { - account.setLockExpireDate(null); - accountRepo.save(account); - }); + lockService.unlock(username); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java new file mode 100644 index 0000000..3848450 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java @@ -0,0 +1,32 @@ +package cn.seqdata.oauth2.service; + +import org.joda.time.DateTime; +import org.springframework.stereotype.Service; + +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; + +/** + * @author jrxian + * @date 2020/12/14 1:00 + */ +@Service +@lombok.AllArgsConstructor +public class AccountLockService { + private final UserAccountRepo accountRepo; + + public void lock(String username, DateTime lockExpireDate) { + accountRepo.findByUsername(username) + .ifPresent(account -> { + account.setLockExpireDate(lockExpireDate); + accountRepo.save(account); + }); + } + + public void unlock(String username) { + accountRepo.findByUsername(username) + .ifPresent(account -> { + account.setLockExpireDate(null); + accountRepo.save(account); + }); + } +} -- Gitee From 62a5b00ab3f10a37101514dd7663ff0d300071db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 14 Dec 2020 02:40:42 +0800 Subject: [PATCH 135/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=94=81=E5=AE=9A=E8=B4=A6=E5=8F=B7=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 6 +- .../oauth2/nonce/NonceHandlerImpl.java | 3 +- .../oauth2/nonce/NonceTokenGranter.java | 44 ++++---------- .../oauth2/nonce/PasswordTokenGranter.java | 60 +++++++++++++++++++ .../filter/captcha/CaptchaException.java | 10 +--- .../filter/captcha/CaptchaGlobalFilter.java | 4 +- 6 files changed, 83 insertions(+), 44 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index 12f795e..518611d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -23,7 +23,6 @@ import org.springframework.security.oauth2.provider.client.ClientCredentialsToke import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter; import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter; -import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter; import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; @@ -32,6 +31,7 @@ import org.springframework.security.oauth2.provider.token.TokenStore; import cn.seqdata.oauth2.nonce.NonceAuthenticationProvider; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.nonce.NonceTokenGranter; +import cn.seqdata.oauth2.nonce.PasswordTokenGranter; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; @@ -106,9 +106,9 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu NonceAuthenticationProvider emailNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByEmail, userDetailsManager, nonceHandlers); tokenGranters.add(new NonceTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.EMAIL)); - //用户名密码验证 if(Objects.nonNull(authenticationManager)) { - tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + //用户名密码验证,用authz.PasswordTokenGranter代替,需要把账号状态输出到前端 + tokenGranters.add(new PasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); } return new CompositeTokenGranter(tokenGranters); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index bfa7f08..e957d8f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -6,6 +6,7 @@ import lombok.AllArgsConstructor; import org.apache.commons.lang3.RandomUtils; import org.joda.time.DateTime; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -53,7 +54,7 @@ public abstract class NonceHandlerImpl implements NonceHandler { DateTime expire = account.getNonceExpire(); Integer code = account.getNonceCode(); if(Objects.isNull(expire) || Objects.isNull(code)) { - throw new BadCredentialsException("未获取验证码"); + throw new AuthenticationCredentialsNotFoundException("未获取验证码"); } if(expire.isBeforeNow()) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java index 44fa268..0939855 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java @@ -1,51 +1,33 @@ package cn.seqdata.oauth2.nonce; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.authentication.AccountStatusException; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.provider.*; -import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.OAuth2RequestFactory; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * Author: jrxian * Date: 2020-01-26 00:16 */ -public class NonceTokenGranter extends AbstractTokenGranter { - private final AuthenticationProvider authenticationProvider; +public class NonceTokenGranter extends PasswordTokenGranter { public NonceTokenGranter(AuthenticationProvider authenticationProvider, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) { - super(tokenServices, clientDetailsService, requestFactory, grantType); - this.authenticationProvider = authenticationProvider; + super(new NonceTokenGranter.NonceAuthenticationManager(authenticationProvider), + tokenServices, clientDetailsService, requestFactory, grantType); } - @Override - protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { - Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); - String username = parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); - String password = parameters.remove(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); + @lombok.RequiredArgsConstructor + private static class NonceAuthenticationManager implements AuthenticationManager { + private final AuthenticationProvider authenticationProvider; - Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password); - ((AbstractAuthenticationToken) userAuth).setDetails(parameters); - try { - userAuth = authenticationProvider.authenticate(userAuth); - } catch(AccountStatusException ex) { - throw new InvalidGrantException(ex.getMessage()); - } - if(userAuth == null || !userAuth.isAuthenticated()) { - throw new InvalidGrantException("Could not authenticate username: " + username); + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + return authenticationProvider.authenticate(authentication); } - - OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); - return new OAuth2Authentication(storedOAuth2Request, userAuth); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java new file mode 100644 index 0000000..bf6c881 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java @@ -0,0 +1,60 @@ +package cn.seqdata.oauth2.nonce; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.security.authentication.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +/** + * @author jrxian + * @date 2020/12/14 1:56 + */ +public class PasswordTokenGranter extends AbstractTokenGranter { + private static final String GRANT_TYPE = "password"; + + private final AuthenticationManager authenticationManager; + + public PasswordTokenGranter(AuthenticationManager authenticationManager, + AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { + this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + } + + protected PasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, + ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) { + super(tokenServices, clientDetailsService, requestFactory, grantType); + this.authenticationManager = authenticationManager; + } + + @Override + protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { + Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); + String username = parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); + String password = parameters.remove(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); + + Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password); + ((AbstractAuthenticationToken) userAuth).setDetails(parameters); + try { + userAuth = authenticationManager.authenticate(userAuth); + } catch(AccountStatusException | BadCredentialsException ex) { + InvalidGrantException exception = new InvalidGrantException(ex.getMessage(), ex); + //给前端输出具体的错误类型,便于前端处理 + //主要解决首次登录/管理员重置密码后必须修改密码才能登录的问题 + Class exClass = ex.getClass(); + exception.addAdditionalInformation("exception", exClass.getSimpleName()); + throw exception; + } + if(userAuth == null || !userAuth.isAuthenticated()) { + throw new InvalidGrantException("Could not authenticate user: " + username); + } + + OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); + return new OAuth2Authentication(storedOAuth2Request, userAuth); + } +} \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java index 4992942..e758953 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaException.java @@ -8,16 +8,12 @@ import org.springframework.web.server.ResponseStatusException; * @date 2020-11-26 14:45 */ public class CaptchaException extends ResponseStatusException { - + public CaptchaException() { super(HttpStatus.PRECONDITION_REQUIRED); } - public CaptchaException(HttpStatus status) { - super(status); - } - - public CaptchaException(HttpStatus status, String reason) { - super(status, reason); + public CaptchaException(String reason) { + super(HttpStatus.PRECONDITION_REQUIRED, reason); } } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java index aa42fbd..4ceda1c 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java @@ -100,13 +100,13 @@ public class CaptchaGlobalFilter implements GlobalFilter, Ordered { String challenge = headers.getFirst(xCaptchaChallenge); String response = headers.getFirst(xCaptchaResponse); if(Objects.isNull(challenge) || Objects.isNull(response)) { - throw new CaptchaException(); + throw new CaptchaException("请输入验证码"); } //回答必须和标准答案一致 String answer = opsForValue.get(challenge); if(!StringUtils.equalsIgnoreCase(answer, response)) { - throw new CaptchaException(); + throw new CaptchaException("验证码不正确"); } } -- Gitee From e7627d97ae77250597b0bf76fd48c62f5c539abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 14 Dec 2020 12:27:09 +0800 Subject: [PATCH 136/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3UserService=E7=9A=84?= =?UTF-8?q?=E4=BA=8B=E5=8A=A1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/WxAppController.java | 4 ++++ .../main/java/cn/seqdata/oauth2/service/UserService.java | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index bbf0ca8..9de2332 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -10,6 +10,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -49,6 +50,7 @@ public class WxAppController { @Syslog @ApiOperation("小程序注册") @GetMapping("/signup") + @Transactional public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData, Principal principal) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); @@ -70,6 +72,7 @@ public class WxAppController { @Syslog @ApiOperation("小程序登录") @GetMapping("/signin") + @Transactional public OAuth2AccessToken signin(@PathVariable("id") String id, String code, Principal principal) { Code2SessionResponse response = wxappService.signin(id, code); @@ -87,6 +90,7 @@ public class WxAppController { */ @ApiOperation("小程序手机号登录") @GetMapping("/phone") + @Transactional public OAuth2AccessToken phone(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 702cf9f..1a45213 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -6,6 +6,8 @@ import java.util.Optional; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; @@ -34,6 +36,7 @@ public class UserService { * 用户名密码登录:grantType='password' * 第三方登录:grantType=clientId */ + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public Optional loadUser(String grantType, String username) { if(OAuth2Constants.PASSWORD.equalsIgnoreCase(grantType)) { return accountRepo.findByUsername(username); @@ -51,7 +54,7 @@ public class UserService { /** * 将第三方认证获取的用户信息转换为本系统的 User */ - //@Transactional //不要加事务,不然User和UserAccount不能保存 + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public void saveAsUser(String clientId, UserProfile profile) { UserDetailId entityId = new UserDetailId(clientId, profile.getName()); UserDetail userDetail = new UserDetail(entityId); @@ -100,7 +103,7 @@ public class UserService { /** * 将第三方认证的用户和本系统的用户绑定 */ - //@Transactional //不要加事务 + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public void bindUser(long userId, String clientId, UserProfile profile) { //如果是微信绑定,更新account的union_id if(profile instanceof WechatUserProfile) { -- Gitee From 51d15bcc3e598a5fd492293f206f5fc00c08ad2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 14 Dec 2020 13:07:00 +0800 Subject: [PATCH 137/228] =?UTF-8?q?Syslog=E8=AE=B0=E5=BD=95=E5=88=B0rabbit?= =?UTF-8?q?mq?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 5 +++ .../seqdata/oauth2/BizlogConfiguration.java | 31 +++++++++++-------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 9e3a7bc..73e5291 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -41,6 +41,11 @@ spring-security-oauth2-autoconfigure + + org.springframework.amqp + spring-rabbit + + com.aliyun aliyun-java-sdk-core diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index 4458c31..5dd76d1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -1,6 +1,8 @@ package cn.seqdata.oauth2; +import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -10,10 +12,7 @@ import org.springframework.security.authentication.event.LogoutSuccessEvent; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import cn.seqdata.syslog.DefaultSyslogResolver; -import cn.seqdata.syslog.EnableSyslog; -import cn.seqdata.syslog.SyslogConsumer; -import cn.seqdata.syslog.SyslogRecord; +import cn.seqdata.syslog.*; /** * @author jrxian @@ -25,15 +24,21 @@ public class BizlogConfiguration { @Value("${spring.application.name}") private String appName; -// @Bean -// public SyslogConsumer jdbcSyslogConsumer(JdbcTemplate jdbcTemplate) { -// JdbcSyslogConsumer syslogConsumer = new JdbcSyslogConsumer(); -// syslogConsumer.setJdbcTemplate(jdbcTemplate); -// return syslogConsumer; -// } + // @Bean + // public SyslogConsumer jdbcSyslogConsumer(JdbcTemplate jdbcTemplate) { + // JdbcSyslogConsumer syslogConsumer = new JdbcSyslogConsumer(); + // syslogConsumer.setJdbcTemplate(jdbcTemplate); + // return syslogConsumer; + // } @Bean - public ApplicationListener syslogAuthcSuccessListener(SyslogConsumer consumer) { + @ConditionalOnProperty("spring.rabbitmq.host") + public RabbitSyslogConsumer rabbitSyslogConsumer(RabbitTemplate rabbitTemplate) { + return new RabbitSyslogConsumer(rabbitTemplate); + } + + @Bean + public ApplicationListener syslogAuthcSuccessListener(CompositeSyslogConsumer consumer) { return event -> { SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) .eventId("login") @@ -43,7 +48,7 @@ public class BizlogConfiguration { } @Bean - public ApplicationListener syslogAuthcFailureListener(SyslogConsumer consumer) { + public ApplicationListener syslogAuthcFailureListener(CompositeSyslogConsumer consumer) { return event -> { AuthenticationException exception = event.getException(); Class exceptionClass = exception.getClass(); @@ -56,7 +61,7 @@ public class BizlogConfiguration { } @Bean - public ApplicationListener syslogLogoutSuccessListener(SyslogConsumer consumer) { + public ApplicationListener syslogLogoutSuccessListener(CompositeSyslogConsumer consumer) { return event -> { SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) .eventId("logout") -- Gitee From a7c7bbb3c87a145c65c6aea49cfdfb2146bc7737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 14 Dec 2020 18:13:37 +0800 Subject: [PATCH 138/228] =?UTF-8?q?=E7=94=A8=E5=AF=86=E7=A0=81=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=EF=BC=8C=E5=AF=86=E7=A0=81=E8=BE=93=E5=87=BA?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E7=BB=99=E5=87=BA=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/controller/ConnectController.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index a38fe84..6d561c4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -17,6 +17,7 @@ import io.swagger.annotations.ApiOperation; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; @@ -94,11 +95,12 @@ public class ConnectController { public void bindPassword(String username, String password, Principal principal) { userService.loadUser(OAuth2Constants.PASSWORD, username) .ifPresent(user -> { - if(passwordEncoder.matches(password, user.getPassword())) { - userService.bindUser(Objects.requireNonNull(user.getId()), - SecurityUtils.grantType(principal), - SecurityUtils.username(principal)); + if(!passwordEncoder.matches(password, user.getPassword())) { + throw new BadCredentialsException("用户名或密码不正确"); } + userService.bindUser(Objects.requireNonNull(user.getId()), + SecurityUtils.grantType(principal), + SecurityUtils.username(principal)); }); } -- Gitee From 9887a6e968795ebb37b8e57035333904423c2f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 15 Dec 2020 01:41:18 +0800 Subject: [PATCH 139/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E5=9C=B0=E5=9D=80=E5=92=8C=E7=99=BB=E5=BD=95=E6=97=B6?= =?UTF-8?q?=E6=AE=B5=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthcServerConfiguration.java | 14 +++++ .../seqdata/oauth2/filter/PeriodFilter.java | 24 +++++++ .../oauth2/filter/PeriodProperties.java | 56 +++++++++++++++++ .../oauth2/filter/RemoteAddrFilter.java | 22 +++++++ .../oauth2/filter/RemoteAddrProperties.java | 55 ++++++++++++++++ .../seqdata/oauth2/filter/RequestFilter.java | 62 +++++++++++++++++++ 6 files changed, 233 insertions(+) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodFilter.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodProperties.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrFilter.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrProperties.java create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RequestFilter.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index ea693f2..04baec4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -1,10 +1,17 @@ package cn.seqdata.oauth2; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; +import org.springframework.security.web.access.channel.ChannelProcessingFilter; + +import cn.seqdata.oauth2.filter.PeriodFilter; +import cn.seqdata.oauth2.filter.PeriodProperties; +import cn.seqdata.oauth2.filter.RemoteAddrFilter; +import cn.seqdata.oauth2.filter.RemoteAddrProperties; /** * Author: jrxian @@ -13,12 +20,19 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R @Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableConfigurationProperties({RemoteAddrProperties.class, PeriodProperties.class}) +@lombok.RequiredArgsConstructor public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { + private final RemoteAddrProperties remoteAddrProperties; + private final PeriodProperties periodProperties; @Override public void configure(HttpSecurity http) throws Exception { http.cors(); + http.addFilterAfter(new RemoteAddrFilter(remoteAddrProperties), ChannelProcessingFilter.class); + http.addFilterAfter(new PeriodFilter(periodProperties), ChannelProcessingFilter.class); + http.authorizeRequests() .antMatchers("/oauth2/**", "/rbac/**", "/perm/**" , "/connect/**", "/oauth/code/**", "/password/**", "/wxapp/**") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodFilter.java new file mode 100644 index 0000000..6c1bef2 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodFilter.java @@ -0,0 +1,24 @@ +package cn.seqdata.oauth2.filter; + +import javax.servlet.http.HttpServletRequest; + +import org.joda.time.LocalTime; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +/** + * @author jrxian + * @date 2020/12/15 0:56 + * 只能在固定的时间段登录 + */ +@lombok.RequiredArgsConstructor +public class PeriodFilter extends RequestFilter { + private final PeriodProperties properties; + + @Override + protected void internalFilter(HttpServletRequest request) throws ResponseStatusException { + if(!properties.test(LocalTime.now())) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, "不允许在此时段登录"); + } + } +} \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodProperties.java new file mode 100644 index 0000000..7acfd11 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/PeriodProperties.java @@ -0,0 +1,56 @@ +package cn.seqdata.oauth2.filter; + +import java.util.LinkedList; +import java.util.Objects; +import java.util.function.Predicate; + +import org.apache.commons.lang3.ObjectUtils; +import org.joda.time.DateTimeConstants; +import org.joda.time.LocalTime; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author jrxian + * @date 2020/12/15 0:49 + */ +@ConfigurationProperties("spring.security.filter.period") +public class PeriodProperties extends LinkedList implements Predicate { + + @Override + public boolean test(LocalTime time) { + if(isEmpty()) { + return true; + } + + for(PeriodProperties.Item i : this) { + LocalTime start = ObjectUtils.defaultIfNull(i.start, LocalTime.MIDNIGHT); + LocalTime end = ObjectUtils.defaultIfNull(i.end, LocalTime.fromMillisOfDay(DateTimeConstants.MILLIS_PER_DAY - 1)); + if(time.isAfter(start) && time.isBefore(end)) { + return true; + } + } + + return false; + } + + public static class Item { + private LocalTime start; + private LocalTime end; + + public String getStart() { + return Objects.nonNull(start) ? start.toString() : null; + } + + public void setStart(String start) { + this.start = Objects.nonNull(start) ? LocalTime.parse(start) : null; + } + + public String getEnd() { + return Objects.nonNull(end) ? end.toString() : null; + } + + public void setEnd(String end) { + this.end = Objects.nonNull(end) ? LocalTime.parse(end) : null; + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrFilter.java new file mode 100644 index 0000000..f8d0201 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrFilter.java @@ -0,0 +1,22 @@ +package cn.seqdata.oauth2.filter; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +/** + * @author jrxian + * @date 2020/12/14 22:43 + */ +@lombok.RequiredArgsConstructor +public class RemoteAddrFilter extends RequestFilter { + private final RemoteAddrProperties properties; + + @Override + protected void internalFilter(HttpServletRequest request) throws ResponseStatusException { + if(!properties.test(request.getRemoteAddr())) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN, "不允许在此客户端地址登录"); + } + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrProperties.java new file mode 100644 index 0000000..aa035d4 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrProperties.java @@ -0,0 +1,55 @@ +package cn.seqdata.oauth2.filter; + +import java.util.Objects; +import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.StringUtils; + +/** + * @author jrxian + * @date 2020/12/15 0:34 + * 只能在固定的地址范围登录 + */ +@lombok.Getter +@lombok.Setter +@ConfigurationProperties("spring.security.filter.address") +public class RemoteAddrProperties implements Predicate { + private Pattern allow; + private Pattern deny; + + public String getAllow() { + return Objects.nonNull(allow) ? allow.toString() : null; + } + + public void setAllow(String allow) { + this.allow = StringUtils.isEmpty(allow) ? null : Pattern.compile(allow); + } + + public String getDeny() { + return Objects.nonNull(deny) ? deny.toString() : null; + } + + public void setDeny(String deny) { + this.deny = StringUtils.isEmpty(deny) ? null : Pattern.compile(deny); + } + + @Override + public boolean test(String property) { + if(Objects.nonNull(deny) && matches(deny.matcher(property))) { + return false; + } + + if(Objects.nonNull(allow) && matches(allow.matcher(property))) { + return true; + } + + return Objects.isNull(deny) && Objects.isNull(allow); + } + + private static boolean matches(Matcher matcher) { + return matcher.matches(); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RequestFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RequestFilter.java new file mode 100644 index 0000000..59aa511 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RequestFilter.java @@ -0,0 +1,62 @@ +package cn.seqdata.oauth2.filter; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.server.ResponseStatusException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author jrxian + * @date 2020/12/15 0:17 + */ +public abstract class RequestFilter implements Filter { + private final ObjectMapper objectMapper = new ObjectMapper(); + + protected abstract void internalFilter(HttpServletRequest request) throws ResponseStatusException; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + try { + if(request instanceof HttpServletRequest) { + HttpServletRequest httpRequest = (HttpServletRequest) request; + String requestURI = httpRequest.getRequestURI(); + if(requestURI.startsWith("/oauth/token") || requestURI.startsWith("/connect")) { + internalFilter(httpRequest); + } + } + chain.doFilter(request, response); + } catch(ResponseStatusException ex) { + if(response instanceof HttpServletResponse) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + + HttpStatus httpStatus = ex.getStatus(); + httpResponse.setStatus(httpStatus.value()); + + Map headers = ex.getHeaders(); + headers.forEach(httpResponse::setHeader); + + httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); + objectMapper.writeValue(httpResponse.getOutputStream(), createErrorAttributes(ex)); + } + } + } + + private Map createErrorAttributes(ResponseStatusException rse) { + Map errorAttributes = new HashMap<>(); + + HttpStatus httpStatus = rse.getStatus(); + errorAttributes.put("timestamp", System.currentTimeMillis()); + errorAttributes.put("status", httpStatus.value()); + errorAttributes.put("error", httpStatus.getReasonPhrase()); + errorAttributes.put("message", rse.getReason()); + + return errorAttributes; + } +} -- Gitee From a81e7996def9a9fca74dcb4c663fcd4502990475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 15 Dec 2020 17:32:47 +0800 Subject: [PATCH 140/228] =?UTF-8?q?=E5=8A=A0=E5=BC=BA=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9C=89=E6=95=88=E6=80=A7=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 40 ++++++++----------- .../oauth2/controller/NonceController.java | 38 ++++++++++++++++++ .../oauth2/controller/PasswordController.java | 10 +++-- 3 files changed, 60 insertions(+), 28 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 6d561c4..14f0dad 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -1,28 +1,28 @@ package cn.seqdata.oauth2.controller; import java.net.URI; -import java.security.GeneralSecurityException; import java.security.Principal; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import javax.security.auth.login.AccountNotFoundException; import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; -import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; -import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.web.bind.annotation.*; +import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; @@ -43,7 +43,8 @@ import cn.seqdata.syslog.Syslog; @Api("第三方账号绑定") @RestController @RequestMapping("/connect") -@AllArgsConstructor +@PreAuthorize("isAuthenticated()") +@lombok.RequiredArgsConstructor public class ConnectController { private final ClientRegistrationRepository repository; private final UserAccountRepo accountRepo; @@ -74,21 +75,6 @@ public class ConnectController { return connectors; } - /** - * 向手机或邮箱发送一次性验证码 - */ - @Syslog - @ApiOperation("发送验证码") - @GetMapping(value = "/nonce") - public ResponseEntity nonce(String username, HttpServletRequest request) { - for(NonceHandler nonceHandler : nonceHandlers) { - if(nonceHandler.support(username)) { - nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); - } - } - return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); - } - @Syslog @ApiOperation("通过密码绑定") @GetMapping(value = "/password") @@ -121,13 +107,13 @@ public class ConnectController { @Syslog @ApiOperation("第三方账号绑定") @GetMapping(value = "/{registrationId}") - public ResponseEntity bind(@PathVariable String registrationId, HttpServletRequest request) { + public ResponseEntity bind(@PathVariable String registrationId, HttpServletRequest request, Principal principal) { ClientRegistration registration = repository.findByRegistrationId(registrationId); OAuth2Provider provider = OAuth2Provider.provider(registration); OAuth2Template oAuth2Template = provider.createOAuth2Template(registration); //从 request 中分离出 accessToken - String accessToken = SecurityUtils.accessToken(request.getUserPrincipal()); + String accessToken = SecurityUtils.accessToken(principal); //重定向 URI,返回信息由 OAuth2CodeController 负责接收 String redirectUri = registration.getRedirectUriTemplate(); @@ -148,7 +134,7 @@ public class ConnectController { @ApiOperation("第三方账号解绑") @Transactional @DeleteMapping(value = "/{registrationId}") - public void unbind(@PathVariable String registrationId, Principal principal) throws GeneralSecurityException { + public void unbind(@PathVariable String registrationId, Principal principal) { String username = SecurityUtils.username(principal); UserDetailId entityId = new UserDetailId(registrationId, username); if(userDetailRepo.existsById(entityId)) { @@ -156,11 +142,17 @@ public class ConnectController { } else { long userId = userService.loadUser(registrationId, username) .map(UserAccount::getId) - .orElseThrow(AccountNotFoundException::new); + .orElseThrow(() -> new UsernameNotFoundException("要解绑的账号不存在")); userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); } } + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(AuthenticationException.class) + public ErrorAttributes authenticationException(AuthenticationException ex) { + return new ErrorAttributes(HttpStatus.BAD_REQUEST, ex.getMessage(), ex); + } + private void bindNonce(String grantType, String username, int nonce, Principal principal) { for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java new file mode 100644 index 0000000..670172e --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java @@ -0,0 +1,38 @@ +package cn.seqdata.oauth2.controller; + +import java.util.List; +import javax.servlet.http.HttpServletRequest; + +import io.swagger.annotations.ApiOperation; +import org.joda.time.DateTimeConstants; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import cn.seqdata.oauth2.nonce.NonceHandler; +import cn.seqdata.syslog.Syslog; + +/** + * @author jrxian + * @date 2020/12/15 17:20 + */ +@RestController +@lombok.RequiredArgsConstructor +public class NonceController { + private final List nonceHandlers; + + /** + * 向手机或邮箱发送一次性验证码 + */ + @Syslog + @ApiOperation("发送验证码") + @GetMapping(value = "/nonce") + public ResponseEntity nonce(String username, HttpServletRequest request) { + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) { + nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); + } + } + return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); + } +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 8c3e9d2..d99834b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -2,6 +2,8 @@ package cn.seqdata.oauth2.controller; import java.util.Collections; import javax.annotation.security.PermitAll; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import io.swagger.annotations.Api; @@ -43,7 +45,7 @@ public class PasswordController { @ApiOperation("密码过期修改") @PermitAll @PostMapping - public void updatePassword(String username, String oldPassword, String newPassword) { + public void updatePassword(@NotBlank String username, String oldPassword, @NotEmpty String newPassword) { UserDetails userDetails = userDetailsManager.loadUserByUsername(username); Authentication authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword); try { @@ -63,7 +65,7 @@ public class PasswordController { @ApiOperation("自己修改密码") @PostMapping("/self") @PreAuthorize("isAuthenticated()") - public void changePassword(String oldPassword, String newPassword) { + public void changePassword(String oldPassword, @NotEmpty String newPassword) { userDetailsManager.changePassword(oldPassword, newPassword); } @@ -71,7 +73,7 @@ public class PasswordController { @ApiOperation("管理员修改密码") @PostMapping("/admin") @PreAuthorize("hasAuthority('sysadmin')") - public void updatePassword(String username, String password) { + public void updatePassword(@NotBlank String username, @NotEmpty String password) { UserDetails userDetails = userDetailsManager.loadUserByUsername(username); passwordService.updatePassword(userDetails, passwordEncoder.encode(password)); } @@ -80,7 +82,7 @@ public class PasswordController { @ApiOperation("解锁密码") @PostMapping("/unlock") @PreAuthorize("hasAuthority('sysadmin')") - public void unlockPassword(String username) { + public void unlockPassword(@NotBlank String username) { lockService.unlock(username); } } -- Gitee From 27c27b667aed09fec1d5c957fbc23f0e08c56577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 15 Dec 2020 17:43:46 +0800 Subject: [PATCH 141/228] =?UTF-8?q?=E5=8A=A0=E5=BC=BA=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9C=89=E6=95=88=E6=80=A7=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/controller/CurrentController.java | 2 ++ .../cn/seqdata/oauth2/controller/NonceController.java | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 6fa5ac7..2594f4b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -11,6 +11,7 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; @@ -37,6 +38,7 @@ import cn.seqdata.syslog.Syslog; @Api("当前登录用户") @RestController @RequestMapping("/current") +@PreAuthorize("isAuthenticated()") @AllArgsConstructor public class CurrentController { private static final String objectName = "UserAccount"; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java index 670172e..761fa07 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java @@ -1,12 +1,12 @@ package cn.seqdata.oauth2.controller; import java.util.List; -import javax.servlet.http.HttpServletRequest; import io.swagger.annotations.ApiOperation; import org.joda.time.DateTimeConstants; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import cn.seqdata.oauth2.nonce.NonceHandler; @@ -26,8 +26,8 @@ public class NonceController { */ @Syslog @ApiOperation("发送验证码") - @GetMapping(value = "/nonce") - public ResponseEntity nonce(String username, HttpServletRequest request) { + @RequestMapping(value = "/nonce", method = {RequestMethod.GET, RequestMethod.POST}) + public ResponseEntity nonce(String username) { for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); -- Gitee From 9c8335b89973fe6c28df1b18752dd015ed2af6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 15 Dec 2020 17:52:12 +0800 Subject: [PATCH 142/228] =?UTF-8?q?=E5=8A=A0=E5=BC=BA=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9C=89=E6=95=88=E6=80=A7=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/AuthcServerConfiguration.java | 2 +- .../java/cn/seqdata/oauth2/controller/NonceController.java | 2 ++ .../java/cn/seqdata/oauth2/controller/WxAppController.java | 5 +++-- .../src/main/java/cn/seqdata/oauth2/service/UserService.java | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 04baec4..e8e912c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -35,7 +35,7 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.authorizeRequests() .antMatchers("/oauth2/**", "/rbac/**", "/perm/**" - , "/connect/**", "/oauth/code/**", "/password/**", "/wxapp/**") + , "/oauth/code/**", "/password/**", "/wxapp/**", "/nonce/**") .permitAll(); super.configure(http); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java index 761fa07..c6dcc9f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java @@ -1,6 +1,7 @@ package cn.seqdata.oauth2.controller; import java.util.List; +import javax.annotation.security.PermitAll; import io.swagger.annotations.ApiOperation; import org.joda.time.DateTimeConstants; @@ -16,6 +17,7 @@ import cn.seqdata.syslog.Syslog; * @author jrxian * @date 2020/12/15 17:20 */ +@PermitAll @RestController @lombok.RequiredArgsConstructor public class NonceController { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 9de2332..93306a0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -8,6 +8,7 @@ import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.springframework.lang.Nullable; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.transaction.annotation.Transactional; @@ -51,7 +52,7 @@ public class WxAppController { @ApiOperation("小程序注册") @GetMapping("/signup") @Transactional - public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData, Principal principal) throws IOException { + public OAuth2AccessToken signup(@PathVariable("id") String id, String code, String iv, String encryptedData, @Nullable Principal principal) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); UserInfo userInfo = objectMapper.readValue(decryptedData, UserInfo.class); @@ -73,7 +74,7 @@ public class WxAppController { @ApiOperation("小程序登录") @GetMapping("/signin") @Transactional - public OAuth2AccessToken signin(@PathVariable("id") String id, String code, Principal principal) { + public OAuth2AccessToken signin(@PathVariable("id") String id, String code, @Nullable Principal principal) { Code2SessionResponse response = wxappService.signin(id, code); Map attributes = new HashMap<>(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 1a45213..a92d1d0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -5,6 +5,7 @@ import java.util.Objects; import java.util.Optional; import lombok.AllArgsConstructor; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -85,7 +86,7 @@ public class UserService { /** * 如果用户存在就绑定,否则就创建 */ - public void updateUser(String registrationId, UserProfile profile, Principal principal) { + public void updateUser(String registrationId, UserProfile profile, @Nullable Principal principal) { if(Objects.nonNull(principal)) { //获取当前用户身份 String localGrantType = SecurityUtils.grantType(principal); -- Gitee From 372d5c4871fa8644e565ff0fecfeeec1cb183b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 15 Dec 2020 18:03:06 +0800 Subject: [PATCH 143/228] =?UTF-8?q?=E5=8A=A0=E5=BC=BA=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9C=89=E6=95=88=E6=80=A7=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthcServerConfiguration.java | 2 +- .../oauth2/controller/ConnectController.java | 20 +++++++++- .../oauth2/controller/NonceController.java | 40 ------------------- 3 files changed, 19 insertions(+), 43 deletions(-) delete mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index e8e912c..2dcdbb2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -35,7 +35,7 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.authorizeRequests() .antMatchers("/oauth2/**", "/rbac/**", "/perm/**" - , "/oauth/code/**", "/password/**", "/wxapp/**", "/nonce/**") + , "/oauth/code/**", "/connect/nonce", "/password/**", "/wxapp/**") .permitAll(); super.configure(http); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 14f0dad..382f9cd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -6,14 +6,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import javax.annotation.security.PermitAll; import javax.servlet.http.HttpServletRequest; import javax.transaction.Transactional; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -43,7 +44,6 @@ import cn.seqdata.syslog.Syslog; @Api("第三方账号绑定") @RestController @RequestMapping("/connect") -@PreAuthorize("isAuthenticated()") @lombok.RequiredArgsConstructor public class ConnectController { private final ClientRegistrationRepository repository; @@ -75,6 +75,22 @@ public class ConnectController { return connectors; } + /** + * 向手机或邮箱发送一次性验证码 + */ + @Syslog + @ApiOperation("发送验证码") + @RequestMapping(value = "/nonce", method = {RequestMethod.GET, RequestMethod.POST}) + @PermitAll + public ResponseEntity nonce(String username) { + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) { + nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); + } + } + return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); + } + @Syslog @ApiOperation("通过密码绑定") @GetMapping(value = "/password") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java deleted file mode 100644 index c6dcc9f..0000000 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/NonceController.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.seqdata.oauth2.controller; - -import java.util.List; -import javax.annotation.security.PermitAll; - -import io.swagger.annotations.ApiOperation; -import org.joda.time.DateTimeConstants; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -import cn.seqdata.oauth2.nonce.NonceHandler; -import cn.seqdata.syslog.Syslog; - -/** - * @author jrxian - * @date 2020/12/15 17:20 - */ -@PermitAll -@RestController -@lombok.RequiredArgsConstructor -public class NonceController { - private final List nonceHandlers; - - /** - * 向手机或邮箱发送一次性验证码 - */ - @Syslog - @ApiOperation("发送验证码") - @RequestMapping(value = "/nonce", method = {RequestMethod.GET, RequestMethod.POST}) - public ResponseEntity nonce(String username) { - for(NonceHandler nonceHandler : nonceHandlers) { - if(nonceHandler.support(username)) { - nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); - } - } - return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); - } -} -- Gitee From 32bc4611eda4c557493507a604d5d8a7e2dd670a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 15 Dec 2020 21:01:34 +0800 Subject: [PATCH 144/228] =?UTF-8?q?=E5=8A=A0=E5=BC=BA=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9C=89=E6=95=88=E6=80=A7=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/controller/ConnectController.java | 4 +--- .../seqdata/oauth2/controller/PasswordController.java | 11 +++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 382f9cd..c9b468f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -32,7 +32,6 @@ import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; -import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; import cn.seqdata.syslog.Syslog; @@ -47,7 +46,6 @@ import cn.seqdata.syslog.Syslog; @lombok.RequiredArgsConstructor public class ConnectController { private final ClientRegistrationRepository repository; - private final UserAccountRepo accountRepo; private final UserService userService; private final PasswordEncoder passwordEncoder; private final UserDetailRepo userDetailRepo; @@ -82,7 +80,7 @@ public class ConnectController { @ApiOperation("发送验证码") @RequestMapping(value = "/nonce", method = {RequestMethod.GET, RequestMethod.POST}) @PermitAll - public ResponseEntity nonce(String username) { + public ResponseEntity nonce(@RequestParam String username) { for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index d99834b..a9c123e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -2,8 +2,6 @@ package cn.seqdata.oauth2.controller; import java.util.Collections; import javax.annotation.security.PermitAll; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import io.swagger.annotations.Api; @@ -21,6 +19,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.UserDetailsManager; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import cn.seqdata.oauth2.service.AccountLockService; @@ -45,7 +44,7 @@ public class PasswordController { @ApiOperation("密码过期修改") @PermitAll @PostMapping - public void updatePassword(@NotBlank String username, String oldPassword, @NotEmpty String newPassword) { + public void updatePassword(@RequestParam String username, @RequestParam String oldPassword, @RequestParam String newPassword) { UserDetails userDetails = userDetailsManager.loadUserByUsername(username); Authentication authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword); try { @@ -65,7 +64,7 @@ public class PasswordController { @ApiOperation("自己修改密码") @PostMapping("/self") @PreAuthorize("isAuthenticated()") - public void changePassword(String oldPassword, @NotEmpty String newPassword) { + public void changePassword(@RequestParam String oldPassword, @RequestParam String newPassword) { userDetailsManager.changePassword(oldPassword, newPassword); } @@ -73,7 +72,7 @@ public class PasswordController { @ApiOperation("管理员修改密码") @PostMapping("/admin") @PreAuthorize("hasAuthority('sysadmin')") - public void updatePassword(@NotBlank String username, @NotEmpty String password) { + public void updatePassword(@RequestParam String username, @RequestParam String password) { UserDetails userDetails = userDetailsManager.loadUserByUsername(username); passwordService.updatePassword(userDetails, passwordEncoder.encode(password)); } @@ -82,7 +81,7 @@ public class PasswordController { @ApiOperation("解锁密码") @PostMapping("/unlock") @PreAuthorize("hasAuthority('sysadmin')") - public void unlockPassword(@NotBlank String username) { + public void unlockPassword(@RequestParam String username) { lockService.unlock(username); } } -- Gitee From d8d5ad6fed508833b0cfb56371751ee5a810a1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 18 Dec 2020 10:47:18 +0800 Subject: [PATCH 145/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E8=A7=A3=E7=BB=91=EF=BC=8C=E5=B0=8F=E7=A8=8B=E5=BA=8F=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 17 ++++++----------- .../oauth2/controller/PasswordController.java | 14 ++++++++++---- .../oauth2/nonce/PasswordTokenGranter.java | 4 +++- .../seqdata/oauth2/service/UserService.java | 19 ++++++++++--------- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index c9b468f..840d7b8 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; -import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; @@ -149,22 +148,18 @@ public class ConnectController { @Transactional @DeleteMapping(value = "/{registrationId}") public void unbind(@PathVariable String registrationId, Principal principal) { + String grantType = SecurityUtils.grantType(principal); String username = SecurityUtils.username(principal); - UserDetailId entityId = new UserDetailId(registrationId, username); - if(userDetailRepo.existsById(entityId)) { - userDetailRepo.deleteById(entityId); - } else { - long userId = userService.loadUser(registrationId, username) - .map(UserAccount::getId) - .orElseThrow(() -> new UsernameNotFoundException("要解绑的账号不存在")); - userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); - } + Long userId = userService.loadUser(grantType, username) + .map(UserAccount::getId) + .orElseThrow(() -> new UsernameNotFoundException("要解绑的账号不存在")); + userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); } @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(AuthenticationException.class) public ErrorAttributes authenticationException(AuthenticationException ex) { - return new ErrorAttributes(HttpStatus.BAD_REQUEST, ex.getMessage(), ex); + return new ErrorAttributes(HttpStatus.BAD_REQUEST, ex.getMessage(), null); } private void bindNonce(String grantType, String username, int nonce, Principal principal) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index a9c123e..7d3a00c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -6,22 +6,22 @@ import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; +import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsPasswordService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.UserDetailsManager; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.syslog.Syslog; @@ -84,4 +84,10 @@ public class PasswordController { public void unlockPassword(@RequestParam String username) { lockService.unlock(username); } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(AuthenticationException.class) + public ErrorAttributes authenticationException(AuthenticationException ex) { + return new ErrorAttributes(HttpStatus.BAD_REQUEST, ex.getMessage(), ex); + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java index bf6c881..f7203f4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2.nonce; import java.util.LinkedHashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.*; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -47,7 +48,8 @@ public class PasswordTokenGranter extends AbstractTokenGranter { //给前端输出具体的错误类型,便于前端处理 //主要解决首次登录/管理员重置密码后必须修改密码才能登录的问题 Class exClass = ex.getClass(); - exception.addAdditionalInformation("exception", exClass.getSimpleName()); + String exClassName = StringUtils.replace(exClass.getSimpleName(), "Exception", ""); + exception.addAdditionalInformation("exception", exClassName); throw exception; } if(userAuth == null || !userAuth.isAuthenticated()) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index a92d1d0..ccc06fe 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -58,11 +58,10 @@ public class UserService { @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public void saveAsUser(String clientId, UserProfile profile) { UserDetailId entityId = new UserDetailId(clientId, profile.getName()); - UserDetail userDetail = new UserDetail(entityId); - - //用户不存在 Optional optional = userDetailRepo.findById(entityId); - if(!optional.isPresent()) { + if(!optional.isPresent()) {//用户不存在 + UserDetail userDetail = new UserDetail(entityId); + if(profile instanceof WechatUserProfile) { //查找微信的unionid,如果找到,视为同一个用户 String unionId = ((WechatUserProfile) profile).getUnionId(); @@ -86,6 +85,7 @@ public class UserService { /** * 如果用户存在就绑定,否则就创建 */ + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public void updateUser(String registrationId, UserProfile profile, @Nullable Principal principal) { if(Objects.nonNull(principal)) { //获取当前用户身份 @@ -95,9 +95,7 @@ public class UserService { .ifPresent(userId -> bindUser(userId, registrationId, profile)); } else { //当用户不存在时自动创建 - if(Objects.nonNull(profile.getName())) { - saveAsUser(registrationId, profile); - } + saveAsUser(registrationId, profile); } } @@ -116,9 +114,11 @@ public class UserService { bindUser(userId, clientId, profile.getName()); } + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public void bindUser(long userId, String clientId, String username) { UserDetailId entityId = new UserDetailId(clientId, username); - UserDetail entity = new UserDetail(entityId); + UserDetail entity = userDetailRepo.findById(entityId) + .orElse(new UserDetail(entityId)); entity.setUserId(userId); userDetailRepo.save(entity); } @@ -132,7 +132,8 @@ public class UserService { } private void saveAsUserAccount(Long entityId, UserProfile profile) { - UserAccount account = new UserAccount(entityId); + UserAccount account = accountRepo.findById(entityId) + .orElse(new UserAccount(entityId)); account.setUsername(profile.getUsername()); account.setEmail(profile.getEmail()); account.setMobile(profile.getMobile()); -- Gitee From 6655a5be09e681df75939c8ca3c9a6af01e2859d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 23 Dec 2020 02:11:28 +0800 Subject: [PATCH 146/228] =?UTF-8?q?=E6=8C=89=E7=85=A7IDEA=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E8=A7=84=E8=8C=83=E8=A6=81=E6=B1=82=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 2 +- .../oauth2/controller/PasswordController.java | 1 - .../oauth2/service/DefaultTokenServices.java | 2 +- ...zationCodeGrantRequestEntityConverter.java | 2 -- .../filter/authc/PathMatcherGlobalFilter.java | 2 -- .../filter/captcha/CaptchaGlobalFilter.java | 2 -- .../gateway/swagger/SwaggerProvider.java | 24 +++++++++---------- 7 files changed, 13 insertions(+), 22 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index 518611d..6a9616d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -104,7 +104,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu tokenGranters.add(new NonceTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.MOBILE)); NonceAuthenticationProvider emailNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByEmail, userDetailsManager, nonceHandlers); - tokenGranters.add(new NonceTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.EMAIL)); + tokenGranters.add(new NonceTokenGranter(emailNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.EMAIL)); if(Objects.nonNull(authenticationManager)) { //用户名密码验证,用authz.PasswordTokenGranter代替,需要把账号状态输出到前端 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 7d3a00c..e135a84 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -45,7 +45,6 @@ public class PasswordController { @PermitAll @PostMapping public void updatePassword(@RequestParam String username, @RequestParam String oldPassword, @RequestParam String newPassword) { - UserDetails userDetails = userDetailsManager.loadUserByUsername(username); Authentication authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword); try { //验证旧密码是否正确 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java index ab4796e..e501ce8 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java @@ -115,7 +115,7 @@ public class DefaultTokenServices implements AuthorizationServerTokenServices { } private Date expiration(int validitySeconds) { - return new Date(System.currentTimeMillis() + validitySeconds * DateTimeConstants.MILLIS_PER_SECOND); + return new Date(System.currentTimeMillis() + (long) validitySeconds * DateTimeConstants.MILLIS_PER_SECOND); } private boolean isExpired(OAuth2RefreshToken refreshToken) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java index ae3187d..12d693f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wechat/WechatAuthorizationCodeGrantRequestEntityConverter.java @@ -10,7 +10,6 @@ import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCo import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationExchange; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponse; import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.util.LinkedMultiValueMap; @@ -28,7 +27,6 @@ public class WechatAuthorizationCodeGrantRequestEntityConverter implements Conve ClientRegistration.ProviderDetails providerDetails = clientRegistration.getProviderDetails(); AuthorizationGrantType grantType = authorizationCodeGrantRequest.getGrantType(); OAuth2AuthorizationExchange authorizationExchange = authorizationCodeGrantRequest.getAuthorizationExchange(); - OAuth2AuthorizationRequest authorizationRequest = authorizationExchange.getAuthorizationRequest(); OAuth2AuthorizationResponse authorizationResponse = authorizationExchange.getAuthorizationResponse(); MultiValueMap formParameters = new LinkedMultiValueMap<>(); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java index d7010f2..65eda3e 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/PathMatcherGlobalFilter.java @@ -11,7 +11,6 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; import org.springframework.security.oauth2.provider.OAuth2Authentication; @@ -35,7 +34,6 @@ public class PathMatcherGlobalFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); - ServerHttpResponse response = exchange.getResponse(); HttpMethod httpMethod = request.getMethod(); RequestPath requestPath = request.getPath(); diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java index 4ceda1c..aa7ffbd 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/captcha/CaptchaGlobalFilter.java @@ -19,7 +19,6 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.server.PathContainer; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.AntPathMatcher; import org.springframework.util.CollectionUtils; import org.springframework.web.server.ServerWebExchange; @@ -61,7 +60,6 @@ public class CaptchaGlobalFilter implements GlobalFilter, Ordered { private void doCaptchaFilters(ServerWebExchange exchange) { ServerHttpRequest request = exchange.getRequest(); - ServerHttpResponse response = exchange.getResponse(); //只针对GET方法 if(!HttpMethod.GET.equals(request.getMethod())) { diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java index 170ca3c..51afe4c 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java @@ -45,20 +45,18 @@ public class SwaggerProvider implements SwaggerResourcesProvider { .toStream() //过滤掉网关本身 .filter(route -> !StringUtils.equals(routeId(route), serviceId)) - .forEach(route -> { - route.getPredicates() - .stream() - .filter(predicateDefinition -> "Path".equals(predicateDefinition.getName())) - .findFirst() - .ifPresent(predicate -> { - String routeId = routeId(route); - Map args = predicate.getArgs(); - String pattern = args.get("pattern"); - String location = StringUtils.replace(pattern, "/**", "/v2/api-docs"); + .forEach(route -> route.getPredicates() + .stream() + .filter(predicateDefinition -> "Path".equals(predicateDefinition.getName())) + .findFirst() + .ifPresent(predicate -> { + String routeId = routeId(route); + Map args = predicate.getArgs(); + String pattern = args.get("pattern"); + String location = StringUtils.replace(pattern, "/**", "/v2/api-docs"); - swaggerResources.add(swaggerResource(routeId, location)); - }); - }); + swaggerResources.add(swaggerResource(routeId, location)); + })); return swaggerResources; }) -- Gitee From e1874410d9ef0bd6af204a6b08e86a303b2d9baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 23 Dec 2020 16:55:46 +0800 Subject: [PATCH 147/228] =?UTF-8?q?=E9=80=9A=E8=BF=87=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/PasswordController.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index e135a84..2453149 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -1,6 +1,7 @@ package cn.seqdata.oauth2.controller; import java.util.Collections; +import java.util.List; import javax.annotation.security.PermitAll; import lombok.AllArgsConstructor; @@ -16,13 +17,14 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsPasswordService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.UserDetailsManager; import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; +import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.service.AccountLockService; +import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; import cn.seqdata.syslog.Syslog; /** @@ -37,8 +39,9 @@ public class PasswordController { private final PasswordEncoder passwordEncoder; private final AuthenticationManager authenticationManager; private final UserDetailsManager userDetailsManager; - private final UserDetailsPasswordService passwordService; + private final JpaUserDetailsPasswordService passwordService; private final AccountLockService lockService; + private final List nonceHandlers; @Syslog @ApiOperation("密码过期修改") @@ -59,6 +62,21 @@ public class PasswordController { SecurityContextHolder.clearContext(); } + @Syslog + @ApiOperation("通用验证码修改密码") + @PermitAll + @PostMapping("/nonce") + public void noncePassword(@RequestParam String username, @RequestParam String password, @RequestParam int nonce) { + //验证码有效性检查 + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) { + nonceHandler.check(username, nonce); + } + } + //直接设置新密码 + passwordService.updatePassword(username, password, null); + } + @Syslog @ApiOperation("自己修改密码") @PostMapping("/self") -- Gitee From b6616aa6b35524db31a8430f25498683d0a848b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 24 Dec 2020 14:38:34 +0800 Subject: [PATCH 148/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 2594f4b..806ba31 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -90,7 +90,7 @@ public class CurrentController { } @Syslog - @ApiOperation("修改显示名称") + @ApiOperation("修改昵称") @PatchMapping(value = "/name") public void updateName(Principal principal, @RequestBody UserAccountParam param) throws BindException { UserAccount entity = getUserAccount(principal); @@ -103,6 +103,21 @@ public class CurrentController { accountRepo.save(entity); } + @Syslog + @ApiOperation("修改登录名") + @PatchMapping(value = "/username") + public void updateUsername(Principal principal, @RequestBody UserAccountParam param) throws BindException { + UserAccount entity = getUserAccount(principal); + + BindException ex = new BindException(param, objectName); + checkField(ex, param, OAuth2Constants.USERNAME, UserAccountParam::getUsername); + checkExist(ex, param, OAuth2Constants.USERNAME, entity, UserAccountParam::getUsername, accountRepo::findByUsername); + checkErrors(ex); + + entity.setUsername(param.getUsername()); + accountRepo.save(entity); + } + @Syslog @ApiOperation("修改手机号") @PatchMapping(value = "/mobile") -- Gitee From 928b873f7f70623fe5051845a9b7fd7814d2f785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 25 Dec 2020 09:28:11 +0800 Subject: [PATCH 149/228] =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E4=BF=AE=E6=94=B9=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/AuthcServerConfiguration.java | 2 +- .../oauth2/controller/ConnectController.java | 2 +- .../oauth2/controller/PasswordController.java | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 2dcdbb2..0acbb7f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -34,7 +34,7 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.addFilterAfter(new PeriodFilter(periodProperties), ChannelProcessingFilter.class); http.authorizeRequests() - .antMatchers("/oauth2/**", "/rbac/**", "/perm/**" + .antMatchers("/csrf", "/oauth2/**", "/rbac/**", "/perm/**" , "/oauth/code/**", "/connect/nonce", "/password/**", "/wxapp/**") .permitAll(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 840d7b8..2062f02 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -79,7 +79,7 @@ public class ConnectController { @ApiOperation("发送验证码") @RequestMapping(value = "/nonce", method = {RequestMethod.GET, RequestMethod.POST}) @PermitAll - public ResponseEntity nonce(@RequestParam String username) { + public ResponseEntity nonce(String username) { for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 2453149..35b2213 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -47,7 +47,7 @@ public class PasswordController { @ApiOperation("密码过期修改") @PermitAll @PostMapping - public void updatePassword(@RequestParam String username, @RequestParam String oldPassword, @RequestParam String newPassword) { + public void updatePassword(String username, String oldPassword, String newPassword) { Authentication authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword); try { //验证旧密码是否正确 @@ -66,11 +66,11 @@ public class PasswordController { @ApiOperation("通用验证码修改密码") @PermitAll @PostMapping("/nonce") - public void noncePassword(@RequestParam String username, @RequestParam String password, @RequestParam int nonce) { + public void noncePassword(String mobile, int nonce, String username, String password) { //验证码有效性检查 for(NonceHandler nonceHandler : nonceHandlers) { - if(nonceHandler.support(username)) { - nonceHandler.check(username, nonce); + if(nonceHandler.support(mobile)) { + nonceHandler.check(mobile, nonce); } } //直接设置新密码 @@ -81,7 +81,7 @@ public class PasswordController { @ApiOperation("自己修改密码") @PostMapping("/self") @PreAuthorize("isAuthenticated()") - public void changePassword(@RequestParam String oldPassword, @RequestParam String newPassword) { + public void changePassword(String oldPassword, String newPassword) { userDetailsManager.changePassword(oldPassword, newPassword); } @@ -89,7 +89,7 @@ public class PasswordController { @ApiOperation("管理员修改密码") @PostMapping("/admin") @PreAuthorize("hasAuthority('sysadmin')") - public void updatePassword(@RequestParam String username, @RequestParam String password) { + public void updatePassword(String username, String password) { UserDetails userDetails = userDetailsManager.loadUserByUsername(username); passwordService.updatePassword(userDetails, passwordEncoder.encode(password)); } @@ -98,7 +98,7 @@ public class PasswordController { @ApiOperation("解锁密码") @PostMapping("/unlock") @PreAuthorize("hasAuthority('sysadmin')") - public void unlockPassword(@RequestParam String username) { + public void unlockPassword(String username) { lockService.unlock(username); } -- Gitee From 5158fcb5d5b8022a4165617d15b5795a839d82df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Fri, 25 Dec 2020 18:15:52 +0800 Subject: [PATCH 150/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=BD=91=E5=85=B3tok?= =?UTF-8?q?en=E5=A4=B1=E6=95=88=E6=8A=A5500=E9=94=99=E8=AF=AF=EF=BC=8C?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E5=BA=94=E8=AF=A5=E6=8A=A5401?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authc/CachedResourceServerTokenServices.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CachedResourceServerTokenServices.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CachedResourceServerTokenServices.java index 0e80a1b..7001fb6 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CachedResourceServerTokenServices.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/CachedResourceServerTokenServices.java @@ -1,6 +1,5 @@ package cn.seqdata.gateway.filter.authc; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import lombok.AllArgsConstructor; @@ -13,6 +12,7 @@ import org.springframework.security.oauth2.provider.token.ResourceServerTokenSer import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.util.concurrent.UncheckedExecutionException; /** * 缓存token,避免高频访问授权服务器 @@ -44,18 +44,18 @@ public class CachedResourceServerTokenServices implements ResourceServerTokenSer @Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { try { - return authenticationCache.get(accessToken); - } catch(ExecutionException ex) { - throw new InvalidTokenException("Invalid token " + accessToken, ex); + return authenticationCache.getUnchecked(accessToken); + } catch(UncheckedExecutionException ex) { + throw new InvalidTokenException(accessToken, ex); } } @Override public OAuth2AccessToken readAccessToken(String accessToken) { try { - return accessTokenCache.get(accessToken); - } catch(ExecutionException ex) { - throw new InvalidTokenException("Invalid token " + accessToken, ex); + return accessTokenCache.getUnchecked(accessToken); + } catch(UncheckedExecutionException ex) { + throw new InvalidTokenException(accessToken, ex); } } } -- Gitee From b6c2514a10599d6ac3cf564b62c68a805f17e8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 6 Jan 2021 19:55:52 +0800 Subject: [PATCH 151/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0hibernate=E5=92=8Cspr?= =?UTF-8?q?ing=E7=9A=84=E4=BA=8C=E7=BA=A7=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 31 +++++++++++++++---- .../oauth2/AuthzServerApplication.java | 2 ++ .../seqdata/oauth2/BizlogConfiguration.java | 8 +++++ .../oauth2/controller/ConnectController.java | 4 +-- .../oauth2/controller/CurrentController.java | 2 +- .../oauth2/controller/PasswordController.java | 9 +++--- .../oauth2/controller/RouterController.java | 2 +- .../seqdata/oauth2/wxapp/WxAppProvider.java | 2 +- .../src/main/resources/application.yml | 25 +++++++++++++-- .../src/main/resources/bootstrap.yml | 4 +-- .../src/main/resources/redisson-jcache.yaml | 3 ++ .../seqdata/gateway/GatewayConfiguration.java | 7 +++++ 12 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/resources/redisson-jcache.yaml diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 73e5291..08e867c 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -10,9 +10,29 @@ seqdata-cloud-authz 认证服务器,提供oauth2认证,接入第三方认证(如微信、钉钉) + + org.hibernate + hibernate-jcache + + + cn.seqdata.cloud + seqdata-cloud-cache + 2.2.1-SNAPSHOT + + + + + org.springframework.amqp + spring-rabbit + + org.springframework.boot - spring-boot-starter-web + spring-boot-starter-cache org.springframework.boot @@ -26,6 +46,10 @@ org.springframework.boot spring-boot-starter-data-redis + + org.springframework.boot + spring-boot-starter-web + org.springframework.boot spring-boot-configuration-processor @@ -41,11 +65,6 @@ spring-security-oauth2-autoconfigure - - org.springframework.amqp - spring-rabbit - - com.aliyun aliyun-java-sdk-core diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index 81a2e98..7d4108c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -2,6 +2,7 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -12,6 +13,7 @@ import cn.seqdata.wxapp.WxAppFeignClient; * Date: 2019-10-02 19:11 */ @SpringBootApplication +@EnableCaching @EnableDiscoveryClient @EnableFeignClients(clients = WxAppFeignClient.class) public class AuthzServerApplication { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index 5dd76d1..ac887a2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -1,6 +1,8 @@ package cn.seqdata.oauth2; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationListener; @@ -11,6 +13,7 @@ import org.springframework.security.authentication.event.AuthenticationSuccessEv import org.springframework.security.authentication.event.LogoutSuccessEvent; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.syslog.*; @@ -31,6 +34,11 @@ public class BizlogConfiguration { // return syslogConsumer; // } + @Bean + public MessageConverter messageConverter(ObjectMapper objectMapper) { + return new Jackson2JsonMessageConverter(objectMapper); + } + @Bean @ConditionalOnProperty("spring.rabbitmq.host") public RabbitSyslogConsumer rabbitSyslogConsumer(RabbitTemplate rabbitTemplate) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 2062f02..e37efdb 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -82,10 +82,10 @@ public class ConnectController { public ResponseEntity nonce(String username) { for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { - nonceHandler.nonce(username, 5 * DateTimeConstants.SECONDS_PER_MINUTE); + nonceHandler.nonce(username, 2 * DateTimeConstants.SECONDS_PER_MINUTE); } } - return ResponseEntity.ok("验证码已经发送,5分钟内输入有效!"); + return ResponseEntity.ok("验证码已经发送,2分钟内输入有效!"); } @Syslog diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 806ba31..69e74a4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -41,7 +41,7 @@ import cn.seqdata.syslog.Syslog; @PreAuthorize("isAuthenticated()") @AllArgsConstructor public class CurrentController { - private static final String objectName = "UserAccount"; + private static final String objectName = UserAccount.class.getSimpleName(); private final UserAccountRepo accountRepo; private final UserRepo userRepo; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 35b2213..7c3fd06 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -25,6 +25,7 @@ import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; +import cn.seqdata.oauth2.util.PasswordUtils; import cn.seqdata.syslog.Syslog; /** @@ -48,13 +49,13 @@ public class PasswordController { @PermitAll @PostMapping public void updatePassword(String username, String oldPassword, String newPassword) { - Authentication authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword); + Authentication authenticate = new UsernamePasswordAuthenticationToken(username, PasswordUtils.encode(oldPassword)); try { //验证旧密码是否正确 authenticate = authenticationManager.authenticate(authenticate); } catch(CredentialsExpiredException ex) { - //必须吃掉这个异常,密码已经过期 - authenticate = new UsernamePasswordAuthenticationToken(username, oldPassword, Collections.emptyList()); + //密码已经过期,必须吃掉这个异常 + authenticate = new UsernamePasswordAuthenticationToken(username, PasswordUtils.encode(oldPassword), Collections.emptyList()); } SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication(authenticate); @@ -91,7 +92,7 @@ public class PasswordController { @PreAuthorize("hasAuthority('sysadmin')") public void updatePassword(String username, String password) { UserDetails userDetails = userDetailsManager.loadUserByUsername(username); - passwordService.updatePassword(userDetails, passwordEncoder.encode(password)); + passwordService.updatePassword(userDetails, password); } @Syslog diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index f66c44e..e86e7ea 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; import java.util.Objects; +import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import org.springframework.data.domain.Sort; @@ -13,7 +14,6 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; -import lombok.AllArgsConstructor; import cn.seqdata.antd.Router; import cn.seqdata.oauth2.jpa.perm.ModulePermission; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java index 43035a4..c482086 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppProvider.java @@ -4,11 +4,11 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.function.Function; +import lombok.AllArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; -import lombok.AllArgsConstructor; import cn.seqdata.oauth2.jpa.oauth.Provider; import cn.seqdata.oauth2.repos.oauth.RegistrationRepo; diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index b677c0f..8649ebf 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -8,6 +8,20 @@ spring: url: jdbc:sqlserver://dev.voltmao.com:1433;DatabaseName=data_space_base username: voltcat password: vot@2018 + jpa: + show-sql: true + properties: + hibernate: + cache: + use_second_level_cache: true + use_query_cache: true + region: + factory_class: org.hibernate.cache.jcache.JCacheRegionFactory + javax: + cache: + missing_cache_strategy: create + cache: + type: simple data: jdbc: repositories: @@ -16,9 +30,14 @@ spring: repositories: enabled: false redis: - host: db.rtelec.cn - port: 6379 - password: seq2015 + host: dev.voltmao.com + password: ftm123 + #rabbitmq: + # host: dev.voltmao.com + #username: seqdata + #password: seq@2015 + #template: + #exchange: syslog security: oauth2: client: diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index 0e669a4..87b2828 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -14,6 +14,4 @@ spring: file-extension: ${NACOS_FILE_EXT:yml} shared-configs: - group: ${spring.cloud.nacos.discovery.group} - data_id: common.${spring.cloud.nacos.config.file-extension} -server: - port: 0 + data_id: common.${spring.cloud.nacos.config.file-extension} \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/resources/redisson-jcache.yaml b/seqdata-cloud-authz/src/main/resources/redisson-jcache.yaml new file mode 100644 index 0000000..4769dc5 --- /dev/null +++ b/seqdata-cloud-authz/src/main/resources/redisson-jcache.yaml @@ -0,0 +1,3 @@ +singleServerConfig: + address: redis://${spring.redis.host}:${spring.redis.port} + password: ${spring.redis.password} \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java index d849399..6f2aaa7 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/GatewayConfiguration.java @@ -3,6 +3,8 @@ package cn.seqdata.gateway; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -32,6 +34,11 @@ import cn.seqdata.oauth2.client.security.PathPermissionEvaluator; @EnableConfigurationProperties({IgnoringProperties.class, CaptchaProperties.class}) public class GatewayConfiguration { + @Bean + public MessageConverter messageConverter() { + return new Jackson2JsonMessageConverter(); + } + @Bean @ConditionalOnProperty("spring.rabbitmq.host") public RabbitRecorder rabbitLogRecorder(RabbitTemplate rabbitTemplate) { -- Gitee From 34519ce9a4261dced39b1b1e9fbfa694a8df8fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 7 Jan 2021 14:53:22 +0800 Subject: [PATCH 152/228] =?UTF-8?q?RedisTokenStore=E6=94=B9=E4=B8=BApipeli?= =?UTF-8?q?ne=E8=BF=90=E8=A1=8C=EF=BC=8C=E6=8F=90=E9=AB=98=E6=95=88?= =?UTF-8?q?=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/service/RedisTokenStore.java | 167 +++++++++++------- 1 file changed, 106 insertions(+), 61 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java index 4a0cc82..9197986 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java @@ -4,10 +4,11 @@ import java.util.*; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisZSetCommands; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.data.redis.core.ValueOperations; -import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.data.redis.core.*; +import org.springframework.lang.NonNull; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken; import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken; @@ -81,8 +82,14 @@ public class RedisTokenStore implements TokenStore { if(Objects.nonNull(accessToken)) { int expiresIn = accessToken.getExpiresIn(); if(expiresIn > 0) { - redisTemplate.expire(ACCESS + tokenValue, expiresIn, TimeUnit.SECONDS); - redisTemplate.expire(ACCESS_AUTH + tokenValue, expiresIn, TimeUnit.SECONDS); + redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(@NonNull RedisOperations operations) throws DataAccessException { + redisTemplate.expire(ACCESS + tokenValue, expiresIn, TimeUnit.SECONDS); + redisTemplate.expire(ACCESS_AUTH + tokenValue, expiresIn, TimeUnit.SECONDS); + return null; + } + }); } } return accessToken; @@ -90,6 +97,7 @@ public class RedisTokenStore implements TokenStore { @Override public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { + ZSetOperations opsForZSet = redisTemplate.opsForZSet(); String key = USERNAME_TO_ACCESS + getApprovalKey(authentication); //找到最后一次生成的token Set> tuples = opsForZSet.reverseRangeWithScores(key, 0, 1); @@ -103,35 +111,41 @@ public class RedisTokenStore implements TokenStore { @Override public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { - OAuth2Request auth2Request = authentication.getOAuth2Request(); - String tokenValue = token.getValue(); - int expires = token.getExpiresIn(); - opsForValue.set(ACCESS + tokenValue, toJSON(token), expires, TimeUnit.SECONDS); - opsForValue.set(ACCESS_AUTH + tokenValue, toJSON(authentication), expires, TimeUnit.SECONDS); - if(!authentication.isClientOnly()) { - String key = USERNAME_TO_ACCESS + getApprovalKey(authentication); - opsForZSet.add(key, tokenValue, System.currentTimeMillis()); - checkMaximumSessions(key); - } else { - String key = CLIENTID_TO_ACCESS + auth2Request.getClientId(); - opsForZSet.add(key, tokenValue, System.currentTimeMillis()); - } + redisTemplate.execute(new SessionCallback() { + @Override + public Object execute(@NonNull RedisOperations operations) throws DataAccessException { + OAuth2Request auth2Request = authentication.getOAuth2Request(); + String tokenValue = token.getValue(); + int expires = token.getExpiresIn(); + opsForValue.set(ACCESS + tokenValue, toJSON(token), expires, TimeUnit.SECONDS); + opsForValue.set(ACCESS_AUTH + tokenValue, toJSON(authentication), expires, TimeUnit.SECONDS); + if(!authentication.isClientOnly()) { + String key = USERNAME_TO_ACCESS + getApprovalKey(authentication); + opsForZSet.add(key, tokenValue, System.currentTimeMillis()); + checkMaximumSessions(key); + } else { + String key = CLIENTID_TO_ACCESS + auth2Request.getClientId(); + opsForZSet.add(key, tokenValue, System.currentTimeMillis()); + } - OAuth2RefreshToken refreshToken = token.getRefreshToken(); - if(Objects.nonNull(refreshToken) && Objects.nonNull(refreshToken.getValue())) { - storeRefreshToken(refreshToken, authentication); + OAuth2RefreshToken refreshToken = token.getRefreshToken(); + if(Objects.nonNull(refreshToken) && Objects.nonNull(refreshToken.getValue())) { + storeRefreshToken(refreshToken, authentication); - String refreshToAccessKey = REFRESH_TO_ACCESS + refreshToken.getValue(); - String accessToRefreshKey = ACCESS_TO_REFRESH + tokenValue; + String refreshToAccessKey = REFRESH_TO_ACCESS + refreshToken.getValue(); + String accessToRefreshKey = ACCESS_TO_REFRESH + tokenValue; - opsForValue.set(refreshToAccessKey, tokenValue); - opsForValue.set(accessToRefreshKey, refreshToken.getValue()); - if(refreshToken instanceof ExpiringOAuth2RefreshToken) { - Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration(); - redisTemplate.expireAt(refreshToAccessKey, expiration); - redisTemplate.expireAt(accessToRefreshKey, expiration); + opsForValue.set(refreshToAccessKey, tokenValue); + opsForValue.set(accessToRefreshKey, refreshToken.getValue()); + if(refreshToken instanceof ExpiringOAuth2RefreshToken) { + Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration(); + redisTemplate.expireAt(refreshToAccessKey, expiration); + redisTemplate.expireAt(accessToRefreshKey, expiration); + } + } + return null; } - } + }); } @Override @@ -146,16 +160,22 @@ public class RedisTokenStore implements TokenStore { @Override public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { - String refreshKey = REFRESH + refreshToken.getValue(); - String refreshAuthKey = REFRESH_AUTH + refreshToken.getValue(); - - opsForValue.set(refreshKey, toJSON(refreshToken)); - opsForValue.set(refreshAuthKey, toJSON(authentication)); - if(refreshToken instanceof ExpiringOAuth2RefreshToken) { - Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration(); - redisTemplate.expireAt(refreshKey, expiration); - redisTemplate.expireAt(refreshAuthKey, expiration); - } + redisTemplate.execute(new SessionCallback() { + @Override + public Object execute(@NonNull RedisOperations operations) throws DataAccessException { + String refreshKey = REFRESH + refreshToken.getValue(); + String refreshAuthKey = REFRESH_AUTH + refreshToken.getValue(); + + opsForValue.set(refreshKey, toJSON(refreshToken)); + opsForValue.set(refreshAuthKey, toJSON(authentication)); + if(refreshToken instanceof ExpiringOAuth2RefreshToken) { + Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration(); + redisTemplate.expireAt(refreshKey, expiration); + redisTemplate.expireAt(refreshAuthKey, expiration); + } + return null; + } + }); } @Override @@ -190,6 +210,8 @@ public class RedisTokenStore implements TokenStore { private Collection findTokens(String key) { List accessTokens = new LinkedList<>(); + ZSetOperations opsForZSet = redisTemplate.opsForZSet(); + Set tokenValues = opsForZSet.rangeByLex(key, RedisZSetCommands.Range.unbounded()); if(!CollectionUtils.isEmpty(tokenValues)) { tokenValues.forEach(tokenValue -> { @@ -212,40 +234,63 @@ public class RedisTokenStore implements TokenStore { */ private void checkMaximumSessions(String key) { SortedMap tokenMap = new TreeMap<>(); + + ZSetOperations opsForZSet = redisTemplate.opsForZSet(); Set> tuples = opsForZSet.rangeWithScores(key, 0, -1); //检查不存在的accessToken,先清除 if(!CollectionUtils.isEmpty(tuples)) { - Iterator> iterator = tuples.iterator(); - while(iterator.hasNext()) { - ZSetOperations.TypedTuple tuple = iterator.next(); - if(Objects.nonNull(tuple.getValue())) { - String tokenValue = tuple.getValue(); - if(BooleanUtils.isNotTrue(redisTemplate.hasKey(ACCESS + tokenValue))) { - //accessToken已经不存在了,从清单中删除 - opsForZSet.remove(key, tokenValue); - iterator.remove(); - } else { - if(Objects.nonNull(tuple.getScore())) { - tokenMap.put(tuple.getScore(), tokenValue); - } + //一次性查出所有的key是否存在 + List hasKeys = redisTemplate.executePipelined(new SessionCallback() { + @Override + public Object execute(@NonNull RedisOperations operations) throws DataAccessException { + tuples.forEach(tuple -> { + String value = ObjectUtils.defaultIfNull(tuple.getValue(), String.valueOf(UUID.randomUUID())); + redisTemplate.hasKey(value); + }); + return null; + } + }); + + //不存在的key,全部删除 + Iterator> i_ = tuples.iterator(); + Iterator j_ = hasKeys.iterator(); + while(i_.hasNext() && j_.hasNext()) { + ZSetOperations.TypedTuple tuple = i_.next(); + String tupleValue = tuple.getValue(); + Object hasKey = j_.next(); + + if(hasKey instanceof Boolean && BooleanUtils.isNotTrue((Boolean) hasKey)) { + //accessToken已经不存在了,从清单中删除 + opsForZSet.remove(key, tupleValue); + i_.remove(); + } else { + if(Objects.nonNull(tuple.getScore())) { + tokenMap.put(tuple.getScore(), tupleValue); } } } } - //对现存的accessToken排序 + //对现存的accessToken排序,超过最大并发会话数限制的,直接删除 List> tokenTuples = new ArrayList<>(tokenMap.entrySet()); if(maximumSessions > 0) { //删除最旧的token - for(int i = 0; i < tokenTuples.size() - maximumSessions; ++i) { - Map.Entry entry = tokenTuples.get(i); - String tokenValue = entry.getValue(); + redisTemplate.execute(new SessionCallback() { + @Override + public Object execute(@NonNull RedisOperations operations) throws DataAccessException { + for(int i = 0; i < tokenTuples.size() - maximumSessions; ++i) { + Map.Entry entry = tokenTuples.get(i); + String tokenValue = entry.getValue(); - redisTemplate.delete(ACCESS + tokenValue); - redisTemplate.delete(ACCESS_AUTH + tokenValue); - opsForZSet.remove(key, tokenValue); - } + redisTemplate.delete(ACCESS + tokenValue); + redisTemplate.delete(ACCESS_AUTH + tokenValue); + + opsForZSet.remove(key, tokenValue); + } + return null; + } + }); } } -- Gitee From 7b6f0e17665ab56100489d3f433a276824dfc404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 7 Jan 2021 16:37:01 +0800 Subject: [PATCH 153/228] =?UTF-8?q?RedisTokenStore=E6=94=B9=E4=B8=BApipeli?= =?UTF-8?q?ne=E8=BF=90=E8=A1=8C=EF=BC=8C=E6=8F=90=E9=AB=98=E6=95=88?= =?UTF-8?q?=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../oauth2/service/RedisTokenStore.java | 48 +++++++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 74188ce..ce39374 100644 --- a/pom.xml +++ b/pom.xml @@ -84,7 +84,7 @@ - ${artifactId} + ${project.artifactId} http://docker.seqdata.cn:2375 src/main/docker diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java index 9197986..604bb7d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java @@ -245,31 +245,39 @@ public class RedisTokenStore implements TokenStore { @Override public Object execute(@NonNull RedisOperations operations) throws DataAccessException { tuples.forEach(tuple -> { - String value = ObjectUtils.defaultIfNull(tuple.getValue(), String.valueOf(UUID.randomUUID())); - redisTemplate.hasKey(value); + String tokenValue = ObjectUtils.defaultIfNull(tuple.getValue(), String.valueOf(UUID.randomUUID())); + redisTemplate.hasKey(ACCESS + tokenValue); }); return null; } }); - //不存在的key,全部删除 - Iterator> i_ = tuples.iterator(); - Iterator j_ = hasKeys.iterator(); - while(i_.hasNext() && j_.hasNext()) { - ZSetOperations.TypedTuple tuple = i_.next(); - String tupleValue = tuple.getValue(); - Object hasKey = j_.next(); - - if(hasKey instanceof Boolean && BooleanUtils.isNotTrue((Boolean) hasKey)) { - //accessToken已经不存在了,从清单中删除 - opsForZSet.remove(key, tupleValue); - i_.remove(); - } else { - if(Objects.nonNull(tuple.getScore())) { - tokenMap.put(tuple.getScore(), tupleValue); + redisTemplate.execute(new SessionCallback() { + @Override + public Object execute(@NonNull RedisOperations operations) throws DataAccessException { + //不存在的key,全部删除 + Iterator> i_ = tuples.iterator(); + Iterator j_ = hasKeys.iterator(); + while(i_.hasNext() && j_.hasNext()) { + ZSetOperations.TypedTuple tuple = i_.next(); + String tokenValue = tuple.getValue(); + Object hasKey = j_.next(); + + if(hasKey instanceof Boolean && BooleanUtils.isNotTrue((Boolean) hasKey)) { + //accessToken已经不存在了,从清单中删除 + i_.remove(); + if(Objects.nonNull(tokenValue)) { + opsForZSet.remove(key, tokenValue); + } + } else { + if(Objects.nonNull(tuple.getScore())) { + tokenMap.put(tuple.getScore(), tokenValue); + } + } } + return null; } - } + }); } //对现存的accessToken排序,超过最大并发会话数限制的,直接删除 @@ -282,11 +290,9 @@ public class RedisTokenStore implements TokenStore { for(int i = 0; i < tokenTuples.size() - maximumSessions; ++i) { Map.Entry entry = tokenTuples.get(i); String tokenValue = entry.getValue(); - + opsForZSet.remove(key, tokenValue); redisTemplate.delete(ACCESS + tokenValue); redisTemplate.delete(ACCESS_AUTH + tokenValue); - - opsForZSet.remove(key, tokenValue); } return null; } -- Gitee From 37f253f607310cb5c135138946810527f4cacfad Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 8 Jan 2021 09:03:54 +0800 Subject: [PATCH 154/228] =?UTF-8?q?=E8=A7=92=E8=89=B2=E4=B8=8D=E5=A4=A7?= =?UTF-8?q?=E5=B0=8F=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerApplication.java | 3 +-- .../src/main/resources/application.yml | 24 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java index 7d4108c..986a5d3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerApplication.java @@ -2,7 +2,6 @@ package cn.seqdata.oauth2; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -13,7 +12,7 @@ import cn.seqdata.wxapp.WxAppFeignClient; * Date: 2019-10-02 19:11 */ @SpringBootApplication -@EnableCaching +//@EnableCaching @EnableDiscoveryClient @EnableFeignClients(clients = WxAppFeignClient.class) public class AuthzServerApplication { diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 8649ebf..1258df4 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -8,18 +8,18 @@ spring: url: jdbc:sqlserver://dev.voltmao.com:1433;DatabaseName=data_space_base username: voltcat password: vot@2018 - jpa: - show-sql: true - properties: - hibernate: - cache: - use_second_level_cache: true - use_query_cache: true - region: - factory_class: org.hibernate.cache.jcache.JCacheRegionFactory - javax: - cache: - missing_cache_strategy: create +# jpa: +# show-sql: true +# properties: +# hibernate: +# cache: +# use_second_level_cache: true +# use_query_cache: true +# region: +# factory_class: org.hibernate.cache.jcache.JCacheRegionFactory +# javax: +# cache: +# missing_cache_strategy: create cache: type: simple data: -- Gitee From e8235701730ba35ff8b3521ddd5391b9824403d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 12 Jan 2021 21:10:53 +0800 Subject: [PATCH 155/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 25 ++++++++++++++++--- .../oauth2/controller/PasswordController.java | 21 ++++++++++------ .../src/main/resources/application.yml | 13 +++++----- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index abe10a6..ae861ff 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -15,10 +15,7 @@ import org.springframework.security.oauth2.provider.token.TokenStore; import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.repos.rbac.UserRepo; -import cn.seqdata.oauth2.service.DefaultTokenEnhancer; -import cn.seqdata.oauth2.service.DefaultTokenServices; -import cn.seqdata.oauth2.service.RedisTokenStore; -import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.oauth2.service.*; /** * @author jrxian @@ -28,6 +25,26 @@ import cn.seqdata.oauth2.service.UserService; @AllArgsConstructor public class AuthzServerConfiguration { + @Bean + public KeyExchanger keyExchanger() { + return new KeyExchanger() { + @Override + public String publicKey() { + return null; + } + + @Override + public String encrypt(String data) { + return data; + } + + @Override + public String decrypt(String encryptedData) { + return encryptedData; + } + }; + } + @Bean public PasswordEncoder passwordEncoder() { PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 7c3fd06..79d8cfe 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -25,7 +25,7 @@ import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; -import cn.seqdata.oauth2.util.PasswordUtils; +import cn.seqdata.oauth2.service.KeyExchanger; import cn.seqdata.syslog.Syslog; /** @@ -37,6 +37,7 @@ import cn.seqdata.syslog.Syslog; @RequestMapping("/password") @AllArgsConstructor public class PasswordController { + private final KeyExchanger keyExchanger; private final PasswordEncoder passwordEncoder; private final AuthenticationManager authenticationManager; private final UserDetailsManager userDetailsManager; @@ -49,17 +50,19 @@ public class PasswordController { @PermitAll @PostMapping public void updatePassword(String username, String oldPassword, String newPassword) { - Authentication authenticate = new UsernamePasswordAuthenticationToken(username, PasswordUtils.encode(oldPassword)); + String decryptOldPswd = keyExchanger.decrypt(oldPassword); + String decryptNewPswd = keyExchanger.decrypt(newPassword); + Authentication authenticate = new UsernamePasswordAuthenticationToken(username, decryptOldPswd); try { //验证旧密码是否正确 authenticate = authenticationManager.authenticate(authenticate); } catch(CredentialsExpiredException ex) { //密码已经过期,必须吃掉这个异常 - authenticate = new UsernamePasswordAuthenticationToken(username, PasswordUtils.encode(oldPassword), Collections.emptyList()); + authenticate = new UsernamePasswordAuthenticationToken(username, decryptOldPswd, Collections.emptyList()); } SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication(authenticate); - userDetailsManager.changePassword(oldPassword, newPassword); + userDetailsManager.changePassword(decryptOldPswd, decryptNewPswd); SecurityContextHolder.clearContext(); } @@ -75,7 +78,8 @@ public class PasswordController { } } //直接设置新密码 - passwordService.updatePassword(username, password, null); + String decryptPassword = keyExchanger.decrypt(password); + passwordService.updatePassword(username, decryptPassword, null); } @Syslog @@ -83,7 +87,9 @@ public class PasswordController { @PostMapping("/self") @PreAuthorize("isAuthenticated()") public void changePassword(String oldPassword, String newPassword) { - userDetailsManager.changePassword(oldPassword, newPassword); + String decryptOldPswd = keyExchanger.decrypt(oldPassword); + String decryptNewPswd = keyExchanger.decrypt(newPassword); + userDetailsManager.changePassword(decryptOldPswd, decryptNewPswd); } @Syslog @@ -91,8 +97,9 @@ public class PasswordController { @PostMapping("/admin") @PreAuthorize("hasAuthority('sysadmin')") public void updatePassword(String username, String password) { + String decryptPassword = keyExchanger.decrypt(password); UserDetails userDetails = userDetailsManager.loadUserByUsername(username); - passwordService.updatePassword(userDetails, password); + passwordService.updatePassword(userDetails, decryptPassword); } @Syslog diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 1258df4..0284575 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -32,12 +32,13 @@ spring: redis: host: dev.voltmao.com password: ftm123 - #rabbitmq: - # host: dev.voltmao.com - #username: seqdata - #password: seq@2015 - #template: - #exchange: syslog + database: 15 + rabbitmq: + host: dev.voltmao.com + username: seqdata + password: seq@2015 + template: + exchange: syslog security: oauth2: client: -- Gitee From 6c8f4109fa9400d70a8a704e104d01ced06d948b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 12 Jan 2021 21:12:32 +0800 Subject: [PATCH 156/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/controller/PasswordController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 79d8cfe..d425b8a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -45,6 +45,12 @@ public class PasswordController { private final AccountLockService lockService; private final List nonceHandlers; + @PermitAll + @GetMapping("/key") + public String key() { + return keyExchanger.publicKey(); + } + @Syslog @ApiOperation("密码过期修改") @PermitAll -- Gitee From cd9812d4d6533ce25994239864efdde817f24e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 13 Jan 2021 10:16:18 +0800 Subject: [PATCH 157/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthzServerConfiguration.java | 27 +++++++------------ .../oauth2/controller/PasswordController.java | 2 +- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index ae861ff..61bac65 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -13,9 +13,14 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; +import cn.seqdata.oauth2.policy.KeyExchanger; +import cn.seqdata.oauth2.policy.KeyExchangers; import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.repos.rbac.UserRepo; -import cn.seqdata.oauth2.service.*; +import cn.seqdata.oauth2.service.DefaultTokenEnhancer; +import cn.seqdata.oauth2.service.DefaultTokenServices; +import cn.seqdata.oauth2.service.RedisTokenStore; +import cn.seqdata.oauth2.service.UserService; /** * @author jrxian @@ -26,23 +31,9 @@ import cn.seqdata.oauth2.service.*; public class AuthzServerConfiguration { @Bean - public KeyExchanger keyExchanger() { - return new KeyExchanger() { - @Override - public String publicKey() { - return null; - } - - @Override - public String encrypt(String data) { - return data; - } - - @Override - public String decrypt(String encryptedData) { - return encryptedData; - } - }; + public KeyExchanger keyExchanger(PasswordPolicy policy) { + KeyExchangers keyExchangeMethod = policy.getKeyExchangeMethod(); + return keyExchangeMethod.supplier.get(); } @Bean diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index d425b8a..95a6512 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -23,9 +23,9 @@ import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.nonce.NonceHandler; +import cn.seqdata.oauth2.policy.KeyExchanger; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; -import cn.seqdata.oauth2.service.KeyExchanger; import cn.seqdata.syslog.Syslog; /** -- Gitee From e588f0c0d46d71178641ef51c3d06119916b50f5 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 13 Jan 2021 15:30:01 +0800 Subject: [PATCH 158/228] =?UTF-8?q?use=5Fsecond=5Flevel=5Fcache=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=80=BC=E6=98=AFtrue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 0284575..47a8b41 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: application: - name: authz + name: authz2 profiles: active: logging, jackson, jpa datasource: @@ -8,20 +8,19 @@ spring: url: jdbc:sqlserver://dev.voltmao.com:1433;DatabaseName=data_space_base username: voltcat password: vot@2018 -# jpa: -# show-sql: true -# properties: -# hibernate: -# cache: -# use_second_level_cache: true -# use_query_cache: true -# region: -# factory_class: org.hibernate.cache.jcache.JCacheRegionFactory -# javax: -# cache: -# missing_cache_strategy: create - cache: - type: simple + jpa: + properties: + hibernate: + cache: + use_second_level_cache: false + # use_query_cache: true + # region: + # factory_class: org.hibernate.cache.jcache.JCacheRegionFactory + # javax: + # cache: + # missing_cache_strategy: create + # cache: + # type: simple data: jdbc: repositories: -- Gitee From 31c66ebdc904ab1383a0ac6870137a40c9680697 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 13 Jan 2021 16:01:34 +0800 Subject: [PATCH 159/228] =?UTF-8?q?authz=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 47a8b41..f8e981b 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -1,6 +1,6 @@ spring: application: - name: authz2 + name: authz profiles: active: logging, jackson, jpa datasource: -- Gitee From 5c311c1f137d638afaea4ac62beacc4b386f706c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 13 Jan 2021 16:42:24 +0800 Subject: [PATCH 160/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/PasswordController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 95a6512..d93d89d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -48,7 +48,7 @@ public class PasswordController { @PermitAll @GetMapping("/key") public String key() { - return keyExchanger.publicKey(); + return keyExchanger.getPublicKey(); } @Syslog -- Gitee From 44723c2aa9ec9b20c9e28ebaad32cccb7860424b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 13 Jan 2021 16:45:03 +0800 Subject: [PATCH 161/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 61bac65..768e5a4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -33,7 +33,9 @@ public class AuthzServerConfiguration { @Bean public KeyExchanger keyExchanger(PasswordPolicy policy) { KeyExchangers keyExchangeMethod = policy.getKeyExchangeMethod(); - return keyExchangeMethod.supplier.get(); + KeyExchanger keyExchanger = keyExchangeMethod.supplier.get(); + keyExchanger.genKeyPair(); + return keyExchanger; } @Bean -- Gitee From aee21f3a5031022a7ffdd5658ff866967e339f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 13 Jan 2021 17:38:28 +0800 Subject: [PATCH 162/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/java/PasswordTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 seqdata-cloud-authz/src/test/java/PasswordTest.java diff --git a/seqdata-cloud-authz/src/test/java/PasswordTest.java b/seqdata-cloud-authz/src/test/java/PasswordTest.java new file mode 100644 index 0000000..22156d2 --- /dev/null +++ b/seqdata-cloud-authz/src/test/java/PasswordTest.java @@ -0,0 +1,14 @@ +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * @author jrxian + * @date 2021/1/13 17:00 + */ +public class PasswordTest { + public static void main(String[] args) { + PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); + String encodedPassword = "{bcrypt}$2a$10$LdxUdtkJAZAj0KAkMAqrP.KwFjBnl8yzPgSpKRWVvQrAHvAl294g2"; + System.err.println(encoder.matches("1", encodedPassword)); + } +} -- Gitee From 81dc7309e49657893e3f5b4b3a4e6a0dd359af20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 13 Jan 2021 21:56:37 +0800 Subject: [PATCH 163/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 4 +++- .../oauth2/nonce/PasswordTokenGranter.java | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index 6a9616d..c5657d5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -32,6 +32,7 @@ import cn.seqdata.oauth2.nonce.NonceAuthenticationProvider; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.nonce.NonceTokenGranter; import cn.seqdata.oauth2.nonce.PasswordTokenGranter; +import cn.seqdata.oauth2.policy.KeyExchanger; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; @@ -47,6 +48,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu private final AuthenticationManager authenticationManager; private final JpaUserDetailsManager userDetailsManager; private final AuthorizationServerTokenServices tokenServices; + private final KeyExchanger keyExchanger; private final TokenStore tokenStore; private final TokenEnhancer tokenEnhancer; private final ClientDetailRepo clientDetailRepo; @@ -108,7 +110,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu if(Objects.nonNull(authenticationManager)) { //用户名密码验证,用authz.PasswordTokenGranter代替,需要把账号状态输出到前端 - tokenGranters.add(new PasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); + tokenGranters.add(new PasswordTokenGranter(keyExchanger, authenticationManager, tokenServices, clientDetails, requestFactory)); } return new CompositeTokenGranter(tokenGranters); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java index f7203f4..3f788d8 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java @@ -13,6 +13,8 @@ import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import cn.seqdata.oauth2.policy.KeyExchanger; + /** * @author jrxian * @date 2020/12/14 1:56 @@ -20,16 +22,20 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic public class PasswordTokenGranter extends AbstractTokenGranter { private static final String GRANT_TYPE = "password"; + private final KeyExchanger keyExchanger; private final AuthenticationManager authenticationManager; - public PasswordTokenGranter(AuthenticationManager authenticationManager, - AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { - this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); + public PasswordTokenGranter(KeyExchanger keyExchanger, AuthenticationManager authenticationManager, + AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, + OAuth2RequestFactory requestFactory) { + this(keyExchanger, authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); } - protected PasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices, - ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) { + protected PasswordTokenGranter(KeyExchanger keyExchanger, AuthenticationManager authenticationManager, + AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, + OAuth2RequestFactory requestFactory, String grantType) { super(tokenServices, clientDetailsService, requestFactory, grantType); + this.keyExchanger = keyExchanger; this.authenticationManager = authenticationManager; } @@ -39,7 +45,8 @@ public class PasswordTokenGranter extends AbstractTokenGranter { String username = parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); String password = parameters.remove(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); - Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password); + String decryptPassword = keyExchanger.decrypt(password); + Authentication userAuth = new UsernamePasswordAuthenticationToken(username, decryptPassword); ((AbstractAuthenticationToken) userAuth).setDetails(parameters); try { userAuth = authenticationManager.authenticate(userAuth); -- Gitee From ac617155b914b766118f4b04a0bd7f1b24ee9d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 13 Jan 2021 22:11:03 +0800 Subject: [PATCH 164/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonce/AbstractPasswordTokenGranter.java | 69 +++++++++++++++++++ .../oauth2/nonce/NonceTokenGranter.java | 2 +- .../oauth2/nonce/PasswordTokenGranter.java | 62 +++-------------- .../src/test/java/PasswordTest.java | 4 +- 4 files changed, 83 insertions(+), 54 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java new file mode 100644 index 0000000..501a9f0 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java @@ -0,0 +1,69 @@ +package cn.seqdata.oauth2.nonce; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.lang.Nullable; +import org.springframework.security.authentication.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +/** + * @author jrxian + * @date 2020/12/14 1:56 + */ +public class AbstractPasswordTokenGranter extends AbstractTokenGranter { + private final AuthenticationManager authenticationManager; + + protected AbstractPasswordTokenGranter(AuthenticationManager authenticationManager, + AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, + OAuth2RequestFactory requestFactory, String grantType) { + super(tokenServices, clientDetailsService, requestFactory, grantType); + this.authenticationManager = authenticationManager; + } + + @Override + protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { + Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); + String username = obtainUsername(tokenRequest); + String password = obtainPassword(tokenRequest); + + Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password); + ((AbstractAuthenticationToken) userAuth).setDetails(parameters); + try { + userAuth = authenticationManager.authenticate(userAuth); + } catch(AccountStatusException | BadCredentialsException ex) { + InvalidGrantException exception = new InvalidGrantException(ex.getMessage(), ex); + //给前端输出具体的错误类型,便于前端处理 + //主要解决首次登录/管理员重置密码后必须修改密码才能登录的问题 + Class exClass = ex.getClass(); + String exClassName = StringUtils.replace(exClass.getSimpleName(), "Exception", ""); + exception.addAdditionalInformation("exception", exClassName); + throw exception; + } + if(userAuth == null || !userAuth.isAuthenticated()) { + throw new InvalidGrantException("Could not authenticate user: " + username); + } + + OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); + return new OAuth2Authentication(storedOAuth2Request, userAuth); + } + + @Nullable + protected String obtainUsername(TokenRequest request) { + Map parameters = request.getRequestParameters(); + return parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); + } + + @Nullable + protected String obtainPassword(TokenRequest request) { + Map parameters = request.getRequestParameters(); + return parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); + } +} \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java index 0939855..919631a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceTokenGranter.java @@ -12,7 +12,7 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok * Author: jrxian * Date: 2020-01-26 00:16 */ -public class NonceTokenGranter extends PasswordTokenGranter { +public class NonceTokenGranter extends AbstractPasswordTokenGranter { public NonceTokenGranter(AuthenticationProvider authenticationProvider, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java index 3f788d8..143739c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java @@ -1,69 +1,29 @@ package cn.seqdata.oauth2.nonce; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.authentication.*; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; -import org.springframework.security.oauth2.provider.*; -import org.springframework.security.oauth2.provider.token.AbstractTokenGranter; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.OAuth2RequestFactory; +import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import cn.seqdata.oauth2.policy.KeyExchanger; /** * @author jrxian - * @date 2020/12/14 1:56 + * @date 2021/1/13 22:05 */ -public class PasswordTokenGranter extends AbstractTokenGranter { +public class PasswordTokenGranter extends AbstractPasswordTokenGranter { private static final String GRANT_TYPE = "password"; - private final KeyExchanger keyExchanger; - private final AuthenticationManager authenticationManager; public PasswordTokenGranter(KeyExchanger keyExchanger, AuthenticationManager authenticationManager, - AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, - OAuth2RequestFactory requestFactory) { - this(keyExchanger, authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); - } - - protected PasswordTokenGranter(KeyExchanger keyExchanger, AuthenticationManager authenticationManager, - AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, - OAuth2RequestFactory requestFactory, String grantType) { - super(tokenServices, clientDetailsService, requestFactory, grantType); + AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) { + super(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); this.keyExchanger = keyExchanger; - this.authenticationManager = authenticationManager; } @Override - protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { - Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); - String username = parameters.get(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY); - String password = parameters.remove(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY); - - String decryptPassword = keyExchanger.decrypt(password); - Authentication userAuth = new UsernamePasswordAuthenticationToken(username, decryptPassword); - ((AbstractAuthenticationToken) userAuth).setDetails(parameters); - try { - userAuth = authenticationManager.authenticate(userAuth); - } catch(AccountStatusException | BadCredentialsException ex) { - InvalidGrantException exception = new InvalidGrantException(ex.getMessage(), ex); - //给前端输出具体的错误类型,便于前端处理 - //主要解决首次登录/管理员重置密码后必须修改密码才能登录的问题 - Class exClass = ex.getClass(); - String exClassName = StringUtils.replace(exClass.getSimpleName(), "Exception", ""); - exception.addAdditionalInformation("exception", exClassName); - throw exception; - } - if(userAuth == null || !userAuth.isAuthenticated()) { - throw new InvalidGrantException("Could not authenticate user: " + username); - } - - OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); - return new OAuth2Authentication(storedOAuth2Request, userAuth); + protected String obtainPassword(TokenRequest request) { + return keyExchanger.decrypt(super.obtainPassword(request)); } -} \ No newline at end of file +} diff --git a/seqdata-cloud-authz/src/test/java/PasswordTest.java b/seqdata-cloud-authz/src/test/java/PasswordTest.java index 22156d2..42bc80e 100644 --- a/seqdata-cloud-authz/src/test/java/PasswordTest.java +++ b/seqdata-cloud-authz/src/test/java/PasswordTest.java @@ -8,7 +8,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; public class PasswordTest { public static void main(String[] args) { PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); - String encodedPassword = "{bcrypt}$2a$10$LdxUdtkJAZAj0KAkMAqrP.KwFjBnl8yzPgSpKRWVvQrAHvAl294g2"; - System.err.println(encoder.matches("1", encodedPassword)); + String encodedPassword = "{bcrypt}$2a$10$Ge9xTes4cMaCYYJo8ne4ceQtOXf.R7kxr5Dy1QsvLpxmQlzp.w7oi"; + System.err.println(encoder.matches("12345678", encodedPassword)); } } -- Gitee From c06a579e0739f746a6f41f3af565f0aabd9122af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 13 Jan 2021 22:35:20 +0800 Subject: [PATCH 165/228] =?UTF-8?q?=E7=94=A8=E9=9D=9E=E5=AF=B9=E7=A7=B0?= =?UTF-8?q?=E5=AF=86=E9=92=A5(rsa,md2)=E4=BA=A4=E6=8D=A2=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/PasswordController.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index d93d89d..019c522 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -24,6 +24,8 @@ import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.policy.KeyExchanger; +import cn.seqdata.oauth2.policy.PasswordChecker; +import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; import cn.seqdata.syslog.Syslog; @@ -37,11 +39,13 @@ import cn.seqdata.syslog.Syslog; @RequestMapping("/password") @AllArgsConstructor public class PasswordController { - private final KeyExchanger keyExchanger; - private final PasswordEncoder passwordEncoder; private final AuthenticationManager authenticationManager; private final UserDetailsManager userDetailsManager; private final JpaUserDetailsPasswordService passwordService; + private final KeyExchanger keyExchanger; + private final PasswordPolicy passwordPolicy; + private final PasswordEncoder passwordEncoder; + private final PasswordChecker passwordChecker; private final AccountLockService lockService; private final List nonceHandlers; @@ -83,9 +87,14 @@ public class PasswordController { nonceHandler.check(mobile, nonce); } } - //直接设置新密码 + + //检查密码强度 String decryptPassword = keyExchanger.decrypt(password); - passwordService.updatePassword(username, decryptPassword, null); + passwordChecker.check(username, decryptPassword); + + //设置的新密码先编码,否则会触发密码升级,导致密码立即过期 + String encodedPassword = passwordEncoder.encode(decryptPassword); + passwordService.updatePassword(username, encodedPassword, passwordPolicy.getCredentialsExpireDate()); } @Syslog -- Gitee From 885eaa9040bd575725dd558d7fabc61df04daf4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 14 Jan 2021 11:14:52 +0800 Subject: [PATCH 166/228] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E5=BC=BA=E5=BA=A6=E6=A3=80=E6=9F=A5=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 2 +- .../oauth2/AuthzServerConfiguration.java | 17 +++++++++++++++-- .../oauth2/controller/PasswordController.java | 6 +++--- .../oauth2/nonce/PasswordTokenGranter.java | 2 +- .../src/test/java/PasswordTest.java | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index c5657d5..91aa814 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -32,7 +32,7 @@ import cn.seqdata.oauth2.nonce.NonceAuthenticationProvider; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.nonce.NonceTokenGranter; import cn.seqdata.oauth2.nonce.PasswordTokenGranter; -import cn.seqdata.oauth2.policy.KeyExchanger; +import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 768e5a4..c318073 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -13,9 +13,11 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; -import cn.seqdata.oauth2.policy.KeyExchanger; -import cn.seqdata.oauth2.policy.KeyExchangers; import cn.seqdata.oauth2.policy.PasswordPolicy; +import cn.seqdata.oauth2.policy.checker.*; +import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; +import cn.seqdata.oauth2.policy.exchanger.KeyExchangers; +import cn.seqdata.oauth2.repos.rbac.PasswordHistoryRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.DefaultTokenEnhancer; import cn.seqdata.oauth2.service.DefaultTokenServices; @@ -48,6 +50,17 @@ public class AuthzServerConfiguration { return encoder; } + @Bean + public PasswordChecker passwordChecker(PasswordPolicy policy, PasswordEncoder encoder, + PasswordHistoryRepo historyRepo) { + CompositePasswordChecker passwordChecker = new CompositePasswordChecker(); + passwordChecker.addChecker(new UsernameChecker()); + passwordChecker.addChecker(new TextChecker()); + passwordChecker.addChecker(new StrengthChecker(policy)); + passwordChecker.addChecker(new HistoryChecker(policy, encoder, historyRepo)); + return passwordChecker; + } + @Bean public TokenStore tokenStore(StringRedisTemplate redisTemplate, PasswordPolicy passwordPolicy) { RedisTokenStore tokenStore = new RedisTokenStore(redisTemplate); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 019c522..340cd3a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -23,9 +23,9 @@ import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.oauth2.nonce.NonceHandler; -import cn.seqdata.oauth2.policy.KeyExchanger; -import cn.seqdata.oauth2.policy.PasswordChecker; import cn.seqdata.oauth2.policy.PasswordPolicy; +import cn.seqdata.oauth2.policy.checker.PasswordChecker; +import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; import cn.seqdata.syslog.Syslog; @@ -90,7 +90,7 @@ public class PasswordController { //检查密码强度 String decryptPassword = keyExchanger.decrypt(password); - passwordChecker.check(username, decryptPassword); + passwordChecker.accept(username, decryptPassword); //设置的新密码先编码,否则会触发密码升级,导致密码立即过期 String encodedPassword = passwordEncoder.encode(decryptPassword); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java index 143739c..e2decac 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java @@ -6,7 +6,7 @@ import org.springframework.security.oauth2.provider.OAuth2RequestFactory; import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import cn.seqdata.oauth2.policy.KeyExchanger; +import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; /** * @author jrxian diff --git a/seqdata-cloud-authz/src/test/java/PasswordTest.java b/seqdata-cloud-authz/src/test/java/PasswordTest.java index 42bc80e..aef74ce 100644 --- a/seqdata-cloud-authz/src/test/java/PasswordTest.java +++ b/seqdata-cloud-authz/src/test/java/PasswordTest.java @@ -8,7 +8,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; public class PasswordTest { public static void main(String[] args) { PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); - String encodedPassword = "{bcrypt}$2a$10$Ge9xTes4cMaCYYJo8ne4ceQtOXf.R7kxr5Dy1QsvLpxmQlzp.w7oi"; + String encodedPassword = "{bcrypt}$2a$10$b.EowYVwXK3a6ps2dYxV/esrAQ7vNaLhpz1BaTVj48FkVHNp0Yv/a"; System.err.println(encoder.matches("12345678", encodedPassword)); } } -- Gitee From 25777e7a93ac829597e49012c356a48b6971b37e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Sun, 17 Jan 2021 16:44:54 +0800 Subject: [PATCH 167/228] =?UTF-8?q?=E4=B8=8D=E6=89=93=E5=8C=85=E6=89=80?= =?UTF-8?q?=E6=9C=89application*.yml=EF=BC=8C=E5=85=A8=E9=83=A8=E4=BB=8Ena?= =?UTF-8?q?cos=E4=B8=AD=E5=8F=96=20nacos=E6=89=80=E9=9C=80=E7=9A=84appname?= =?UTF-8?q?=E4=BB=8EDockerfile=E7=9A=84-Dspring.application.name=3Dmyapp?= =?UTF-8?q?=E4=B8=AD=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 ++-- pom.xml | 11 +++++++++++ seqdata-cloud-authc/src/main/docker/Dockerfile | 2 +- .../src/main/resources/bootstrap.yml | 16 ---------------- seqdata-cloud-authz/src/main/docker/Dockerfile | 2 +- .../src/main/resources/application.yml | 16 ++++++++-------- seqdata-cloud-gateway/src/main/docker/Dockerfile | 2 +- 7 files changed, 24 insertions(+), 29 deletions(-) delete mode 100644 seqdata-cloud-authc/src/main/resources/bootstrap.yml diff --git a/docker-compose.yml b/docker-compose.yml index bc427b3..d804082 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,7 +34,7 @@ services: - server.port=8080 - spring.cloud.nacos.config.server-addr=nacos:8848 - spring.cloud.nacos.discovery.server-addr=nacos:8848 - - security.oauth2.resource.token-info-uri=http://authz:8080/oauth/check_token + - security.oauth2.resource.token-info-uri=http://authz/oauth/check_token authz: container_name: authz image: seqdata-cloud-authz @@ -59,4 +59,4 @@ services: - server.port=8080 - spring.cloud.nacos.config.server-addr=nacos:8848 - spring.cloud.nacos.discovery.server-addr=nacos:8848 - - security.oauth2.resource.token-info-uri=http://authz:8080/oauth/check_token + - security.oauth2.resource.token-info-uri=http://authz/oauth/check_token diff --git a/pom.xml b/pom.xml index ce39374..d6c3f6e 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,17 @@ + + + src/main/resources + true + + application*.properties + application*.yaml + application*.yml + + + org.springframework.boot diff --git a/seqdata-cloud-authc/src/main/docker/Dockerfile b/seqdata-cloud-authc/src/main/docker/Dockerfile index e8e9948..61caeee 100644 --- a/seqdata-cloud-authc/src/main/docker/Dockerfile +++ b/seqdata-cloud-authc/src/main/docker/Dockerfile @@ -3,4 +3,4 @@ VOLUME /tmp ADD seqdata-cloud-authc-2.2.1-SNAPSHOT.jar myapp.jar ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone -ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","-Dspring.application.name=authc","/myapp.jar"] \ No newline at end of file diff --git a/seqdata-cloud-authc/src/main/resources/bootstrap.yml b/seqdata-cloud-authc/src/main/resources/bootstrap.yml deleted file mode 100644 index 8636e73..0000000 --- a/seqdata-cloud-authc/src/main/resources/bootstrap.yml +++ /dev/null @@ -1,16 +0,0 @@ -spring: - cloud: - nacos: - discovery: - server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} - cluster-name: ${NACOS_CLUSTER_NAME:DEFAULT} - namespace: ${NACOS_NAMESPACE:public} - group: ${NACOS_GROUP:DEFAULT_GROUP} - config: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - cluster-name: ${spring.cloud.nacos.discovery.cluster-name} - namespace: ${spring.cloud.nacos.discovery.namespace} - group: ${spring.cloud.nacos.discovery.group} - file-extension: ${NACOS_FILE_EXT:yml} -server: - port: 0 \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/docker/Dockerfile b/seqdata-cloud-authz/src/main/docker/Dockerfile index a46d314..e99c634 100644 --- a/seqdata-cloud-authz/src/main/docker/Dockerfile +++ b/seqdata-cloud-authz/src/main/docker/Dockerfile @@ -3,4 +3,4 @@ VOLUME /tmp ADD seqdata-cloud-authz-2.2.1-SNAPSHOT.jar myapp.jar ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone -ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","-Dspring.application.name=authz","/myapp.jar"] \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index f8e981b..84d9574 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -13,14 +13,14 @@ spring: hibernate: cache: use_second_level_cache: false - # use_query_cache: true - # region: - # factory_class: org.hibernate.cache.jcache.JCacheRegionFactory - # javax: - # cache: - # missing_cache_strategy: create - # cache: - # type: simple + use_query_cache: true + region: + factory_class: org.hibernate.cache.jcache.JCacheRegionFactory + javax: + cache: + missing_cache_strategy: create + cache: + type: simple data: jdbc: repositories: diff --git a/seqdata-cloud-gateway/src/main/docker/Dockerfile b/seqdata-cloud-gateway/src/main/docker/Dockerfile index f6a5758..c3f5bb9 100644 --- a/seqdata-cloud-gateway/src/main/docker/Dockerfile +++ b/seqdata-cloud-gateway/src/main/docker/Dockerfile @@ -5,4 +5,4 @@ VOLUME /tmp ADD seqdata-cloud-gateway-2.2.1-SNAPSHOT.jar myapp.jar ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo '$TZ' > /etc/timezone -ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/myapp.jar"] \ No newline at end of file +ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","-Dspring.application.name=gateway","/myapp.jar"] \ No newline at end of file -- Gitee From 0b233fd2078ccc154f49a6ed45cb3a41a532826a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 18 Jan 2021 00:13:02 +0800 Subject: [PATCH 168/228] =?UTF-8?q?=E4=BB=8EClientDetails=E8=8E=B7?= =?UTF-8?q?=E5=8F=96token=E6=9C=89=E6=95=88=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 21 +++++---- .../oauth2/service/DefaultTokenServices.java | 45 ++++++++++++++++--- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/pom.xml b/pom.xml index d6c3f6e..11feec4 100644 --- a/pom.xml +++ b/pom.xml @@ -66,18 +66,17 @@ - - - src/main/resources - true - - application*.properties - application*.yaml - application*.yml - - - + + maven-jar-plugin + + + application*.properties + application*.yaml + application*.yml + + + org.springframework.boot spring-boot-maven-plugin diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java index e501ce8..83d68d5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java @@ -4,15 +4,14 @@ import java.util.Date; import java.util.Objects; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.RandomStringUtils; import org.joda.time.DateTimeConstants; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.common.*; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; -import org.springframework.security.oauth2.provider.OAuth2Authentication; -import org.springframework.security.oauth2.provider.OAuth2Request; -import org.springframework.security.oauth2.provider.TokenRequest; +import org.springframework.security.oauth2.provider.*; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; @@ -25,6 +24,7 @@ import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class DefaultTokenServices implements AuthorizationServerTokenServices { + private final ClientDetailsService clientDetailsService; private boolean supportRefreshToken; private int accessTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_MINUTE; private int refreshTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_DAY; @@ -97,7 +97,7 @@ public class DefaultTokenServices implements AuthorizationServerTokenServices { private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(createTokenValue()); - token.setExpiration(expiration(accessTokenValiditySeconds)); + token.setExpiration(expiration(accessTokenValiditySeconds(authentication))); token.setRefreshToken(refreshToken); OAuth2Request auth2Request = authentication.getOAuth2Request(); token.setScope(auth2Request.getScope()); @@ -107,17 +107,52 @@ public class DefaultTokenServices implements AuthorizationServerTokenServices { private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) { if(!supportRefreshToken) return null; - return new DefaultExpiringOAuth2RefreshToken(createTokenValue(), expiration(refreshTokenValiditySeconds)); + return new DefaultExpiringOAuth2RefreshToken(createTokenValue(), expiration(refreshTokenValiditySeconds(authentication))); } private String createTokenValue() { return String.format("%08x", (int) (System.currentTimeMillis() / 1000)) + RandomStringUtils.randomAlphanumeric(8); } + private String clientId(OAuth2Authentication authentication) { + OAuth2Request request = authentication.getOAuth2Request(); + return request.getClientId(); + } + + /** + * 获取accessToken有效时间,先查client配置,没有的话用默认时间代替 + */ + private int accessTokenValiditySeconds(OAuth2Authentication authentication) { + try { + ClientDetails client = clientDetailsService.loadClientByClientId(clientId(authentication)); + return ObjectUtils.defaultIfNull(client.getAccessTokenValiditySeconds(), accessTokenValiditySeconds); + } catch(ClientRegistrationException ex) { + return accessTokenValiditySeconds; + } + } + + /** + * 获取refreshToken有效时间,先查client配置,没有的话用默认时间代替 + */ + private int refreshTokenValiditySeconds(OAuth2Authentication authentication) { + try { + ClientDetails client = clientDetailsService.loadClientByClientId(clientId(authentication)); + return ObjectUtils.defaultIfNull(client.getRefreshTokenValiditySeconds(), refreshTokenValiditySeconds); + } catch(ClientRegistrationException ex) { + return refreshTokenValiditySeconds; + } + } + + /** + * 当前系统时间加上秒数,得到过期时间 + */ private Date expiration(int validitySeconds) { return new Date(System.currentTimeMillis() + (long) validitySeconds * DateTimeConstants.MILLIS_PER_SECOND); } + /** + * 判断refreshToken是否过期 + */ private boolean isExpired(OAuth2RefreshToken refreshToken) { if(refreshToken instanceof ExpiringOAuth2RefreshToken) { ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken; -- Gitee From 75ea20c481accd5c92ff67d73fb6906816fc461f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Mon, 18 Jan 2021 00:29:08 +0800 Subject: [PATCH 169/228] =?UTF-8?q?=E4=BB=8EClientDetails=E8=8E=B7?= =?UTF-8?q?=E5=8F=96token=E6=9C=89=E6=95=88=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/AuthzServerConfiguration.java | 6 ++++-- .../cn/seqdata/oauth2/service/DefaultTokenServices.java | 4 +--- .../cn/seqdata/gateway/filter/authc/AnonymousService.java | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index c318073..0741c4d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -9,6 +9,7 @@ import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; @@ -74,7 +75,8 @@ public class AuthzServerConfiguration { } @Bean - public AuthorizationServerTokenServices tokenServices(TokenStore tokenStore, TokenEnhancer tokenEnhancer) { - return new DefaultTokenServices(tokenStore, tokenEnhancer); + public AuthorizationServerTokenServices tokenServices(ClientDetailsService clientDetailsService, + TokenStore tokenStore, TokenEnhancer tokenEnhancer) { + return new DefaultTokenServices(clientDetailsService, tokenStore, tokenEnhancer); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java index 83d68d5..51c2ca6 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java @@ -15,20 +15,18 @@ import org.springframework.security.oauth2.provider.*; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; -import org.springframework.stereotype.Service; /** * @author jrxian * @date 2020-11-23 16:49 */ -@Service @RequiredArgsConstructor public class DefaultTokenServices implements AuthorizationServerTokenServices { - private final ClientDetailsService clientDetailsService; private boolean supportRefreshToken; private int accessTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_MINUTE; private int refreshTokenValiditySeconds = 30 * DateTimeConstants.SECONDS_PER_DAY; + private final ClientDetailsService clientDetailsService; private final TokenStore tokenStore; private final TokenEnhancer tokenEnhancer; diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousService.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousService.java index 39ac1e3..ef346ee 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousService.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AnonymousService.java @@ -17,6 +17,7 @@ import cn.seqdata.oauth2.client.entity.NamedObject; /** * @author jrxian * @date 2020-11-12 00:26 + * 从Feign中获取anonymous角色的url授权 */ @Service @AllArgsConstructor -- Gitee From 3929414d0a1ced286da0e70b5fd0d3c8dd05d79b Mon Sep 17 00:00:00 2001 From: zl1995 Date: Thu, 21 Jan 2021 14:24:11 +0800 Subject: [PATCH 170/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=AC=E4=BC=97?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 84d9574..6438792 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -56,10 +56,25 @@ spring: authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/{action}/oauth/code/{registrationId}" scope: snsapi_login + weixin: + provider: weixin + client-id: wx4c5c26c776f67a4a + client-secret: 3823127d7e67cbbb5ed460b71607db25 + client-name: 微信公众号 + client-authentication-method: post + authorization-grant-type: authorization_code + redirect-uri: "{baseUrl}/{action}/oauth/code/{registrationId}" + scope: snsapi_userinfo provider: wechat: authorization-uri: https://open.weixin.qq.com/connect/qrconnect token-uri: https://api.weixin.qq.com/sns/oauth2/access_token user-info-uri: https://api.weixin.qq.com/sns/userinfo user-info-authentication-method: query + user-name-attribute: openid + weixin: + authorization-uri: https://open.weixin.qq.com/connect/oauth2/authorize + token-uri: https://api.weixin.qq.com/sns/oauth2/access_token + user-info-uri: https://api.weixin.qq.com/sns/userinfo + user-info-authentication-method: query user-name-attribute: openid \ No newline at end of file -- Gitee From cb92c70e5e423ee6f22995696f752111dd7e328e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 27 Jan 2021 10:19:39 +0800 Subject: [PATCH 171/228] =?UTF-8?q?=E8=BF=87=E6=BB=A4=E6=95=8F=E6=84=9F?= =?UTF-8?q?=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gateway/filter/logging/LoggingGlobalFilter.java | 11 +++++++++-- .../cn/seqdata/gateway/filter/logging/UrlRecord.java | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java index 47f0d85..d099a20 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -6,6 +6,7 @@ import java.util.StringJoiner; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; @@ -37,6 +38,8 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { UrlRecord.UrlRecordBuilder builder = UrlRecord.builder(); + builder.logId(exchange.getLogPrefix()); + buildRequst(builder, exchange.getRequest()); builder.start(System.currentTimeMillis()); @@ -52,7 +55,7 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { @Override public int getOrder() { - return -90; + return -100; } private void buildRequst(UrlRecord.UrlRecordBuilder builder, ServerHttpRequest request) { @@ -93,7 +96,11 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { queryParams.forEach((key, vals) -> { if(Objects.nonNull(vals)) { - vals.forEach(val -> joiner.add(key + "=" + val)); + if(StringUtils.containsIgnoreCase(key, "password")) { + vals.forEach(val -> joiner.add(key + "=*******")); + } else { + vals.forEach(val -> joiner.add(key + "=" + val)); + } } else { joiner.add(key); } diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java index 2fd9d30..8bedfd7 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java @@ -17,6 +17,8 @@ import lombok.NoArgsConstructor; public class UrlRecord implements Serializable { private static final long serialVersionUID = 1L; + private String logId; + /** * 开始时间 */ -- Gitee From a06b6c3af1550d0ac9e13dd6bd1cfa0fbf67334f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 27 Jan 2021 10:20:43 +0800 Subject: [PATCH 172/228] =?UTF-8?q?=E4=BA=A4=E6=8D=A2nacos=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=B8=ADconfig=E5=92=8Cdiscovery=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 15 --------------- .../src/main/resources/bootstrap.yml | 16 ++++++++-------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 6438792..84d9574 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -56,25 +56,10 @@ spring: authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/{action}/oauth/code/{registrationId}" scope: snsapi_login - weixin: - provider: weixin - client-id: wx4c5c26c776f67a4a - client-secret: 3823127d7e67cbbb5ed460b71607db25 - client-name: 微信公众号 - client-authentication-method: post - authorization-grant-type: authorization_code - redirect-uri: "{baseUrl}/{action}/oauth/code/{registrationId}" - scope: snsapi_userinfo provider: wechat: authorization-uri: https://open.weixin.qq.com/connect/qrconnect token-uri: https://api.weixin.qq.com/sns/oauth2/access_token user-info-uri: https://api.weixin.qq.com/sns/userinfo user-info-authentication-method: query - user-name-attribute: openid - weixin: - authorization-uri: https://open.weixin.qq.com/connect/oauth2/authorize - token-uri: https://api.weixin.qq.com/sns/oauth2/access_token - user-info-uri: https://api.weixin.qq.com/sns/userinfo - user-info-authentication-method: query user-name-attribute: openid \ No newline at end of file diff --git a/seqdata-cloud-authz/src/main/resources/bootstrap.yml b/seqdata-cloud-authz/src/main/resources/bootstrap.yml index 87b2828..a7f432d 100644 --- a/seqdata-cloud-authz/src/main/resources/bootstrap.yml +++ b/seqdata-cloud-authz/src/main/resources/bootstrap.yml @@ -1,17 +1,17 @@ spring: cloud: nacos: - discovery: + config: server-addr: ${NACOS_SERVER_IP:nacos.seqdata.cn}:${NACOS_SERVER_PORT:8848} cluster-name: ${NACOS_CLUSTER_NAME:DEFAULT} namespace: ${NACOS_NAMESPACE:public} group: ${NACOS_GROUP:DEFAULT_GROUP} - config: - server-addr: ${spring.cloud.nacos.discovery.server-addr} - cluster-name: ${spring.cloud.nacos.discovery.cluster-name} - namespace: ${spring.cloud.nacos.discovery.namespace} - group: ${spring.cloud.nacos.discovery.group} file-extension: ${NACOS_FILE_EXT:yml} shared-configs: - - group: ${spring.cloud.nacos.discovery.group} - data_id: common.${spring.cloud.nacos.config.file-extension} \ No newline at end of file + - group: ${spring.cloud.nacos.config.group} + data_id: common.${spring.cloud.nacos.config.file-extension} + discovery: + server-addr: ${spring.cloud.nacos.config.server-addr} + cluster-name: ${spring.cloud.nacos.config.cluster-name} + namespace: ${spring.cloud.nacos.config.namespace} + group: ${spring.cloud.nacos.config.group} \ No newline at end of file -- Gitee From 27d614f399e5b2ca700005ae5d00448b4e536d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 27 Jan 2021 10:28:13 +0800 Subject: [PATCH 173/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/src/main/resources/application.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 84d9574..68d6e32 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -56,6 +56,15 @@ spring: authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/{action}/oauth/code/{registrationId}" scope: snsapi_login + weixin: + provider: wechat + client-id: wx4c5c26c776f67a4a + client-secret: 3823127d7e67cbbb5ed460b71607db25 + client-name: 微信公众号 + client-authentication-method: post + authorization-grant-type: authorization_code + redirect-uri: "{baseUrl}/{action}/oauth/code/{registrationId}" + scope: snsapi_userinfo provider: wechat: authorization-uri: https://open.weixin.qq.com/connect/qrconnect -- Gitee From 259a4724afd94278123e32bd367a43a604bd20c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 27 Jan 2021 13:22:25 +0800 Subject: [PATCH 174/228] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/logging/LoggingGlobalFilter.java | 33 ++++++++++++------- .../gateway/filter/logging/UrlRecord.java | 2 +- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java index d099a20..edad918 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -1,12 +1,14 @@ package cn.seqdata.gateway.filter.logging; import java.net.InetSocketAddress; +import java.util.Map; import java.util.Objects; import java.util.StringJoiner; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; @@ -30,27 +32,32 @@ import cn.seqdata.gateway.filter.authc.DefaultBearerTokenResolver; @Slf4j @AllArgsConstructor public class LoggingGlobalFilter implements GlobalFilter, Ordered { + private static final String REQUEST_TIME = LoggingGlobalFilter.class.getSimpleName() + ".requestTime"; + private final BearerTokenResolver tokenResolver = new DefaultBearerTokenResolver(); private final LogRecorder logRecorder; private final ResourceServerTokenServices tokenServices; @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - UrlRecord.UrlRecordBuilder builder = UrlRecord.builder(); - - builder.logId(exchange.getLogPrefix()); + Map attributes = exchange.getAttributes(); + attributes.put(REQUEST_TIME, System.currentTimeMillis()); + return chain.filter(exchange) + .then(Mono.defer(() -> { + UrlRecord.UrlRecordBuilder builder = UrlRecord.builder(); - buildRequst(builder, exchange.getRequest()); + Long start = exchange.getAttribute(REQUEST_TIME); + long end = System.currentTimeMillis(); + builder.start(ObjectUtils.defaultIfNull(start, end)); + builder.end(end); - builder.start(System.currentTimeMillis()); - Mono returnObj = chain.filter(exchange); - builder.end(System.currentTimeMillis()); + buildRequst(builder, exchange.getRequest()); + buildResponse(builder, exchange.getResponse()); - buildResponse(builder, exchange.getResponse()); + logRecorder.accept(builder.build()); - logRecorder.accept(builder.build()); - - return returnObj; + return Mono.empty(); + })); } @Override @@ -59,6 +66,8 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { } private void buildRequst(UrlRecord.UrlRecordBuilder builder, ServerHttpRequest request) { + builder.requestId(request.getId()); + RequestPath requestPath = request.getPath(); builder.method(request.getMethodValue()) .path(requestPath.value()) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java index 8bedfd7..e26e970 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/UrlRecord.java @@ -17,7 +17,7 @@ import lombok.NoArgsConstructor; public class UrlRecord implements Serializable { private static final long serialVersionUID = 1L; - private String logId; + private String requestId; /** * 开始时间 -- Gitee From 73e1aa013927a363f5ee50aa7c4dd3ce833b2d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 27 Jan 2021 14:52:19 +0800 Subject: [PATCH 175/228] =?UTF-8?q?=E8=BF=87=E6=BB=A4=E6=8E=89=E5=A4=A7?= =?UTF-8?q?=E9=87=8F=E6=97=A0=E7=94=A8=E5=86=85=E9=83=A8=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/BizlogConfiguration.java | 48 +++++++++++-------- .../src/main/resources/application.yml | 5 +- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index ac887a2..20094b9 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -8,11 +8,13 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.event.AbstractAuthenticationEvent; import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.authentication.event.LogoutSuccessEvent; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.provider.OAuth2Authentication; import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.syslog.*; @@ -27,13 +29,6 @@ public class BizlogConfiguration { @Value("${spring.application.name}") private String appName; - // @Bean - // public SyslogConsumer jdbcSyslogConsumer(JdbcTemplate jdbcTemplate) { - // JdbcSyslogConsumer syslogConsumer = new JdbcSyslogConsumer(); - // syslogConsumer.setJdbcTemplate(jdbcTemplate); - // return syslogConsumer; - // } - @Bean public MessageConverter messageConverter(ObjectMapper objectMapper) { return new Jackson2JsonMessageConverter(objectMapper); @@ -46,23 +41,24 @@ public class BizlogConfiguration { } @Bean - public ApplicationListener syslogAuthcSuccessListener(CompositeSyslogConsumer consumer) { + public ApplicationListener syslogAuthSuccessListener(CompositeSyslogConsumer consumer) { return event -> { - SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) - .eventId("login") - .eventName("登录"); + if(isClientOnly(event.getAuthentication())) return; + + SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event); consumer.accept(builder.build()); }; } @Bean - public ApplicationListener syslogAuthcFailureListener(CompositeSyslogConsumer consumer) { + public ApplicationListener syslogAuthFailureListener(CompositeSyslogConsumer consumer) { return event -> { + if(isClientOnly(event.getAuthentication())) return; + AuthenticationException exception = event.getException(); Class exceptionClass = exception.getClass(); - SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) - .eventId("login") - .eventName("登录") + + SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event) .errorCode(exceptionClass.getSimpleName()); consumer.accept(builder.build()); }; @@ -71,18 +67,28 @@ public class BizlogConfiguration { @Bean public ApplicationListener syslogLogoutSuccessListener(CompositeSyslogConsumer consumer) { return event -> { - SyslogRecord.SyslogRecordBuilder builder = logBuilder(event.getAuthentication()) - .eventId("logout") - .eventName("注销"); + if(isClientOnly(event.getAuthentication())) return; + + SyslogRecord.SyslogRecordBuilder builder = logBuilder("logout", "注销", event); consumer.accept(builder.build()); }; } - private SyslogRecord.SyslogRecordBuilder logBuilder(Authentication authentication) { + private boolean isClientOnly(Authentication authentication) { + if(authentication instanceof OAuth2Authentication) { + return ((OAuth2Authentication) authentication).isClientOnly(); + } else { + return false; + } + } + + private SyslogRecord.SyslogRecordBuilder logBuilder(String eventId, String eventName, AbstractAuthenticationEvent event) { return SyslogRecord.builder() - .eventTime(System.currentTimeMillis()) .projectId(appName) + .eventId(eventId) + .eventName(eventName) + .eventTime(event.getTimestamp()) .sourceIp(DefaultSyslogResolver.remoteAddr()) - .username(DefaultSyslogResolver.username(authentication)); + .username(DefaultSyslogResolver.username(event.getAuthentication())); } } diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 68d6e32..adcb59f 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -38,6 +38,7 @@ spring: password: seq@2015 template: exchange: syslog + routing-key: syslog.biz.${spring.application.name} security: oauth2: client: @@ -71,4 +72,6 @@ spring: token-uri: https://api.weixin.qq.com/sns/oauth2/access_token user-info-uri: https://api.weixin.qq.com/sns/userinfo user-info-authentication-method: query - user-name-attribute: openid \ No newline at end of file + user-name-attribute: openid +server: + port: 0 \ No newline at end of file -- Gitee From d35de76f52bdbf9c48e251c2de774d348fac5e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 27 Jan 2021 17:49:05 +0800 Subject: [PATCH 176/228] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E7=9A=84=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../filter/logging/LoggingGlobalFilter.java | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java index edad918..33752de 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -11,7 +11,9 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.filter.headers.XForwardedHeadersFilter; import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.RequestPath; import org.springframework.http.server.reactive.ServerHttpRequest; @@ -51,7 +53,7 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { builder.start(ObjectUtils.defaultIfNull(start, end)); builder.end(end); - buildRequst(builder, exchange.getRequest()); + buildRequest(builder, exchange.getRequest()); buildResponse(builder, exchange.getResponse()); logRecorder.accept(builder.build()); @@ -65,30 +67,24 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { return -100; } - private void buildRequst(UrlRecord.UrlRecordBuilder builder, ServerHttpRequest request) { + private void buildRequest(UrlRecord.UrlRecordBuilder builder, ServerHttpRequest request) { builder.requestId(request.getId()); RequestPath requestPath = request.getPath(); builder.method(request.getMethodValue()) + .host(remoteHost(request)) + .port(remotePort(request)) .path(requestPath.value()) .queryParams(queryParams(request.getQueryParams())); - InetSocketAddress remoteAddress = request.getRemoteAddress(); - if(Objects.nonNull(remoteAddress)) { - builder.host(remoteAddress.getHostString()) - .port(remoteAddress.getPort()); - } - String accessToken = tokenResolver.resolve(request); if(Objects.nonNull(accessToken)) { builder.token(accessToken); - try { OAuth2Authentication authentication = tokenServices.loadAuthentication(accessToken); Object principal = authentication.getPrincipal(); builder.principal(String.valueOf(principal)); - } catch(RuntimeException ex) { - log.warn(ex.getMessage()); + } catch(RuntimeException ignored) { } } } @@ -117,4 +113,37 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { return joiner.toString(); } + + private String remoteHost(ServerHttpRequest request) { + HttpHeaders headers = request.getHeaders(); + String xForwardedFor = headers.getFirst(XForwardedHeadersFilter.X_FORWARDED_FOR_HEADER); + String[] strings = StringUtils.split(xForwardedFor, ','); + if(null != strings && strings.length >= 1) { + return StringUtils.trim(strings[0]); + } + + InetSocketAddress remote = request.getRemoteAddress(); + if(Objects.nonNull(remote)) { + return remote.getHostString(); + } + + return null; + } + + private int remotePort(ServerHttpRequest request) { + HttpHeaders headers = request.getHeaders(); + String xForwardedPort = headers.getFirst(XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER); + if(null != xForwardedPort) { + try { + return Integer.parseInt(xForwardedPort); + } catch(NumberFormatException ex) { + InetSocketAddress remote = request.getRemoteAddress(); + if(Objects.nonNull(remote)) { + return remote.getPort(); + } + } + } + + return 0; + } } -- Gitee From 27d4c5ba10fd6b53a6602441c6b320069c246534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 28 Jan 2021 16:17:13 +0800 Subject: [PATCH 177/228] =?UTF-8?q?=E5=8D=95=E7=8B=AC=E5=89=A5=E7=A6=BBcry?= =?UTF-8?q?pto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 2 +- .../oauth2/AuthzServerConfiguration.java | 23 +++++++++++++++---- .../oauth2/controller/PasswordController.java | 2 +- .../oauth2/nonce/PasswordTokenGranter.java | 13 +++++++++-- .../src/main/resources/application.yml | 8 ++++--- .../filter/logging/LoggingGlobalFilter.java | 2 +- 6 files changed, 38 insertions(+), 12 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index 91aa814..db54821 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -28,11 +28,11 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; +import cn.seqdata.crypto.exchanger.KeyExchanger; import cn.seqdata.oauth2.nonce.NonceAuthenticationProvider; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.nonce.NonceTokenGranter; import cn.seqdata.oauth2.nonce.PasswordTokenGranter; -import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 0741c4d..619d2a2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -2,9 +2,11 @@ package cn.seqdata.oauth2; import lombok.AllArgsConstructor; +import org.apache.commons.lang3.BooleanUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; @@ -14,10 +16,10 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; +import cn.seqdata.crypto.exchanger.KeyExchanger; +import cn.seqdata.crypto.exchanger.KeyExchangers; import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.policy.checker.*; -import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; -import cn.seqdata.oauth2.policy.exchanger.KeyExchangers; import cn.seqdata.oauth2.repos.rbac.PasswordHistoryRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.DefaultTokenEnhancer; @@ -34,10 +36,23 @@ import cn.seqdata.oauth2.service.UserService; public class AuthzServerConfiguration { @Bean - public KeyExchanger keyExchanger(PasswordPolicy policy) { + public KeyExchanger keyExchanger(PasswordPolicy policy, StringRedisTemplate redisTemplate) { KeyExchangers keyExchangeMethod = policy.getKeyExchangeMethod(); KeyExchanger keyExchanger = keyExchangeMethod.supplier.get(); - keyExchanger.genKeyPair(); + + final String PRIVATE_KEY = "exchanger.privateKey"; + final String PUBLIC_KEY = "exchanger.publicKey"; + ValueOperations opsForValue = redisTemplate.opsForValue(); + + if(BooleanUtils.isTrue(redisTemplate.hasKey(PRIVATE_KEY))) { + keyExchanger.setPrivateKey(opsForValue.get(PRIVATE_KEY)); + keyExchanger.setPublicKey(opsForValue.get(PUBLIC_KEY)); + } else { + keyExchanger.genKeyPair(); + opsForValue.set(PRIVATE_KEY, keyExchanger.getPrivateKey()); + opsForValue.set(PUBLIC_KEY, keyExchanger.getPublicKey()); + } + return keyExchanger; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index 340cd3a..d82d7ec 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -22,10 +22,10 @@ import org.springframework.security.provisioning.UserDetailsManager; import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; +import cn.seqdata.crypto.exchanger.KeyExchanger; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.policy.checker.PasswordChecker; -import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; import cn.seqdata.syslog.Syslog; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java index e2decac..4dd2be6 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/PasswordTokenGranter.java @@ -1,17 +1,21 @@ package cn.seqdata.oauth2.nonce; +import lombok.extern.slf4j.Slf4j; + import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException; import org.springframework.security.oauth2.provider.ClientDetailsService; import org.springframework.security.oauth2.provider.OAuth2RequestFactory; import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; -import cn.seqdata.oauth2.policy.exchanger.KeyExchanger; +import cn.seqdata.crypto.exchanger.KeyExchanger; /** * @author jrxian * @date 2021/1/13 22:05 */ +@Slf4j public class PasswordTokenGranter extends AbstractPasswordTokenGranter { private static final String GRANT_TYPE = "password"; private final KeyExchanger keyExchanger; @@ -24,6 +28,11 @@ public class PasswordTokenGranter extends AbstractPasswordTokenGranter { @Override protected String obtainPassword(TokenRequest request) { - return keyExchanger.decrypt(super.obtainPassword(request)); + try { + return keyExchanger.decrypt(super.obtainPassword(request)); + } catch(RuntimeException ex) { + log.debug(ex.getMessage()); + throw new BadClientCredentialsException(); + } } } diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index adcb59f..8408b6e 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -21,6 +21,10 @@ spring: missing_cache_strategy: create cache: type: simple + mvc: + hiddenmethod: + filter: + enabled: true data: jdbc: repositories: @@ -72,6 +76,4 @@ spring: token-uri: https://api.weixin.qq.com/sns/oauth2/access_token user-info-uri: https://api.weixin.qq.com/sns/userinfo user-info-authentication-method: query - user-name-attribute: openid -server: - port: 0 \ No newline at end of file + user-name-attribute: openid \ No newline at end of file diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java index 33752de..25c1439 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/logging/LoggingGlobalFilter.java @@ -133,7 +133,7 @@ public class LoggingGlobalFilter implements GlobalFilter, Ordered { private int remotePort(ServerHttpRequest request) { HttpHeaders headers = request.getHeaders(); String xForwardedPort = headers.getFirst(XForwardedHeadersFilter.X_FORWARDED_PORT_HEADER); - if(null != xForwardedPort) { + if(StringUtils.isNotBlank(xForwardedPort)) { try { return Integer.parseInt(xForwardedPort); } catch(NumberFormatException ex) { -- Gitee From bbfbff0a5e9a64ef4d3f40cb7f612d388344d2b1 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 29 Jan 2021 10:07:36 +0800 Subject: [PATCH 178/228] =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index e37efdb..71f2101 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -92,15 +92,14 @@ public class ConnectController { @ApiOperation("通过密码绑定") @GetMapping(value = "/password") public void bindPassword(String username, String password, Principal principal) { - userService.loadUser(OAuth2Constants.PASSWORD, username) - .ifPresent(user -> { - if(!passwordEncoder.matches(password, user.getPassword())) { - throw new BadCredentialsException("用户名或密码不正确"); - } - userService.bindUser(Objects.requireNonNull(user.getId()), - SecurityUtils.grantType(principal), - SecurityUtils.username(principal)); - }); + UserAccount user = userService.loadUser(OAuth2Constants.PASSWORD, username) + .orElseThrow(() -> new BadCredentialsException("用户名不存在")); + if(!passwordEncoder.matches(password, user.getPassword())) { + throw new BadCredentialsException("用户名或密码不正确"); + } + userService.bindUser(Objects.requireNonNull(user.getId()), + SecurityUtils.grantType(principal), + SecurityUtils.username(principal)); } @Syslog -- Gitee From 3c372214beb8bf46e4c43e387b86691beea72cc4 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 29 Jan 2021 10:07:36 +0800 Subject: [PATCH 179/228] =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/ConnectController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 71f2101..ec7ae52 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -93,10 +93,12 @@ public class ConnectController { @GetMapping(value = "/password") public void bindPassword(String username, String password, Principal principal) { UserAccount user = userService.loadUser(OAuth2Constants.PASSWORD, username) - .orElseThrow(() -> new BadCredentialsException("用户名不存在")); + .orElseThrow(() -> new UsernameNotFoundException("用户名不存在")); + if(!passwordEncoder.matches(password, user.getPassword())) { throw new BadCredentialsException("用户名或密码不正确"); } + userService.bindUser(Objects.requireNonNull(user.getId()), SecurityUtils.grantType(principal), SecurityUtils.username(principal)); -- Gitee From b7329c0baf784ee8cbdb0f8d0eee78336a95dbdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 2 Mar 2021 11:13:40 +0800 Subject: [PATCH 180/228] =?UTF-8?q?=E6=9B=B4=E6=96=B0syslog=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0priority=EF=BC=8C=E5=88=A0=E9=99=A4principal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 38 ++++++++++++++++++- .../oauth2/AccountLockConfiguration.java | 6 ++- seqdata-cloud-gateway/pom.xml | 18 +++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 11feec4..5704270 100644 --- a/pom.xml +++ b/pom.xml @@ -87,16 +87,48 @@ 1.2.2 + docker-build install build + + docker-tag + deploy + + tag + + + ${project.artifactId} + + swr.cn-north-1.myhuaweicloud.com/seqdata/${project.artifactId} + + + + + docker-push + deploy + + push + + + + swr.cn-north-1.myhuaweicloud.com/seqdata/${project.artifactId} + + + ${project.artifactId} - http://docker.seqdata.cn:2375 - src/main/docker + openjdk:8-jdk-alpine + / + + ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + echo 'Asia/Shanghai' > /etc/timezone + + ["java","-jar","/${project.build.finalName}.jar"] + / @@ -104,6 +136,8 @@ ${project.build.finalName}.jar + + http://docker.seqdata.cn:2375 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java index 34b3b61..3d42277 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java @@ -13,6 +13,7 @@ import org.springframework.security.authentication.event.AuthenticationFailureBa import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.core.Authentication; +import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.service.AccountLockService; /** @@ -24,6 +25,7 @@ import cn.seqdata.oauth2.service.AccountLockService; @lombok.AllArgsConstructor public class AccountLockConfiguration { private final StringRedisTemplate redisTemplate; + private final PasswordPolicy passwordPolicy; private final AccountLockService lockService; @Bean @@ -44,8 +46,8 @@ public class AccountLockConfiguration { String lockName = lockName(username); ValueOperations opsForValue = redisTemplate.opsForValue(); Long hits = opsForValue.increment(lockName); - if(Objects.nonNull(hits) && hits > 3) { - redisTemplate.expire(lockName, 5, TimeUnit.MINUTES); + if(Objects.nonNull(hits) && hits > passwordPolicy.lockHits) { + redisTemplate.expire(lockName, passwordPolicy.lockExpire, TimeUnit.SECONDS); DateTime occur = DateTime.now(); lockService.lock(username, occur.plusMinutes(5)); } diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index bf8ff2b..46825c1 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -45,4 +45,22 @@ ${project.version} + + + + com.spotify + docker-maven-plugin + 1.2.2 + + + sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories + apk add --update ttf-dejavu fontconfig + rm -rf /var/cache/apk/* + ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + echo 'Asia/Shanghai' > /etc/timezone + + + + + -- Gitee From d472669dbc751a8b6d2bef2d969d9a26d4c903d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Tue, 2 Mar 2021 12:21:56 +0800 Subject: [PATCH 181/228] =?UTF-8?q?=E6=9B=B4=E6=96=B0syslog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/BizlogConfiguration.java | 1 + seqdata-cloud-authz/src/main/resources/application.yml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index 20094b9..d7ca69d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -88,6 +88,7 @@ public class BizlogConfiguration { .eventId(eventId) .eventName(eventName) .eventTime(event.getTimestamp()) + .forwardIp(DefaultSyslogResolver.forwardAddr()) .sourceIp(DefaultSyslogResolver.remoteAddr()) .username(DefaultSyslogResolver.username(event.getAuthentication())); } diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index 8408b6e..f092c41 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -1,3 +1,12 @@ +logging: + level: + root: info + cn.seqdata: debug + com.netflix: warn + com.alibaba.nacos: warn + org.springframework: warn + io.swagger: error + springfox.documentation: error spring: application: name: authz -- Gitee From 743db8bb86bee29ae5b078e8f8dfe04fa81eca6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Wed, 17 Mar 2021 18:46:11 +0800 Subject: [PATCH 182/228] =?UTF-8?q?=E8=A7=A3=E5=86=B3javax.transaction.Tra?= =?UTF-8?q?nsactional=E5=AF=BC=E8=87=B4=E7=9A=84=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- seqdata-cloud-authz/pom.xml | 215 +++++++++--------- .../oauth2/controller/ConnectController.java | 4 +- 2 files changed, 109 insertions(+), 110 deletions(-) diff --git a/seqdata-cloud-authz/pom.xml b/seqdata-cloud-authz/pom.xml index 08e867c..233259a 100644 --- a/seqdata-cloud-authz/pom.xml +++ b/seqdata-cloud-authz/pom.xml @@ -1,118 +1,117 @@ - 4.0.0 - - cn.seqdata.cloud - seqdata-cloud-parent - 2.2.1-SNAPSHOT - - seqdata-cloud-authz - 认证服务器,提供oauth2认证,接入第三方认证(如微信、钉钉) - - - org.hibernate - hibernate-jcache - - - cn.seqdata.cloud - seqdata-cloud-cache - 2.2.1-SNAPSHOT - - - - org.springframework.amqp - spring-rabbit - - - - org.springframework.boot - spring-boot-starter-cache - - - org.springframework.boot - spring-boot-starter-data-jdbc - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-data-redis - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.springframework.security - spring-security-oauth2-client - - - org.springframework.security.oauth.boot - spring-security-oauth2-autoconfigure - - - - com.aliyun - aliyun-java-sdk-core - 4.5.2 - - - com.aliyun - aliyun-java-sdk-dysmsapi - 2.1.0 - + + com.aliyun + aliyun-java-sdk-core + 4.5.2 + + + com.aliyun + aliyun-java-sdk-dysmsapi + 2.1.0 + - - cn.seqdata.cloud - seqdata-cloud-wechat - ${project.version} - - - cn.seqdata.cloud - seqdata-cloud-oauth2 - ${project.version} - - - cn.seqdata.cloud - seqdata-cloud-syslog - ${project.version} - + + cn.seqdata.cloud + seqdata-cloud-wechat + ${project.version} + + + cn.seqdata.cloud + seqdata-cloud-oauth2 + ${project.version} + + + cn.seqdata.cloud + seqdata-cloud-syslog + ${project.version} + - - com.microsoft.sqlserver - mssql-jdbc - runtime - - - mysql - mysql-connector-java - runtime - - - com.oracle.ojdbc - ojdbc8 - 19.3.0.0 - runtime - - - com.oracle.database.nls - orai18n - 19.3.0.0 - runtime - - + + com.microsoft.sqlserver + mssql-jdbc + runtime + + + mysql + mysql-connector-java + runtime + + + com.oracle.ojdbc + ojdbc8 + 19.3.0.0 + runtime + + + com.oracle.database.nls + orai18n + 19.3.0.0 + runtime + + diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index ec7ae52..63e9b33 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -8,7 +8,6 @@ import java.util.Map; import java.util.Objects; import javax.annotation.security.PermitAll; import javax.servlet.http.HttpServletRequest; -import javax.transaction.Transactional; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -21,6 +20,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; @@ -98,7 +98,7 @@ public class ConnectController { if(!passwordEncoder.matches(password, user.getPassword())) { throw new BadCredentialsException("用户名或密码不正确"); } - + userService.bindUser(Objects.requireNonNull(user.getId()), SecurityUtils.grantType(principal), SecurityUtils.username(principal)); -- Gitee From 306481e34fa0e50f2c83ad8ac376f26f76414c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 18 Mar 2021 15:34:30 +0800 Subject: [PATCH 183/228] =?UTF-8?q?=E7=A6=81=E7=94=A8=E6=8E=88=E6=9D=83?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E4=B8=AD=E8=B5=84=E6=BA=90=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E7=9A=84=E6=8E=88=E6=9D=83=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=8F=91=E5=B8=83=EF=BC=8C=E4=BC=9A=E5=AF=BC=E8=87=B4=E5=A4=A7?= =?UTF-8?q?=E9=87=8F=E7=9A=84=E5=86=85=E9=83=A8=E7=99=BB=E5=BD=95=E8=A2=AB?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthcServerConfiguration.java | 18 ++++++++++++++++++ .../oauth2/AuthzServerConfiguration.java | 8 ++++---- .../oauth2/service/AccountLockService.java | 3 +++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 0acbb7f..44ea20c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -2,10 +2,14 @@ package cn.seqdata.oauth2; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.web.access.channel.ChannelProcessingFilter; import cn.seqdata.oauth2.filter.PeriodFilter; @@ -26,6 +30,20 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { private final RemoteAddrProperties remoteAddrProperties; private final PeriodProperties periodProperties; + @Override + public void configure(ResourceServerSecurityConfigurer resources) { + //禁用授权服务器中资源服务器的授权事件发布,会导致大量的内部登录被日志记录 + resources.eventPublisher(new AuthenticationEventPublisher() { + @Override + public void publishAuthenticationSuccess(Authentication authentication) { + } + + @Override + public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) { + } + }); + } + @Override public void configure(HttpSecurity http) throws Exception { http.cors(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 619d2a2..614a8fb 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -37,11 +37,12 @@ public class AuthzServerConfiguration { @Bean public KeyExchanger keyExchanger(PasswordPolicy policy, StringRedisTemplate redisTemplate) { + final String PRIVATE_KEY = "exchanger.privateKey"; + final String PUBLIC_KEY = "exchanger.publicKey"; + KeyExchangers keyExchangeMethod = policy.getKeyExchangeMethod(); KeyExchanger keyExchanger = keyExchangeMethod.supplier.get(); - final String PRIVATE_KEY = "exchanger.privateKey"; - final String PUBLIC_KEY = "exchanger.publicKey"; ValueOperations opsForValue = redisTemplate.opsForValue(); if(BooleanUtils.isTrue(redisTemplate.hasKey(PRIVATE_KEY))) { @@ -67,8 +68,7 @@ public class AuthzServerConfiguration { } @Bean - public PasswordChecker passwordChecker(PasswordPolicy policy, PasswordEncoder encoder, - PasswordHistoryRepo historyRepo) { + public PasswordChecker passwordChecker(PasswordPolicy policy, PasswordEncoder encoder, PasswordHistoryRepo historyRepo) { CompositePasswordChecker passwordChecker = new CompositePasswordChecker(); passwordChecker.addChecker(new UsernameChecker()); passwordChecker.addChecker(new TextChecker()); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java index 3848450..dcf4d9a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java @@ -4,6 +4,7 @@ import org.joda.time.DateTime; import org.springframework.stereotype.Service; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.syslog.Syslog; /** * @author jrxian @@ -14,6 +15,7 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; public class AccountLockService { private final UserAccountRepo accountRepo; + @Syslog public void lock(String username, DateTime lockExpireDate) { accountRepo.findByUsername(username) .ifPresent(account -> { @@ -22,6 +24,7 @@ public class AccountLockService { }); } + @Syslog public void unlock(String username) { accountRepo.findByUsername(username) .ifPresent(account -> { -- Gitee From 6f1a7e52b763c51ef19ac3ffc818dcd3fe224054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 18 Mar 2021 15:50:04 +0800 Subject: [PATCH 184/228] =?UTF-8?q?=E7=A6=81=E7=94=A8=E6=8E=88=E6=9D=83?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E4=B8=AD=E8=B5=84=E6=BA=90=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E7=9A=84=E6=8E=88=E6=9D=83=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=8F=91=E5=B8=83=EF=BC=8C=E4=BC=9A=E5=AF=BC=E8=87=B4=E5=A4=A7?= =?UTF-8?q?=E9=87=8F=E7=9A=84=E5=86=85=E9=83=A8=E7=99=BB=E5=BD=95=E8=A2=AB?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/BizlogConfiguration.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index d7ca69d..3b72000 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -1,5 +1,8 @@ package cn.seqdata.oauth2; +import java.util.HashMap; +import java.util.Map; + import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; @@ -18,6 +21,7 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication; import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.syslog.*; +import cn.seqdata.syslog.SyslogRecord.SyslogRecordBuilder; /** * @author jrxian @@ -83,13 +87,24 @@ public class BizlogConfiguration { } private SyslogRecord.SyslogRecordBuilder logBuilder(String eventId, String eventName, AbstractAuthenticationEvent event) { - return SyslogRecord.builder() + Authentication authentication = event.getAuthentication(); + + SyslogRecordBuilder builder = SyslogRecord.builder() .projectId(appName) .eventId(eventId) .eventName(eventName) .eventTime(event.getTimestamp()) .forwardIp(DefaultSyslogResolver.forwardAddr()) .sourceIp(DefaultSyslogResolver.remoteAddr()) - .username(DefaultSyslogResolver.username(event.getAuthentication())); + .username(DefaultSyslogResolver.username(authentication)); + + Object details = authentication.getDetails(); + if(details instanceof Map) { + Map buildDetails = new HashMap<>(); + ((Map) details).forEach((k, v) -> buildDetails.put(String.valueOf(k), String.valueOf(v))); + builder.details(buildDetails); + } + + return builder; } } -- Gitee From 3f08ed52dc46580057c3fc6dfd35904feeb93b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B2=9C=E6=99=AF=E6=B6=A6?= Date: Thu, 18 Mar 2021 17:59:11 +0800 Subject: [PATCH 185/228] =?UTF-8?q?=E8=BF=87=E6=BB=A4=E6=8E=89=E5=86=85?= =?UTF-8?q?=E7=BD=AE=E7=94=A8=E6=88=B7=E7=9A=84=E6=97=A5=E5=BF=97=E8=AE=B0?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthcServerConfiguration.java | 10 ++-- .../AuthorizationServerConfiguration.java | 2 +- .../seqdata/oauth2/BizlogConfiguration.java | 47 ++++++++++--------- ...guration.java => LockerConfiguration.java} | 2 +- ...moteAddrFilter.java => AddressFilter.java} | 4 +- ...Properties.java => AddressProperties.java} | 2 +- .../oauth2/service/AccountLockService.java | 1 - .../src/main/resources/application.yml | 6 ++- 8 files changed, 39 insertions(+), 35 deletions(-) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/{AccountLockConfiguration.java => LockerConfiguration.java} (98%) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/{RemoteAddrFilter.java => AddressFilter.java} (84%) rename seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/{RemoteAddrProperties.java => AddressProperties.java} (95%) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 44ea20c..524d9ed 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -12,10 +12,10 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.web.access.channel.ChannelProcessingFilter; +import cn.seqdata.oauth2.filter.AddressFilter; +import cn.seqdata.oauth2.filter.AddressProperties; import cn.seqdata.oauth2.filter.PeriodFilter; import cn.seqdata.oauth2.filter.PeriodProperties; -import cn.seqdata.oauth2.filter.RemoteAddrFilter; -import cn.seqdata.oauth2.filter.RemoteAddrProperties; /** * Author: jrxian @@ -24,10 +24,10 @@ import cn.seqdata.oauth2.filter.RemoteAddrProperties; @Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) -@EnableConfigurationProperties({RemoteAddrProperties.class, PeriodProperties.class}) +@EnableConfigurationProperties({AddressProperties.class, PeriodProperties.class}) @lombok.RequiredArgsConstructor public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { - private final RemoteAddrProperties remoteAddrProperties; + private final AddressProperties remoteAddrProperties; private final PeriodProperties periodProperties; @Override @@ -48,7 +48,7 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { public void configure(HttpSecurity http) throws Exception { http.cors(); - http.addFilterAfter(new RemoteAddrFilter(remoteAddrProperties), ChannelProcessingFilter.class); + http.addFilterAfter(new AddressFilter(remoteAddrProperties), ChannelProcessingFilter.class); http.addFilterAfter(new PeriodFilter(periodProperties), ChannelProcessingFilter.class); http.authorizeRequests() diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index db54821..2931ef8 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -76,7 +76,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints - .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) + .allowedTokenEndpointRequestMethods(HttpMethod.POST) .authenticationManager(authenticationManager) .userDetailsService(userDetailsManager) .tokenServices(tokenServices) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index 3b72000..9709c1c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -1,8 +1,5 @@ package cn.seqdata.oauth2; -import java.util.HashMap; -import java.util.Map; - import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; @@ -11,6 +8,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.event.AbstractAuthenticationEvent; import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; @@ -18,10 +16,10 @@ import org.springframework.security.authentication.event.LogoutSuccessEvent; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.syslog.*; -import cn.seqdata.syslog.SyslogRecord.SyslogRecordBuilder; /** * @author jrxian @@ -47,7 +45,7 @@ public class BizlogConfiguration { @Bean public ApplicationListener syslogAuthSuccessListener(CompositeSyslogConsumer consumer) { return event -> { - if(isClientOnly(event.getAuthentication())) return; + if(isBuiltInUser(event.getAuthentication())) return; SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event); consumer.accept(builder.build()); @@ -57,7 +55,7 @@ public class BizlogConfiguration { @Bean public ApplicationListener syslogAuthFailureListener(CompositeSyslogConsumer consumer) { return event -> { - if(isClientOnly(event.getAuthentication())) return; + if(isBuiltInUser(event.getAuthentication())) return; AuthenticationException exception = event.getException(); Class exceptionClass = exception.getClass(); @@ -71,40 +69,43 @@ public class BizlogConfiguration { @Bean public ApplicationListener syslogLogoutSuccessListener(CompositeSyslogConsumer consumer) { return event -> { - if(isClientOnly(event.getAuthentication())) return; + if(isBuiltInUser(event.getAuthentication())) return; SyslogRecord.SyslogRecordBuilder builder = logBuilder("logout", "注销", event); consumer.accept(builder.build()); }; } - private boolean isClientOnly(Authentication authentication) { + /** + * 系统内置用户,不记录登录日志 + */ + private boolean isBuiltInUser(Authentication authentication) { + //没有任何角色 + if(CollectionUtils.isEmpty(authentication.getAuthorities())) { + return true; + } + + //匿名用户 + if(authentication instanceof AnonymousAuthenticationToken) { + return true; + } + + //client登录 if(authentication instanceof OAuth2Authentication) { return ((OAuth2Authentication) authentication).isClientOnly(); - } else { - return false; } + + return false; } private SyslogRecord.SyslogRecordBuilder logBuilder(String eventId, String eventName, AbstractAuthenticationEvent event) { - Authentication authentication = event.getAuthentication(); - - SyslogRecordBuilder builder = SyslogRecord.builder() + return SyslogRecord.builder() .projectId(appName) .eventId(eventId) .eventName(eventName) .eventTime(event.getTimestamp()) .forwardIp(DefaultSyslogResolver.forwardAddr()) .sourceIp(DefaultSyslogResolver.remoteAddr()) - .username(DefaultSyslogResolver.username(authentication)); - - Object details = authentication.getDetails(); - if(details instanceof Map) { - Map buildDetails = new HashMap<>(); - ((Map) details).forEach((k, v) -> buildDetails.put(String.valueOf(k), String.valueOf(v))); - builder.details(buildDetails); - } - - return builder; + .username(DefaultSyslogResolver.username(event.getAuthentication())); } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/LockerConfiguration.java similarity index 98% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/LockerConfiguration.java index 3d42277..5da5e5c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AccountLockConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/LockerConfiguration.java @@ -23,7 +23,7 @@ import cn.seqdata.oauth2.service.AccountLockService; */ @Configuration @lombok.AllArgsConstructor -public class AccountLockConfiguration { +public class LockerConfiguration { private final StringRedisTemplate redisTemplate; private final PasswordPolicy passwordPolicy; private final AccountLockService lockService; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrFilter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/AddressFilter.java similarity index 84% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrFilter.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/AddressFilter.java index f8d0201..077882e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrFilter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/AddressFilter.java @@ -10,8 +10,8 @@ import org.springframework.web.server.ResponseStatusException; * @date 2020/12/14 22:43 */ @lombok.RequiredArgsConstructor -public class RemoteAddrFilter extends RequestFilter { - private final RemoteAddrProperties properties; +public class AddressFilter extends RequestFilter { + private final AddressProperties properties; @Override protected void internalFilter(HttpServletRequest request) throws ResponseStatusException { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrProperties.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/AddressProperties.java similarity index 95% rename from seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrProperties.java rename to seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/AddressProperties.java index aa035d4..4482a43 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/RemoteAddrProperties.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/filter/AddressProperties.java @@ -16,7 +16,7 @@ import org.springframework.util.StringUtils; @lombok.Getter @lombok.Setter @ConfigurationProperties("spring.security.filter.address") -public class RemoteAddrProperties implements Predicate { +public class AddressProperties implements Predicate { private Pattern allow; private Pattern deny; diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java index dcf4d9a..d268911 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/AccountLockService.java @@ -24,7 +24,6 @@ public class AccountLockService { }); } - @Syslog public void unlock(String username) { accountRepo.findByUsername(username) .ifPresent(account -> { diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index f092c41..c082566 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -18,11 +18,15 @@ spring: username: voltcat password: vot@2018 jpa: + open-in-view: true + database: SQL_SERVER properties: hibernate: + enable_lazy_load_no_trans: true + dialect: org.hibernate.dialect.SQLServer2008Dialect cache: use_second_level_cache: false - use_query_cache: true + use_query_cache: false region: factory_class: org.hibernate.cache.jcache.JCacheRegionFactory javax: -- Gitee From 61b30954f92cf174f2026fa43e0bee33d0ed7767 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Tue, 23 Mar 2021 17:36:56 +0800 Subject: [PATCH 186/228] =?UTF-8?q?=E8=B7=AF=E7=94=B1=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E5=8F=82=E6=95=B0-=E7=AD=9B=E9=80=89?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E8=A7=92=E8=89=B2=E4=B8=8B=E7=9A=84=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=EF=BC=9B=E6=81=A2=E5=A4=8Dget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 4 +- .../oauth2/controller/RouterController.java | 59 +++++++++++++++---- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index 2931ef8..2285105 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -3,7 +3,6 @@ package cn.seqdata.oauth2; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import lombok.AllArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -36,6 +35,7 @@ import cn.seqdata.oauth2.nonce.PasswordTokenGranter; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -76,7 +76,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints - .allowedTokenEndpointRequestMethods(HttpMethod.POST) + .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST) .authenticationManager(authenticationManager) .userDetailsService(userDetailsManager) .tokenServices(tokenServices) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java index e86e7ea..5d2f7bb 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/RouterController.java @@ -1,11 +1,11 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; -import java.util.Collection; -import java.util.Objects; -import lombok.AllArgsConstructor; +import java.util.*; +import java.util.stream.Collectors; import io.swagger.annotations.Api; +import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Sort; import org.springframework.data.web.SortDefault; import org.springframework.web.bind.annotation.GetMapping; @@ -16,11 +16,17 @@ import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; import cn.seqdata.antd.Router; +import cn.seqdata.oauth2.domain.PermissionType; +import cn.seqdata.oauth2.jpa.perm.ActionPermission; import cn.seqdata.oauth2.jpa.perm.ModulePermission; import cn.seqdata.oauth2.jpa.perm.ViewPermission; +import cn.seqdata.oauth2.jpa.rbac.AuthorityPermission; +import cn.seqdata.oauth2.jpa.rbac.Permission; import cn.seqdata.oauth2.repos.perm.SysPermRepo; +import cn.seqdata.oauth2.repos.rbac.AuthorityPermissionRepo; import cn.seqdata.oauth2.service.RouterService; import cn.seqdata.oauth2.util.RouterUtils; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -31,30 +37,59 @@ import cn.seqdata.oauth2.util.RouterUtils; @RequestMapping("/router") @AllArgsConstructor public class RouterController { + private final AuthorityPermissionRepo permissionRepo; private final SysPermRepo sysPermRepo; private final RouterService service; @GetMapping - public Collection routers(@RequestParam(required = false) Long sys, - Principal principal, @SortDefault("orderNo") Sort sort) { + public Collection routers( + @RequestParam(required = false) Long sys, + @RequestParam(required = false) String id, + Principal principal, + @SortDefault("orderNo") Sort sort) { + //获取有权限的全部module Collection modulePerms = service.fetchModulePerms(principal, sort); //获取有权限的全部action,并且按view做好分组 - Multimap actionPerms = LinkedListMultimap.create(); - service.fetchActionPerms(principal, sort) - .forEach(x -> { - ViewPermission viewPerm = x.getViewPerm(); - actionPerms.put(viewPerm.getId(), x.getIdentifier()); + Collection actionPerms = service.fetchActionPerms(principal, sort); + + //筛选指定角色下面的所有菜单 + Set topRouterIds = new HashSet<>(); + boolean hasId = !StringUtils.isBlank(id); + if(hasId) { + List authorityIds = Arrays.stream(StringUtils.split(id, ",")) + .map(Long::valueOf) + .collect(Collectors.toList()); + List specialAuthPerms = permissionRepo.findByAuthorityIdIn(authorityIds, Sort.unsorted()); + Set authPerms = new HashSet<>(); + specialAuthPerms.forEach(x -> { + Permission perm = x.getPermission(); + authPerms.add(perm.getId()); + if(Objects.equals(perm.getType(), PermissionType.sys)) { + topRouterIds.add(perm.getId()); + } }); + modulePerms.removeIf(perm -> !authPerms.contains(perm.getId())); + actionPerms.removeIf(perm -> !authPerms.contains(perm.getId())); + } + Multimap actionPermMap = LinkedListMultimap.create(); + actionPerms.forEach(x -> { + ViewPermission viewPerm = x.getViewPerm(); + actionPermMap.put(viewPerm.getId(), x.getIdentifier()); + }); if(Objects.isNull(sys)) { Collection routers = service.topRouters(principal, sort); - routers.forEach(router -> service.doRouter(router, modulePerms, actionPerms)); + routers.removeIf(x -> hasId && !topRouterIds.contains(x.key)); + routers.forEach(router -> service.doRouter(router, modulePerms, actionPermMap)); return routers; } else { //如果指定了sys,只查询本sys下的module Router topRouter = RouterUtils.toRouter(sysPermRepo.getOne(sys)); - service.doRouter(topRouter, modulePerms, actionPerms); + if(hasId && !topRouterIds.contains(topRouter.key)) { + return Collections.emptyList(); + } + service.doRouter(topRouter, modulePerms, actionPermMap); return topRouter.children; } } -- Gitee From 15327aceaf79270e7f3efc5ad1a34c0dbdab70e1 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 12 May 2021 15:29:25 +0800 Subject: [PATCH 187/228] =?UTF-8?q?unionId-=E6=96=B0=E5=BB=BA=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/service/UserService.java | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index ccc06fe..4af3f42 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -3,8 +3,8 @@ package cn.seqdata.oauth2.service; import java.security.Principal; import java.util.Objects; import java.util.Optional; -import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -21,11 +21,14 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.util.SecurityUtils; import cn.seqdata.oauth2.wechat.WechatUserProfile; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** * Author: jrxian * Date: 2020-01-27 01:46 */ +@Slf4j @Service @AllArgsConstructor public class UserService { @@ -59,7 +62,8 @@ public class UserService { public void saveAsUser(String clientId, UserProfile profile) { UserDetailId entityId = new UserDetailId(clientId, profile.getName()); Optional optional = userDetailRepo.findById(entityId); - if(!optional.isPresent()) {//用户不存在 + if(!optional.isPresent()) { + //用户不存在 UserDetail userDetail = new UserDetail(entityId); if(profile instanceof WechatUserProfile) { @@ -71,7 +75,7 @@ public class UserService { } } - //没有找到微信unionid + //没有找到微信unionid-全新的用户 if(Objects.isNull(userDetail.getUserId())) { Long userId = saveAsUser(profile); saveAsUserAccount(userId, profile); @@ -107,18 +111,57 @@ public class UserService { //如果是微信绑定,更新account的union_id if(profile instanceof WechatUserProfile) { UserAccount account = accountRepo.getOne(userId); - account.setUnionId(((WechatUserProfile) profile).getUnionId()); + String unionId = ((WechatUserProfile) profile).getUnionId(); + if(StringUtils.isNotBlank(unionId) && !Objects.equals(unionId, account.getUnionId())) { + // 判断unionId是否已经存在,如果存在则不设置 + if(accountRepo.existsByUnionId(unionId)) { + log.warn("重复设置,unionId:{}, userId:{}", unionId, userId); + unionId = null; + } + } + account.setUnionId(unionId); accountRepo.save(account); } - bindUser(userId, clientId, profile.getName()); + bindUserDetail(userId, clientId, profile.getName()); } + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) + public void bindUserDetail(long userId, String clientId, String username) { + UserDetailId entityId = new UserDetailId(clientId, username); + UserDetail entity = userDetailRepo.findById(entityId) + .orElse(new UserDetail(entityId)); + entity.setUserId(userId); + userDetailRepo.save(entity); + } + + /** + * UserDetail修改了user_id,但是原user:临时用户却依然占据着UnionId,未被清理掉; + * 同上fixme, 清理临时用户 + * UserDetailId设置为新#userId; + * UserDetailId旧userId的unionId清理掉并且设置到新的user上 + */ @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public void bindUser(long userId, String clientId, String username) { UserDetailId entityId = new UserDetailId(clientId, username); UserDetail entity = userDetailRepo.findById(entityId) .orElse(new UserDetail(entityId)); + //执行清理行为-重置掉unionId + Optional.ofNullable(entity.getUser()) + .ifPresent(clearAccount -> { + String unionId = clearAccount.getUnionId(); + if(StringUtils.isBlank(unionId)) { + return; + } + UserAccount account = accountRepo.getOne(userId); + if(StringUtils.isBlank(account.getUnionId())) { + account.setUnionId(unionId); + accountRepo.save(account); + } + clearAccount.setUnionId(null); + accountRepo.save(clearAccount); + }); + entity.setUserId(userId); userDetailRepo.save(entity); } @@ -136,12 +179,21 @@ public class UserService { .orElse(new UserAccount(entityId)); account.setUsername(profile.getUsername()); account.setEmail(profile.getEmail()); + // TODO 唯一性校验确实 account.setMobile(profile.getMobile()); //第三方首次创建的账号默认是管理员 account.setAdmin(Boolean.TRUE); if(profile instanceof WechatUserProfile) { //为微信单独保存 union_id - account.setUnionId(((WechatUserProfile) profile).getUnionId()); + String unionId = ((WechatUserProfile) profile).getUnionId(); + if(StringUtils.isNotBlank(unionId) && !Objects.equals(unionId, account.getUnionId())) { + // 判断unionId是否已经存在,如果存在则不设置 + if(accountRepo.existsByUnionId(unionId)) { + log.warn("重复设置,unionId:{}, userId:{}", unionId, account.getId()); + unionId = null; + } + } + account.setUnionId(unionId); } accountRepo.save(account); } -- Gitee From 238a12d22955dcfa3752bbc41aa3af3da559e882 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Wed, 12 May 2021 16:38:23 +0800 Subject: [PATCH 188/228] =?UTF-8?q?=E7=BD=91=E5=85=B3=E5=A4=84=E6=94=BE?= =?UTF-8?q?=E7=BD=AE=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6-saas=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E6=96=87=E4=BB=B6=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/gateway/swagger/SwaggerProvider.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java index 51afe4c..e36a9a7 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/swagger/SwaggerProvider.java @@ -6,7 +6,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -15,6 +14,8 @@ import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; + +import lombok.SneakyThrows; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; @@ -54,7 +55,9 @@ public class SwaggerProvider implements SwaggerResourcesProvider { Map args = predicate.getArgs(); String pattern = args.get("pattern"); String location = StringUtils.replace(pattern, "/**", "/v2/api-docs"); - + if(StringUtils.isBlank(location)) { + return; + } swaggerResources.add(swaggerResource(routeId, location)); })); -- Gitee From 665b9072a251e9a720b1397517de653f30d98a50 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Tue, 22 Jun 2021 15:55:17 +0800 Subject: [PATCH 189/228] =?UTF-8?q?=E8=A7=A3=E5=AF=86=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=B0=8F=E7=A8=8B=E5=BA=8F=E6=89=8B=E6=9C=BA?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/WxAppController.java | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 93306a0..e12444b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.security.Principal; import java.util.HashMap; import java.util.Map; -import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -30,6 +29,7 @@ import cn.seqdata.wxapp.message.Code2SessionResponse; import cn.seqdata.wxapp.pojo.PhoneInfo; import cn.seqdata.wxapp.pojo.UserInfo; import cn.seqdata.wxapp.util.WxAppUtils; +import lombok.AllArgsConstructor; /** * @author jrxian @@ -93,24 +93,39 @@ public class WxAppController { @GetMapping("/phone") @Transactional public OAuth2AccessToken phone(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { - Code2SessionResponse response = wxappService.signin(id, code); - String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); - PhoneInfo phoneInfo = objectMapper.readValue(decryptedData, PhoneInfo.class); - - Map attributes = new HashMap<>(); - WxAppUtils.toAttributes(attributes, phoneInfo); - WxAppUtils.toAttributes(attributes, response); + Map attributes = attributes(id, code, iv, encryptedData); - userService.loadUser(OAuth2Constants.MOBILE, phoneInfo.purePhoneNumber) + userService.loadUser(OAuth2Constants.MOBILE, String.valueOf(attributes.get("purePhoneNumber"))) .map(UserAccount::getId) - .ifPresent(userId -> userService.bindUser(userId, id, response.openid)); + .ifPresent(userId -> userService.bindUser(userId, id, String.valueOf(attributes.get("openid")))); DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, WxAppUtils.WXAPP_ATTR_KEY); return tokenService.createToken(id, remoteUser); } + /** + * 获取微信手机号,绑定到现有账号 + */ + @ApiOperation("解密信息小程序手机号") + @GetMapping("/phone/info") + @Transactional + public Map decryptData(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { + return attributes(id, code, iv, encryptedData); + } + @GetMapping("/token") public String token(@PathVariable("id") String id) { return wxappService.accessToken(id); } + + private Map attributes(String id, String code, String iv, String encryptedData) throws IOException { + Code2SessionResponse response = wxappService.signin(id, code); + String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); + PhoneInfo phoneInfo = objectMapper.readValue(decryptedData, PhoneInfo.class); + + Map attributes = new HashMap<>(); + WxAppUtils.toAttributes(attributes, phoneInfo); + WxAppUtils.toAttributes(attributes, response); + return attributes; + } } -- Gitee From 4c94637d74c3f70accbc8c8027269f5d37fde3eb Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 25 Jun 2021 11:41:25 +0800 Subject: [PATCH 190/228] =?UTF-8?q?=E5=B0=8F=E7=A8=8B=E5=BA=8F=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E6=89=8B=E6=9C=BA=E5=8F=B7:=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E4=B8=80=E4=BD=93=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 ++-- .../oauth2/controller/WxAppController.java | 24 ++++++++++++++++ .../seqdata/oauth2/service/UserService.java | 28 +++++++++++++++++-- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 5704270..f2d074d 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ - + org.springframework.boot spring-boot-maven-plugin @@ -137,7 +137,7 @@ - http://docker.seqdata.cn:2375 + http://192.168.0.88:2375 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index e12444b..92d963c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.security.Principal; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -103,6 +104,29 @@ public class WxAppController { return tokenService.createToken(id, remoteUser); } + /** + * 获取微信手机号,绑定到现有账号 + */ + @ApiOperation("小程序手机号登录注册一体化-档案猫") + @GetMapping("/v2/phone") + @Transactional + public OAuth2AccessToken phoneV2(@PathVariable("id") String id, String code, String iv, String encryptedData) throws IOException { + Map attributes = attributes(id, code, iv, encryptedData); + String purePhoneNumber = String.valueOf(attributes.get("purePhoneNumber")); + Optional user = userService.loadUser(OAuth2Constants.MOBILE, purePhoneNumber); + if(user.isPresent()) { + // 如果手机号所在用户存在,则将openId从空user(只有openId)绑定到该手机号所在用户上 + user.map(UserAccount::getId) + .ifPresent(userId -> userService.bindUser(userId, id, String.valueOf(attributes.get("openid")))); + } else { + // 如果手机号所在用户不存在,则将手机号绑定到空user(只有openId)所在用户上 or 创建新用户 + userService.bindUserPhone(purePhoneNumber, id, String.valueOf(attributes.get("openid")), attributes); + } + + DefaultOAuth2User remoteUser = new DefaultOAuth2User(DefaultRole.NO_ROLES, attributes, WxAppUtils.WXAPP_ATTR_KEY); + return tokenService.createToken(id, remoteUser); + } + /** * 获取微信手机号,绑定到现有账号 */ diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 4af3f42..38fe6b5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -1,6 +1,7 @@ package cn.seqdata.oauth2.service; import java.security.Principal; +import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -25,8 +26,8 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; /** - * Author: jrxian - * Date: 2020-01-27 01:46 + * @author jrxian + * @date 2020-01-27 01:46 */ @Slf4j @Service @@ -166,6 +167,29 @@ public class UserService { userDetailRepo.save(entity); } + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) + public void bindUserPhone(String phone, String clientId, String username, Map attributes) { + UserDetailId entityId = new UserDetailId(clientId, username); + UserDetail entity = userDetailRepo.findById(entityId) + .orElse(new UserDetail(entityId)); + if(Objects.isNull(entity.getUserId())) { + //找不到用户 + UserAccount account = new UserAccount(); + account.setUnionId((String) attributes.get("unionid")); + account.setMobile(phone); + account = accountRepo.save(account); + //绑定第三方-小程序 + entity.setUserId(account.getId()); + userDetailRepo.save(entity); + } else { + //找到用户,绑定手机号到该用户上 + UserAccount account = accountRepo.getOne(entity.getUserId()); + account.setMobile(phone); + accountRepo.save(account); + } + //TODO 设置角色-group-user(后端url权限) + } + private Long saveAsUser(UserProfile profile) { User user = new User(); user.setName(profile.getNickname()); -- Gitee From 0f8e9180b8236879a34e23759fb8f940f0b12e84 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 25 Jun 2021 15:27:22 +0800 Subject: [PATCH 191/228] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=8F=91?= =?UTF-8?q?=E9=80=81=E5=AD=98=E5=82=A8=E6=96=B9=E5=BC=8F2=EF=BC=9Aredis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/AuthcServerConfiguration.java | 2 +- .../cn/seqdata/oauth2/NonceConfiguration.java | 14 ++-- .../oauth2/controller/ConnectController.java | 16 +++++ .../oauth2/nonce/EmailNonceHandler.java | 5 +- .../oauth2/nonce/MobileNonceHandler.java | 5 +- .../java/cn/seqdata/oauth2/nonce/Nonce.java | 21 ++++++ .../cn/seqdata/oauth2/nonce/NonceHandler.java | 5 ++ .../oauth2/nonce/NonceHandlerImpl.java | 64 +++++++++++++++++-- 8 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/Nonce.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java index 524d9ed..9e47b1d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthcServerConfiguration.java @@ -53,7 +53,7 @@ public class AuthcServerConfiguration extends ResourceServerConfigurerAdapter { http.authorizeRequests() .antMatchers("/csrf", "/oauth2/**", "/rbac/**", "/perm/**" - , "/oauth/code/**", "/connect/nonce", "/password/**", "/wxapp/**") + , "/oauth/code/**", "/connect/nonce", "/connect/v2/nonce", "/password/**", "/wxapp/**") .permitAll(); super.configure(http); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java index 28aad03..0b48b89 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java @@ -6,6 +6,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.AuthenticationException; @@ -32,14 +33,14 @@ public class NonceConfiguration { @Bean @ConditionalOnBean(AliyunSmsService.class) - public NonceHandler mobileNonceService(UserAccountRepo accountRepo, AliyunSmsService smsService) { - return new MobileNonceHandler(accountRepo, smsService); + public NonceHandler mobileNonceService(UserAccountRepo accountRepo, AliyunSmsService smsService, StringRedisTemplate redisTemplate) { + return new MobileNonceHandler(accountRepo, smsService, redisTemplate); } @Bean @ConditionalOnBean(JavaMailSender.class) - public NonceHandler emailNonceService(UserAccountRepo accountRepo, JavaMailSender mailSender) { - return new EmailNonceHandler(accountRepo, mailSender); + public NonceHandler emailNonceService(UserAccountRepo accountRepo, JavaMailSender mailSender, StringRedisTemplate redisTemplate) { + return new EmailNonceHandler(accountRepo, mailSender, redisTemplate); } @Bean @@ -56,6 +57,11 @@ public class NonceConfiguration { throw new AuthenticationServiceException("未实现验证码登录功能"); } + @Override + public void nonceWithRedis(String mobile, int expire) { + throw new AuthenticationServiceException("未实现验证码登录功能"); + } + @Override public void check(String mobile, int nonce) throws AuthenticationException { throw new AuthenticationServiceException("未实现验证码登录功能"); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 63e9b33..112c1d8 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -88,6 +88,22 @@ public class ConnectController { return ResponseEntity.ok("验证码已经发送,2分钟内输入有效!"); } + /** + * 向手机或邮箱发送一次性验证码 + */ + @Syslog + @ApiOperation("发送验证码") + @RequestMapping(value = "/v2/nonce", method = {RequestMethod.GET, RequestMethod.POST}) + @PermitAll + public ResponseEntity nonce2(String username) { + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) { + nonceHandler.nonceWithRedis(username, 2 * DateTimeConstants.SECONDS_PER_MINUTE); + } + } + return ResponseEntity.ok("验证码已经发送,2分钟内输入有效!"); + } + @Syslog @ApiOperation("通过密码绑定") @GetMapping(value = "/password") diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java index fe0e392..96f7bcd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2.nonce; import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.mail.MailSender; import org.springframework.mail.SimpleMailMessage; @@ -16,8 +17,8 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; public class EmailNonceHandler extends NonceHandlerImpl { private final MailSender mailSender; - public EmailNonceHandler(UserAccountRepo accountRepo, MailSender mailSender) { - super(accountRepo); + public EmailNonceHandler(UserAccountRepo accountRepo, MailSender mailSender, StringRedisTemplate redisTemplate) { + super(accountRepo, redisTemplate); this.mailSender = mailSender; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java index 120696a..ca32b4e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2.nonce; import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import org.springframework.data.redis.core.StringRedisTemplate; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; @@ -14,8 +15,8 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; public class MobileNonceHandler extends NonceHandlerImpl { private final AliyunSmsService smsService; - public MobileNonceHandler(UserAccountRepo accountRepo, AliyunSmsService smsService) { - super(accountRepo); + public MobileNonceHandler(UserAccountRepo accountRepo, AliyunSmsService smsService, StringRedisTemplate redisTemplate) { + super(accountRepo, redisTemplate); this.smsService = smsService; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/Nonce.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/Nonce.java new file mode 100644 index 0000000..dcfae98 --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/Nonce.java @@ -0,0 +1,21 @@ +package cn.seqdata.oauth2.nonce; + +import java.io.Serializable; + +import lombok.*; + +/** + * @author Lin.Zhang + * @date 2021/6/25 + */ +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class Nonce implements Serializable { + private static final long serialVersionUID = 1L; + + private Integer nonceCode; + private Long expire; +} diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java index 66304b4..e61346b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java @@ -12,6 +12,11 @@ public interface NonceHandler { */ void nonce(String username, int expire); + /** + * 生成一次性验证码-存入redis + */ + void nonceWithRedis(String username, int expire); + /** * 检查一次性验证码 */ diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index e957d8f..39fdec6 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -2,17 +2,20 @@ package cn.seqdata.oauth2.nonce; import java.util.Objects; import java.util.Optional; -import lombok.AllArgsConstructor; +import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.RandomUtils; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.www.NonceExpiredException; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ServerWebInputException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; @@ -21,14 +24,21 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; * @author jrxian * @date 2020-06-21 21:26 */ -@AllArgsConstructor public abstract class NonceHandlerImpl implements NonceHandler { - protected final UserAccountRepo accountRepo; + protected UserAccountRepo accountRepo; + protected StringRedisTemplate redisTemplate; + private final ObjectMapper objectMapper = new ObjectMapper(); + private static final String NONCE = "username_to_nonce:"; protected abstract Optional findUserAccount(String username); protected abstract void sendNonce(String username, int nonce); + public NonceHandlerImpl(UserAccountRepo accountRepo, StringRedisTemplate redisTemplate) { + this.accountRepo = accountRepo; + this.redisTemplate = redisTemplate; + } + @Transactional @Override public void nonce(String username, int expire) { @@ -46,10 +56,31 @@ public abstract class NonceHandlerImpl implements NonceHandler { @Transactional @Override - public void check(String username, int nonce) throws AuthenticationException { - UserAccount account = findUserAccount(username) - .orElseThrow(() -> new UsernameNotFoundException("无法用此账号登录")); + public void nonceWithRedis(String username, int expire) { + int nonce = RandomUtils.nextInt(100000, 999999); + DateTime now = DateTime.now(); + now = now.plusSeconds(expire); + // key:username_to_nonce:135xxxxxxxx + redisTemplate.opsForValue() + .set(NONCE + username, toJSON(new Nonce(nonce, now.getMillis())), expire, TimeUnit.SECONDS); +// sendNonce(username, nonce); + } + @Transactional + @Override + public void check(String username, int nonce) throws AuthenticationException { + Optional accountOptional = findUserAccount(username); + if(!accountOptional.isPresent()) { + //找不到验证码的情况下进入redis查看 + String json = redisTemplate.opsForValue() + .get(NONCE + username); + Nonce non = fromJSON(json, Nonce.class); + if(Objects.nonNull(non) && Objects.equals(nonce, non.getNonceCode()) && non.getExpire() < System.currentTimeMillis()) { + return; + } + throw new RuntimeException("验证码无效"); + } + UserAccount account = accountOptional.get(); try { DateTime expire = account.getNonceExpire(); Integer code = account.getNonceCode(); @@ -70,4 +101,23 @@ public abstract class NonceHandlerImpl implements NonceHandler { accountRepo.save(account); } } + + private String toJSON(T object) { + try { + return objectMapper.writeValueAsString(object); + } catch(JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } + + private T fromJSON(String json, Class clazz) { + if(StringUtils.isBlank(json)) { + return null; + } + try { + return objectMapper.readValue(json, clazz); + } catch(JsonProcessingException ex) { + throw new RuntimeException(ex); + } + } } -- Gitee From b36f122d5e3de7d761c28d6c1a534de2dd640a36 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 25 Jun 2021 15:53:45 +0800 Subject: [PATCH 192/228] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=8F=91?= =?UTF-8?q?=E9=80=81=E5=AD=98=E5=82=A8=E6=96=B9=E5=BC=8F2=EF=BC=9Aredis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/nonce/NonceHandlerImpl.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index 39fdec6..f7e10c0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -11,6 +11,7 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.www.NonceExpiredException; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ServerWebInputException; @@ -69,32 +70,37 @@ public abstract class NonceHandlerImpl implements NonceHandler { @Transactional @Override public void check(String username, int nonce) throws AuthenticationException { + RuntimeException checkNonceAccount = checkNonceAccount(username, nonce); + if(Objects.isNull(checkNonceAccount)) { + return; + } + //redis判断验证码是否存在 + RuntimeException checkNonceRedis = checkNonceRedis(username, nonce); + if(Objects.isNull(checkNonceRedis)) { + return; + } + throw checkNonceAccount; + } + + private RuntimeException checkNonceAccount(String username, int nonce) { Optional accountOptional = findUserAccount(username); if(!accountOptional.isPresent()) { - //找不到验证码的情况下进入redis查看 - String json = redisTemplate.opsForValue() - .get(NONCE + username); - Nonce non = fromJSON(json, Nonce.class); - if(Objects.nonNull(non) && Objects.equals(nonce, non.getNonceCode()) && non.getExpire() < System.currentTimeMillis()) { - return; - } - throw new RuntimeException("验证码无效"); + return new UsernameNotFoundException("无法用此账号登录"); } UserAccount account = accountOptional.get(); try { DateTime expire = account.getNonceExpire(); Integer code = account.getNonceCode(); if(Objects.isNull(expire) || Objects.isNull(code)) { - throw new AuthenticationCredentialsNotFoundException("未获取验证码"); + return new AuthenticationCredentialsNotFoundException("未获取验证码"); } - if(expire.isBeforeNow()) { - throw new NonceExpiredException("验证码已过期"); + return new NonceExpiredException("验证码已过期"); } - if(nonce != code) { - throw new BadCredentialsException("无效的验证码"); + return new BadCredentialsException("无效的验证码"); } + return null; } finally { account.setNonceCode(null); account.setNonceExpire(null); @@ -102,6 +108,22 @@ public abstract class NonceHandlerImpl implements NonceHandler { } } + private RuntimeException checkNonceRedis(String username, int nonce) { + String json = redisTemplate.opsForValue() + .get(NONCE + username); + Nonce non = fromJSON(json, Nonce.class); + if(Objects.isNull(non)) { + return new AuthenticationCredentialsNotFoundException("验证码已失效或未获取"); + } + if(!Objects.equals(nonce, non.getNonceCode())) { + return new BadCredentialsException("无效的验证码"); + } + if(non.getExpire() > System.currentTimeMillis()) { + return new NonceExpiredException("验证码已过期"); + } + return null; + } + private String toJSON(T object) { try { return objectMapper.writeValueAsString(object); -- Gitee From 7e22ba1fc1600321d97278f11ebe0a897996274d Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 25 Jun 2021 16:10:55 +0800 Subject: [PATCH 193/228] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=8F=91?= =?UTF-8?q?=E9=80=81=E5=AD=98=E5=82=A8=E6=96=B9=E5=BC=8F2=EF=BC=9Aredis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index f7e10c0..6bd9f5c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -64,7 +64,7 @@ public abstract class NonceHandlerImpl implements NonceHandler { // key:username_to_nonce:135xxxxxxxx redisTemplate.opsForValue() .set(NONCE + username, toJSON(new Nonce(nonce, now.getMillis())), expire, TimeUnit.SECONDS); -// sendNonce(username, nonce); + sendNonce(username, nonce); } @Transactional @@ -79,6 +79,9 @@ public abstract class NonceHandlerImpl implements NonceHandler { if(Objects.isNull(checkNonceRedis)) { return; } + if(checkNonceRedis instanceof NonceExpiredException || checkNonceRedis instanceof BadCredentialsException) { + throw checkNonceRedis; + } throw checkNonceAccount; } @@ -118,7 +121,7 @@ public abstract class NonceHandlerImpl implements NonceHandler { if(!Objects.equals(nonce, non.getNonceCode())) { return new BadCredentialsException("无效的验证码"); } - if(non.getExpire() > System.currentTimeMillis()) { + if(non.getExpire() < System.currentTimeMillis()) { return new NonceExpiredException("验证码已过期"); } return null; -- Gitee From 0fb3492fd4c8b5adb26fff48de5f52baf11cf4db Mon Sep 17 00:00:00 2001 From: zl1995 Date: Mon, 28 Jun 2021 10:47:06 +0800 Subject: [PATCH 194/228] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=B3=A8=E5=86=8C-=E7=94=A8=E6=88=B7=E6=9C=AA?= =?UTF-8?q?=E6=89=BE=E5=88=B0=EF=BC=8C=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthorizationServerConfiguration.java | 10 +++--- .../nonce/NonceAuthenticationProvider.java | 18 +++++++++- .../oauth2/nonce/NonceHandlerImpl.java | 3 ++ .../oauth2/nonce/NonceUserService.java | 33 +++++++++++++++++++ 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceUserService.java diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java index 2285105..2f61f69 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthorizationServerConfiguration.java @@ -28,10 +28,7 @@ import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; import cn.seqdata.crypto.exchanger.KeyExchanger; -import cn.seqdata.oauth2.nonce.NonceAuthenticationProvider; -import cn.seqdata.oauth2.nonce.NonceHandler; -import cn.seqdata.oauth2.nonce.NonceTokenGranter; -import cn.seqdata.oauth2.nonce.PasswordTokenGranter; +import cn.seqdata.oauth2.nonce.*; import cn.seqdata.oauth2.repos.oauth.ClientDetailRepo; import cn.seqdata.oauth2.service.JpaClientDetailsService; import cn.seqdata.oauth2.service.JpaUserDetailsManager; @@ -53,6 +50,7 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu private final TokenEnhancer tokenEnhancer; private final ClientDetailRepo clientDetailRepo; private final List nonceHandlers; + private final NonceUserService userService; @Override public void configure(AuthorizationServerSecurityConfigurer security) { @@ -102,10 +100,10 @@ public class AuthorizationServerConfiguration extends AuthorizationServerConfigu //自定义的手机/邮箱验证码登录 //!!!必须在用户名密码验证前面 - NonceAuthenticationProvider mobileNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByMobile, userDetailsManager, nonceHandlers); + NonceAuthenticationProvider mobileNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByMobile, userDetailsManager, nonceHandlers, userService); tokenGranters.add(new NonceTokenGranter(mobileNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.MOBILE)); - NonceAuthenticationProvider emailNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByEmail, userDetailsManager, nonceHandlers); + NonceAuthenticationProvider emailNonceProvider = new NonceAuthenticationProvider(JpaUserDetailsManager::loadUserByEmail, userDetailsManager, nonceHandlers, userService); tokenGranters.add(new NonceTokenGranter(emailNonceProvider, tokenServices, clientDetails, requestFactory, OAuth2Constants.EMAIL)); if(Objects.nonNull(authenticationManager)) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationProvider.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationProvider.java index 6d13179..70fef14 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationProvider.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceAuthenticationProvider.java @@ -3,7 +3,6 @@ package cn.seqdata.oauth2.nonce; import java.util.List; import java.util.Optional; import java.util.function.BiFunction; -import lombok.AllArgsConstructor; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.security.authentication.AuthenticationServiceException; @@ -12,8 +11,10 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import cn.seqdata.oauth2.service.JpaUserDetailsManager; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -24,6 +25,7 @@ public class NonceAuthenticationProvider extends AbstractUserDetailsAuthenticati private final BiFunction userDetailsLoader; private final JpaUserDetailsManager userDetailsManager; private final List nonceHandlers; + private final NonceUserService userService; @Override protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { @@ -32,6 +34,14 @@ public class NonceAuthenticationProvider extends AbstractUserDetailsAuthenticati .orElseThrow(() -> new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation")); } catch(AuthenticationServiceException ex) { throw ex; + } catch(UsernameNotFoundException ex) { + // 用户未找到,新增用户 + try { + additionalAuthenticationChecks(null, authentication); + return addUser(username, authentication); + } catch(Exception e) { + throw new InternalAuthenticationServiceException(e.getMessage(), e); + } } catch(Exception ex) { throw new InternalAuthenticationServiceException(ex.getMessage(), ex); } @@ -47,4 +57,10 @@ public class NonceAuthenticationProvider extends AbstractUserDetailsAuthenticati } } } + + public UserDetails addUser(String username, UsernamePasswordAuthenticationToken authentication) { + // 处理手机号-不存在可以注册的逻辑 + userService.addUser(username); + return retrieveUser(username, authentication); + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index 6bd9f5c..59a3906 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -82,6 +82,9 @@ public abstract class NonceHandlerImpl implements NonceHandler { if(checkNonceRedis instanceof NonceExpiredException || checkNonceRedis instanceof BadCredentialsException) { throw checkNonceRedis; } + if(checkNonceAccount instanceof UsernameNotFoundException && checkNonceRedis instanceof AuthenticationCredentialsNotFoundException) { + throw checkNonceRedis; + } throw checkNonceAccount; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceUserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceUserService.java new file mode 100644 index 0000000..abb244a --- /dev/null +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceUserService.java @@ -0,0 +1,33 @@ +package cn.seqdata.oauth2.nonce; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import cn.seqdata.oauth2.jpa.rbac.User; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.repos.rbac.UserRepo; +import lombok.AllArgsConstructor; + +/** + * @author Lin.Zhang + * @date 2021/6/28 + */ +@Service +@AllArgsConstructor +public class NonceUserService { + + private final UserAccountRepo accountRepo; + private final UserRepo userRepo; + + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) + public void addUser(String username) { + // 处理手机号-不存在可以注册的逻辑 + User user = new User(); + user = userRepo.save(user); + UserAccount account = accountRepo.getOne(user.getId()); + account.setMobile(username); + accountRepo.save(account); + } +} -- Gitee From 1dcb4b8727526fac4bb29c4b2e7acbdfdad6f86d Mon Sep 17 00:00:00 2001 From: zl1995 Date: Mon, 28 Jun 2021 15:44:19 +0800 Subject: [PATCH 195/228] =?UTF-8?q?=E8=B4=A6=E6=88=B7=E5=90=8D-=E6=89=8B?= =?UTF-8?q?=E6=9C=BA=E5=8F=B7=E4=B8=8D=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/PasswordController.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java index d82d7ec..9d7fdd1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/PasswordController.java @@ -2,14 +2,15 @@ package cn.seqdata.oauth2.controller; import java.util.Collections; import java.util.List; +import java.util.Objects; import javax.annotation.security.PermitAll; -import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.CredentialsExpiredException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; @@ -26,9 +27,11 @@ import cn.seqdata.crypto.exchanger.KeyExchanger; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.policy.PasswordPolicy; import cn.seqdata.oauth2.policy.checker.PasswordChecker; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.AccountLockService; import cn.seqdata.oauth2.service.JpaUserDetailsPasswordService; import cn.seqdata.syslog.Syslog; +import lombok.AllArgsConstructor; /** * @author jrxian @@ -39,6 +42,7 @@ import cn.seqdata.syslog.Syslog; @RequestMapping("/password") @AllArgsConstructor public class PasswordController { + private final UserAccountRepo accountRepo; private final AuthenticationManager authenticationManager; private final UserDetailsManager userDetailsManager; private final JpaUserDetailsPasswordService passwordService; @@ -88,6 +92,14 @@ public class PasswordController { } } + //账号和手机号关联检查 + accountRepo.findByUsername(username) + .ifPresent(account -> { + if(!Objects.equals(account.getMobile(), mobile)) { + throw new AuthenticationServiceException("账户名与手机号不匹配"); + } + }); + //检查密码强度 String decryptPassword = keyExchanger.decrypt(password); passwordChecker.accept(username, decryptPassword); -- Gitee From 23954c043c4d75a4234e1170a866ec9fdfeffad8 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Mon, 28 Jun 2021 16:02:55 +0800 Subject: [PATCH 196/228] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E9=80=9A=E7=94=A8=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/nonce/AliyunSmsService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AliyunSmsService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AliyunSmsService.java index 43e5b7b..4788f42 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AliyunSmsService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AliyunSmsService.java @@ -3,8 +3,6 @@ package cn.seqdata.oauth2.nonce; import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; @@ -20,6 +18,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.oauth2.AliyunProperties; import cn.seqdata.oauth2.AliyunSmsProperties; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; /** * @author jrxian @@ -50,8 +50,8 @@ public class AliyunSmsService implements BiConsumer { try { SendSmsResponse response = acsClient.getAcsResponse(request); if(!StringUtils.equals("OK", response.getCode())) { - log.warn(response.getMessage()); - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, response.getMessage()); + log.error("{}发送验证码失败,原因:{}", mobile, response.getMessage()); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "验证码发送失败,请稍后再试"); } } catch(ClientException ex) { log.warn(ex.getMessage()); -- Gitee From 8257715cc4d90e380c5991b2e53bc9bca604fb65 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Tue, 29 Jun 2021 09:48:33 +0800 Subject: [PATCH 197/228] =?UTF-8?q?docker=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f2d074d..dff1488 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ - http://192.168.0.88:2375 + http://docker.voltmao.com:2375 -- Gitee From c826190f23e60b04be6375df78db56a6f1415cd0 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 13 Aug 2021 16:29:49 +0800 Subject: [PATCH 198/228] =?UTF-8?q?=E6=96=B0=E5=A2=9Etoken=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E6=8E=A5=E5=8F=A3=EF=BC=8Ctoken=E4=B8=8D=E5=8F=98?= =?UTF-8?q?=EF=BC=8C=E6=9D=83=E9=99=90=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 64 ++++++++++++++++++- .../oauth2/service/DefaultTokenServices.java | 10 ++- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 69e74a4..9853f6b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -2,17 +2,23 @@ package cn.seqdata.oauth2.controller; import java.security.Principal; import java.util.Collection; +import java.util.HashSet; import java.util.Objects; import java.util.Optional; import java.util.function.Function; -import lombok.AllArgsConstructor; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; import org.springframework.security.oauth2.provider.token.ConsumerTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.validation.BindException; @@ -27,9 +33,12 @@ import cn.seqdata.oauth2.params.UserAccountParam; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.service.AuthorityService; +import cn.seqdata.oauth2.service.DefaultTokenServices; +import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; import cn.seqdata.syslog.Syslog; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -47,8 +56,11 @@ public class CurrentController { private final UserRepo userRepo; private final TokenStore tokenStore; private final UserService userService; - private final ConsumerTokenServices tokenServices; + private final ConsumerTokenServices consumerTokenServices; private final AuthorityService authorityService; + private final TokenService tokenService; + + private final AuthorizationServerTokenServices tokenServices; @ApiOperation("通行证") @GetMapping("/principal") @@ -76,6 +88,52 @@ public class CurrentController { return tokenStore.readAccessToken(SecurityUtils.accessToken(principal)); } + @ApiOperation("刷新token对应的角色关系") + @GetMapping("/token/refresh") + public void tokenRefresh(Principal principal) { + String grantType = SecurityUtils.grantType(principal); + String username = SecurityUtils.username(principal); + Collection newAuthorities = tokenService.loadAuthorities(grantType, username); + OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) principal; + + // 重新构造OAuth2Authentication + HashSet origAuthorities = (HashSet) oAuth2Authentication.getOAuth2Request() + .getAuthorities(); + origAuthorities.clear(); + origAuthorities.addAll(newAuthorities); + + Authentication userAuthentication = oAuth2Authentication.getUserAuthentication(); + if(userAuthentication != null) { + if(userAuthentication instanceof OAuth2AuthenticationToken) { + userAuthentication = + new OAuth2AuthenticationToken(((OAuth2AuthenticationToken) userAuthentication).getPrincipal(), + newAuthorities, ((OAuth2AuthenticationToken) userAuthentication).getAuthorizedClientRegistrationId()); + } else { + Authentication finalUserAuthentication = userAuthentication; + userAuthentication = new AbstractAuthenticationToken(newAuthorities) { + private static final long serialVersionUID = -1786107511865438515L; + + @Override + public Object getCredentials() { + return finalUserAuthentication.getCredentials(); + } + + @Override + public Object getPrincipal() { + return finalUserAuthentication.getPrincipal(); + } + + @Override + public boolean isAuthenticated() { + return true; + } + }; + } + } + OAuth2Authentication newOAuth2Authentication = new OAuth2Authentication(oAuth2Authentication.getOAuth2Request(), userAuthentication); + ((DefaultTokenServices) tokenServices).refreshAccessTokenAuthorities(oAuth2Authentication, newOAuth2Authentication); + } + @ApiOperation("角色") @GetMapping("/authorities") public Collection authorities(Principal principal) { @@ -86,7 +144,7 @@ public class CurrentController { @ApiOperation("注销") @DeleteMapping public void logout(Principal principal) { - tokenServices.revokeToken(SecurityUtils.accessToken(principal)); + consumerTokenServices.revokeToken(SecurityUtils.accessToken(principal)); } @Syslog diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java index 51c2ca6..a2fd6ce 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/DefaultTokenServices.java @@ -2,7 +2,6 @@ package cn.seqdata.oauth2.service; import java.util.Date; import java.util.Objects; -import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.RandomStringUtils; @@ -16,6 +15,8 @@ import org.springframework.security.oauth2.provider.token.AuthorizationServerTok import org.springframework.security.oauth2.provider.token.TokenEnhancer; import org.springframework.security.oauth2.provider.token.TokenStore; +import lombok.RequiredArgsConstructor; + /** * @author jrxian * @date 2020-11-23 16:49 @@ -67,6 +68,13 @@ public class DefaultTokenServices implements AuthorizationServerTokenServices { return accessToken; } + public void refreshAccessTokenAuthorities(OAuth2Authentication oldAuthentication, + OAuth2Authentication newAuthentication) { + OAuth2AccessToken origAccessToken = getAccessToken(oldAuthentication); + // 设置origAccessToken部分参数更新,增强等,未做 + tokenStore.storeAccessToken(origAccessToken, newAuthentication); + } + @Override public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException { if(!supportRefreshToken) { -- Gitee From ca0399c7467b70caf28a8b1a25779754b24a1ff6 Mon Sep 17 00:00:00 2001 From: zl1995 Date: Fri, 13 Aug 2021 16:33:07 +0800 Subject: [PATCH 199/228] =?UTF-8?q?=E6=96=B0=E5=A2=9Etoken=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E6=8E=A5=E5=8F=A3=EF=BC=8Ctoken=E4=B8=8D=E5=8F=98?= =?UTF-8?q?=EF=BC=8C=E6=9D=83=E9=99=90=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/CurrentController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index 9853f6b..b931029 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -88,6 +88,7 @@ public class CurrentController { return tokenStore.readAccessToken(SecurityUtils.accessToken(principal)); } + @PreAuthorize("permitAll()") @ApiOperation("刷新token对应的角色关系") @GetMapping("/token/refresh") public void tokenRefresh(Principal principal) { -- Gitee From f9edee0df176b467bb54cb0f07f27d83d3e2152f Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Fri, 10 Sep 2021 10:45:42 +0800 Subject: [PATCH 200/228] =?UTF-8?q?=E7=A7=81=E9=92=A5key=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=EF=BC=8C=E5=80=BC=E4=B8=BA=E7=A9=BA=E5=AF=BC=E8=87=B4=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/AuthzServerConfiguration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java index 614a8fb..4789a91 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/AuthzServerConfiguration.java @@ -1,8 +1,7 @@ package cn.seqdata.oauth2; -import lombok.AllArgsConstructor; - import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.StringRedisTemplate; @@ -26,6 +25,7 @@ import cn.seqdata.oauth2.service.DefaultTokenEnhancer; import cn.seqdata.oauth2.service.DefaultTokenServices; import cn.seqdata.oauth2.service.RedisTokenStore; import cn.seqdata.oauth2.service.UserService; +import lombok.AllArgsConstructor; /** * @author jrxian @@ -45,7 +45,7 @@ public class AuthzServerConfiguration { ValueOperations opsForValue = redisTemplate.opsForValue(); - if(BooleanUtils.isTrue(redisTemplate.hasKey(PRIVATE_KEY))) { + if(BooleanUtils.isTrue(redisTemplate.hasKey(PRIVATE_KEY)) && StringUtils.isNotBlank(opsForValue.get(PRIVATE_KEY))) { keyExchanger.setPrivateKey(opsForValue.get(PRIVATE_KEY)); keyExchanger.setPublicKey(opsForValue.get(PUBLIC_KEY)); } else { -- Gitee From 72c66e8cfc2691478f5b8f74458bad937d246680 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Wed, 29 Dec 2021 09:25:15 +0800 Subject: [PATCH 201/228] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/controller/OAuth2CodeController.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index c6646aa..03bb0f3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -1,7 +1,5 @@ package cn.seqdata.oauth2.controller; -import lombok.AllArgsConstructor; - import io.swagger.annotations.ApiOperation; import org.springframework.http.HttpStatus; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -20,6 +18,7 @@ import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.provider.UserProfile; import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -51,7 +50,7 @@ public class OAuth2CodeController { //这里的 state 是 accessToken OAuth2Authentication authentication = tokenStore.readAuthentication(state); userService.updateUser(registrationId, profile, authentication); - + // 注意,此处将registrationId作为clientId return tokenService.createToken(registrationId, remoteUser); } -- Gitee From bf516fc45e64fe03b94bcc88261ee1e107bc4b39 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Thu, 13 Jan 2022 16:28:34 +0800 Subject: [PATCH 202/228] =?UTF-8?q?=E6=B5=8B=E8=AF=95=EF=BC=9A=E6=89=AB?= =?UTF-8?q?=E7=A0=81=E6=8E=88=E6=9D=83=E7=AD=89=E7=94=A8=E6=88=B7=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E8=87=AA=E5=8A=A8=E5=88=9B=E5=BB=BA=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/CurrentController.java | 7 +++++++ .../java/cn/seqdata/oauth2/service/UserService.java | 13 +++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java index b931029..241d0c9 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/CurrentController.java @@ -68,6 +68,13 @@ public class CurrentController { return principal; } + @PreAuthorize("permitAll()") + @ApiOperation("token换取通行证") + @GetMapping("/token/{token}/principal") + public Principal principal(@PathVariable String token) { + return tokenStore.readAuthentication(token); + } + @ApiOperation("用户信息") @GetMapping("/user") public Optional info(Principal principal) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 38fe6b5..7ad557b 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -75,14 +75,11 @@ public class UserService { .ifPresent(account -> userDetail.setUserId(account.getId())); } } - - //没有找到微信unionid-全新的用户 - if(Objects.isNull(userDetail.getUserId())) { - Long userId = saveAsUser(profile); - saveAsUserAccount(userId, profile); - userDetail.setUserId(userId); - } - + /* + * 没有找到微信unionid-全新的用户 + * @since 2022-01-13: 没有user时,不会创建全新的用户 + * 此时依据client_id和principal生成一个无用户的token. + */ userDetailRepo.save(userDetail); } } -- Gitee From ff798788be2b2d12e57d337ece5276f49170f1f8 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Thu, 13 Jan 2022 16:38:55 +0800 Subject: [PATCH 203/228] =?UTF-8?q?=E5=9B=9E=E6=BB=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/service/UserService.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 7ad557b..38fe6b5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -75,11 +75,14 @@ public class UserService { .ifPresent(account -> userDetail.setUserId(account.getId())); } } - /* - * 没有找到微信unionid-全新的用户 - * @since 2022-01-13: 没有user时,不会创建全新的用户 - * 此时依据client_id和principal生成一个无用户的token. - */ + + //没有找到微信unionid-全新的用户 + if(Objects.isNull(userDetail.getUserId())) { + Long userId = saveAsUser(profile); + saveAsUserAccount(userId, profile); + userDetail.setUserId(userId); + } + userDetailRepo.save(userDetail); } } -- Gitee From 8633c6c4fa480ea1772be66bc80f48e620c8bbf9 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Fri, 14 Jan 2022 11:23:08 +0800 Subject: [PATCH 204/228] =?UTF-8?q?=E5=AF=86=E7=A0=81=E8=A7=A3=E5=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/ConnectController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 112c1d8..71b87d2 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -24,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import cn.seqdata.core.error.ErrorAttributes; +import cn.seqdata.crypto.exchanger.KeyExchanger; import cn.seqdata.oauth2.OAuth2Constants; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.rbac.UserAccount; @@ -49,6 +50,7 @@ public class ConnectController { private final PasswordEncoder passwordEncoder; private final UserDetailRepo userDetailRepo; private final List nonceHandlers; + private final KeyExchanger keyExchanger; @GetMapping public Map list(Principal principal) { @@ -111,10 +113,11 @@ public class ConnectController { UserAccount user = userService.loadUser(OAuth2Constants.PASSWORD, username) .orElseThrow(() -> new UsernameNotFoundException("用户名不存在")); + password = keyExchanger.decrypt(password); if(!passwordEncoder.matches(password, user.getPassword())) { throw new BadCredentialsException("用户名或密码不正确"); } - + userService.bindUser(Objects.requireNonNull(user.getId()), SecurityUtils.grantType(principal), SecurityUtils.username(principal)); -- Gitee From b4cdc80e0083e4ba7e2a8409d2762d9e3612f51c Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Fri, 14 Jan 2022 14:57:01 +0800 Subject: [PATCH 205/228] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=82=AE=E7=AE=B1?= =?UTF-8?q?=E5=8F=91=E9=80=81=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/NonceConfiguration.java | 10 ++++++---- .../oauth2/controller/ConnectController.java | 19 ++++++++++++++++--- .../oauth2/nonce/EmailNonceHandler.java | 10 ++++++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java index 0b48b89..632e95a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.mail.MailProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,13 +39,14 @@ public class NonceConfiguration { } @Bean - @ConditionalOnBean(JavaMailSender.class) - public NonceHandler emailNonceService(UserAccountRepo accountRepo, JavaMailSender mailSender, StringRedisTemplate redisTemplate) { - return new EmailNonceHandler(accountRepo, mailSender, redisTemplate); + @ConditionalOnProperty(value = "spring.mail.host") + public NonceHandler emailNonceService(UserAccountRepo accountRepo, JavaMailSender mailSender, + MailProperties mailProperties, StringRedisTemplate redisTemplate) { + return new EmailNonceHandler(accountRepo, mailSender, mailProperties, redisTemplate); } @Bean - @ConditionalOnMissingBean({AliyunSmsService.class, JavaMailSender.class}) + @ConditionalOnMissingBean(name = {"smsService", "emailNonceService"}) public NonceHandler anonymousMobileNonceHandler() { return new NonceHandler() { @Override diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 71b87d2..922cc30 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -14,6 +14,7 @@ import io.swagger.annotations.ApiOperation; import org.joda.time.DateTimeConstants; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -82,12 +83,18 @@ public class ConnectController { @RequestMapping(value = "/nonce", method = {RequestMethod.GET, RequestMethod.POST}) @PermitAll public ResponseEntity nonce(String username) { + boolean isSend = false; for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { nonceHandler.nonce(username, 2 * DateTimeConstants.SECONDS_PER_MINUTE); + isSend = true; } } - return ResponseEntity.ok("验证码已经发送,2分钟内输入有效!"); + if(isSend) { + return ResponseEntity.ok("验证码已经发送,2分钟内输入有效!"); + } else { + throw new AuthenticationServiceException("暂未实现验证码发送功能"); + } } /** @@ -98,12 +105,18 @@ public class ConnectController { @RequestMapping(value = "/v2/nonce", method = {RequestMethod.GET, RequestMethod.POST}) @PermitAll public ResponseEntity nonce2(String username) { + boolean isSend = false; for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { nonceHandler.nonceWithRedis(username, 2 * DateTimeConstants.SECONDS_PER_MINUTE); + isSend = true; } } - return ResponseEntity.ok("验证码已经发送,2分钟内输入有效!"); + if(isSend) { + return ResponseEntity.ok("验证码已经发送,2分钟内输入有效!"); + } else { + throw new AuthenticationServiceException("暂未实现验证码发送功能"); + } } @Syslog @@ -117,7 +130,7 @@ public class ConnectController { if(!passwordEncoder.matches(password, user.getPassword())) { throw new BadCredentialsException("用户名或密码不正确"); } - + userService.bindUser(Objects.requireNonNull(user.getId()), SecurityUtils.grantType(principal), SecurityUtils.username(principal)); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java index 96f7bcd..e7e2597 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java @@ -3,6 +3,7 @@ package cn.seqdata.oauth2.nonce; import java.util.Optional; import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.mail.MailProperties; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.mail.MailSender; import org.springframework.mail.SimpleMailMessage; @@ -16,10 +17,13 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; */ public class EmailNonceHandler extends NonceHandlerImpl { private final MailSender mailSender; + private final MailProperties mailProperties; - public EmailNonceHandler(UserAccountRepo accountRepo, MailSender mailSender, StringRedisTemplate redisTemplate) { + public EmailNonceHandler(UserAccountRepo accountRepo, MailSender mailSender, MailProperties mailProperties, + StringRedisTemplate redisTemplate) { super(accountRepo, redisTemplate); this.mailSender = mailSender; + this.mailProperties = mailProperties; } @Override @@ -35,8 +39,10 @@ public class EmailNonceHandler extends NonceHandlerImpl { @Override protected void sendNonce(String username, int nonce) { SimpleMailMessage mailMessage = new SimpleMailMessage(); + mailMessage.setFrom(mailProperties.getUsername()); mailMessage.setTo(username); - mailMessage.setSubject(String.format("验证码%06d", nonce)); + mailMessage.setSubject("获取验证码"); + mailMessage.setText(String.format("验证码: %06d", nonce)); mailSender.send(mailMessage); } } -- Gitee From db5274c40dedcb77723611c5d9af60ee2ba39ec0 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Fri, 14 Jan 2022 17:07:26 +0800 Subject: [PATCH 206/228] =?UTF-8?q?=E9=82=AE=E7=AE=B1=E8=A7=A3=E7=BB=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 922cc30..bccc6c1 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -23,6 +23,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.crypto.exchanger.KeyExchanger; @@ -33,6 +34,7 @@ import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.provider.OAuth2Provider; import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; import cn.seqdata.oauth2.util.SecurityUtils; import cn.seqdata.syslog.Syslog; @@ -49,6 +51,7 @@ public class ConnectController { private final ClientRegistrationRepository repository; private final UserService userService; private final PasswordEncoder passwordEncoder; + private final UserAccountRepo accountRepo; private final UserDetailRepo userDetailRepo; private final List nonceHandlers; private final KeyExchanger keyExchanger; @@ -189,6 +192,19 @@ public class ConnectController { userDetailRepo.deleteByIdClientAndUserId(registrationId, userId); } + @Syslog + @ApiOperation("邮箱解绑") + @Transactional + @DeleteMapping("/email") + public void unbindEmail(Principal principal) { + String grantType = SecurityUtils.grantType(principal); + String username = SecurityUtils.username(principal); + UserAccount userAccount = userService.loadUser(grantType, username) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED)); + userAccount.setEmail(null); + accountRepo.save(userAccount); + } + @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(AuthenticationException.class) public ErrorAttributes authenticationException(AuthenticationException ex) { -- Gitee From 0738a3125e2be7f3b2e1af0c5f8c0da23b0f12f8 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Mon, 17 Jan 2022 11:35:02 +0800 Subject: [PATCH 207/228] =?UTF-8?q?=E8=B4=A6=E6=88=B7=E4=B8=8D=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index 59a3906..3771bc3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -44,7 +44,7 @@ public abstract class NonceHandlerImpl implements NonceHandler { @Override public void nonce(String username, int expire) { UserAccount account = findUserAccount(username) - .orElseThrow(() -> new ServerWebInputException("无法用此账号登录")); + .orElseThrow(() -> new ServerWebInputException("账户不存在")); int nonce = RandomUtils.nextInt(100000, 999999); DateTime now = DateTime.now(); -- Gitee From 75a2ad5c923911efa37722cb978b227a63215df5 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Fri, 18 Feb 2022 15:06:27 +0800 Subject: [PATCH 208/228] =?UTF-8?q?=E6=96=B0=E5=A2=9Esyslog-clientId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/BizlogConfiguration.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index 9709c1c..76dc5fd 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -45,7 +45,9 @@ public class BizlogConfiguration { @Bean public ApplicationListener syslogAuthSuccessListener(CompositeSyslogConsumer consumer) { return event -> { - if(isBuiltInUser(event.getAuthentication())) return; + if(isBuiltInUser(event.getAuthentication())) { + return; + } SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event); consumer.accept(builder.build()); @@ -55,7 +57,9 @@ public class BizlogConfiguration { @Bean public ApplicationListener syslogAuthFailureListener(CompositeSyslogConsumer consumer) { return event -> { - if(isBuiltInUser(event.getAuthentication())) return; + if(isBuiltInUser(event.getAuthentication())) { + return; + } AuthenticationException exception = event.getException(); Class exceptionClass = exception.getClass(); @@ -69,7 +73,9 @@ public class BizlogConfiguration { @Bean public ApplicationListener syslogLogoutSuccessListener(CompositeSyslogConsumer consumer) { return event -> { - if(isBuiltInUser(event.getAuthentication())) return; + if(isBuiltInUser(event.getAuthentication())) { + return; + } SyslogRecord.SyslogRecordBuilder builder = logBuilder("logout", "注销", event); consumer.accept(builder.build()); @@ -106,6 +112,7 @@ public class BizlogConfiguration { .eventTime(event.getTimestamp()) .forwardIp(DefaultSyslogResolver.forwardAddr()) .sourceIp(DefaultSyslogResolver.remoteAddr()) + .clientId(DefaultSyslogResolver.clientId(event.getAuthentication())) .username(DefaultSyslogResolver.username(event.getAuthentication())); } } -- Gitee From d2f01fcdb6d62a33bd25c93449195ca01e7508be Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Mon, 21 Feb 2022 10:05:37 +0800 Subject: [PATCH 209/228] =?UTF-8?q?=E8=B4=A6=E6=88=B7=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=96=B9=E5=BC=8F=E6=B7=BB=E5=8A=A0clientId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/BizlogConfiguration.java | 18 +++++++++++++++++- .../nonce/AbstractPasswordTokenGranter.java | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index 76dc5fd..75df22f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -1,5 +1,8 @@ package cn.seqdata.oauth2; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.MessageConverter; @@ -112,7 +115,20 @@ public class BizlogConfiguration { .eventTime(event.getTimestamp()) .forwardIp(DefaultSyslogResolver.forwardAddr()) .sourceIp(DefaultSyslogResolver.remoteAddr()) - .clientId(DefaultSyslogResolver.clientId(event.getAuthentication())) + .clientId(clientId(event)) .username(DefaultSyslogResolver.username(event.getAuthentication())); } + + private String clientId(AbstractAuthenticationEvent event) { + String clientId = DefaultSyslogResolver.clientId(event.getAuthentication()); + if(StringUtils.isNotBlank(clientId)) { + return clientId; + } + Object details = event.getAuthentication() + .getDetails(); + if(details instanceof Map) { + return (String) ((Map) details).get("clientId"); + } + return null; + } } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java index 501a9f0..23deddc 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/AbstractPasswordTokenGranter.java @@ -31,6 +31,7 @@ public class AbstractPasswordTokenGranter extends AbstractTokenGranter { @Override protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters()); + parameters.put("clientId", client.getClientId()); String username = obtainUsername(tokenRequest); String password = obtainPassword(tokenRequest); -- Gitee From 2c025af344ab1b89220bd1895d9d027270eb94c2 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Mon, 21 Feb 2022 10:43:35 +0800 Subject: [PATCH 210/228] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug=EF=BC=9A=E9=A2=84?= =?UTF-8?q?=E6=9C=9F=E6=8B=BF=E6=9C=80=E6=96=B0=E7=9A=84token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/service/RedisTokenStore.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java index 604bb7d..e586315 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/RedisTokenStore.java @@ -100,7 +100,7 @@ public class RedisTokenStore implements TokenStore { ZSetOperations opsForZSet = redisTemplate.opsForZSet(); String key = USERNAME_TO_ACCESS + getApprovalKey(authentication); //找到最后一次生成的token - Set> tuples = opsForZSet.reverseRangeWithScores(key, 0, 1); + Set> tuples = opsForZSet.reverseRangeWithScores(key, 0, 0); ZSetOperations.TypedTuple tuple = CollectionUtils.lastElement(tuples); if(Objects.nonNull(tuple)) { return readAccessToken(tuple.getValue()); @@ -301,7 +301,9 @@ public class RedisTokenStore implements TokenStore { } private T fromJSON(String json, Class clazz) { - if(Objects.isNull(json)) return null; + if(Objects.isNull(json)) { + return null; + } try { return objectMapper.readValue(json, clazz); -- Gitee From e608b8d5e4abeb2590bf6b50146caadd34aea37b Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Tue, 22 Feb 2022 09:23:17 +0800 Subject: [PATCH 211/228] =?UTF-8?q?=E6=96=B0=E5=BB=BAtoken=E6=97=B6?= =?UTF-8?q?=E9=92=88=E5=AF=B9=E5=8C=BF=E5=90=8D=E7=94=A8=E6=88=B7=E9=87=8D?= =?UTF-8?q?=E8=AE=BE=E7=BD=AEAuthentication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/service/TokenService.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java index f1f9556..4b9af1c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/TokenService.java @@ -2,9 +2,10 @@ package cn.seqdata.oauth2.service; import java.util.Collection; import java.util.Collections; -import lombok.AllArgsConstructor; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -15,6 +16,7 @@ import org.springframework.stereotype.Service; import cn.seqdata.oauth2.DefaultRole; import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import lombok.AllArgsConstructor; /** * Author: jrxian @@ -31,7 +33,13 @@ public class TokenService { * 为第三方认证的用户创建本系统的 token */ public OAuth2AccessToken createToken(String clientId, OAuth2User principal) { - return tokenServices.createAccessToken(createOAuth2Authentication(clientId, principal)); + OAuth2Authentication authentication = createOAuth2Authentication(clientId, principal); + if(SecurityContextHolder.getContext() + .getAuthentication() instanceof AnonymousAuthenticationToken) { + SecurityContextHolder.getContext() + .setAuthentication(authentication); + } + return tokenServices.createAccessToken(authentication); } public OAuth2Authentication createOAuth2Authentication(String clientId, OAuth2User principal) { -- Gitee From d844e7140b07cd8cdb684aaff344dace576ea30a Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Fri, 25 Feb 2022 11:20:41 +0800 Subject: [PATCH 212/228] =?UTF-8?q?pc=E6=89=AB=E7=A0=81=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/OAuth2CodeController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java index 03bb0f3..38fb279 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/OAuth2CodeController.java @@ -18,6 +18,7 @@ import cn.seqdata.oauth2.provider.OAuth2Template; import cn.seqdata.oauth2.provider.UserProfile; import cn.seqdata.oauth2.service.TokenService; import cn.seqdata.oauth2.service.UserService; +import cn.seqdata.syslog.Syslog; import lombok.AllArgsConstructor; /** @@ -33,6 +34,7 @@ public class OAuth2CodeController { private final UserService userService; private final TokenService tokenService; + @Syslog @ApiOperation("authorization_code的回调函数,处理code和state") @GetMapping(value = "/{registrationId}", params = OAuth2ParameterNames.CODE) public OAuth2AccessToken bind(@PathVariable String registrationId, -- Gitee From 214a5e883e58292343b008477bf432f66ec02378 Mon Sep 17 00:00:00 2001 From: liang chenglong Date: Thu, 3 Mar 2022 17:40:17 +0800 Subject: [PATCH 213/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=9F=AD=E4=BF=A1/?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/ConnectController.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index bccc6c1..bef9561 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -122,6 +122,27 @@ public class ConnectController { } } + /** + * 手机邮箱验证码校验 + * + * @author liang chenglong + * @date 2022-03-03 + */ + @Syslog + @ApiOperation("验证码校验") + @RequestMapping(value = "/verify",method = {RequestMethod.GET, RequestMethod.POST}) + public ResponseEntity verify(String username, int nonce){ + try { + for(NonceHandler nonceHandler : nonceHandlers) { + if(nonceHandler.support(username)) + nonceHandler.check(username, nonce); + } + return ResponseEntity.ok("验证成功!"); + } catch(Exception e) { + throw new AuthenticationServiceException("验证失败,请填检查验证码是否正确!"); + } + } + @Syslog @ApiOperation("通过密码绑定") @GetMapping(value = "/password") -- Gitee From 32a69013bbcb923321e3cae83422b1941319e7c7 Mon Sep 17 00:00:00 2001 From: liang chenglong Date: Wed, 6 Apr 2022 14:44:50 +0800 Subject: [PATCH 214/228] =?UTF-8?q?=E4=BF=AE=E5=A4=8DrabbitMQ=E5=AE=95?= =?UTF-8?q?=E6=9C=BA=E6=97=A0=E6=B3=95=E7=99=BB=E9=99=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/BizlogConfiguration.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index 75df22f..e3575ed 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -23,11 +23,13 @@ import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.syslog.*; +import lombok.extern.slf4j.Slf4j; /** * @author jrxian * @date 2020/12/13 13:24 */ +@Slf4j @Configuration @EnableSyslog public class BizlogConfiguration { @@ -53,7 +55,7 @@ public class BizlogConfiguration { } SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event); - consumer.accept(builder.build()); + sendLog(consumer, builder.build()); }; } @@ -69,7 +71,7 @@ public class BizlogConfiguration { SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event) .errorCode(exceptionClass.getSimpleName()); - consumer.accept(builder.build()); + sendLog(consumer, builder.build()); }; } @@ -81,7 +83,7 @@ public class BizlogConfiguration { } SyslogRecord.SyslogRecordBuilder builder = logBuilder("logout", "注销", event); - consumer.accept(builder.build()); + sendLog(consumer, builder.build()); }; } @@ -131,4 +133,18 @@ public class BizlogConfiguration { } return null; } + + /** + * 捕捉RabbitMQ异常,避免客户无法登陆 + * + * @param consumer + * @param record + */ + private void sendLog(CompositeSyslogConsumer consumer, SyslogRecord record) { + try { + consumer.accept(record); + } catch(Exception ex) { + log.error("记录日志失败, ex:{}", ex.getMessage()); + } + } } -- Gitee From 1565dbd3f80d5e324faad94aaca2c5f97a332bc4 Mon Sep 17 00:00:00 2001 From: liang chenglong Date: Mon, 11 Apr 2022 15:55:25 +0800 Subject: [PATCH 215/228] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E6=8D=95=E6=8D=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/BizlogConfiguration.java | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java index e3575ed..75df22f 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/BizlogConfiguration.java @@ -23,13 +23,11 @@ import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.syslog.*; -import lombok.extern.slf4j.Slf4j; /** * @author jrxian * @date 2020/12/13 13:24 */ -@Slf4j @Configuration @EnableSyslog public class BizlogConfiguration { @@ -55,7 +53,7 @@ public class BizlogConfiguration { } SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event); - sendLog(consumer, builder.build()); + consumer.accept(builder.build()); }; } @@ -71,7 +69,7 @@ public class BizlogConfiguration { SyslogRecord.SyslogRecordBuilder builder = logBuilder("login", "登录", event) .errorCode(exceptionClass.getSimpleName()); - sendLog(consumer, builder.build()); + consumer.accept(builder.build()); }; } @@ -83,7 +81,7 @@ public class BizlogConfiguration { } SyslogRecord.SyslogRecordBuilder builder = logBuilder("logout", "注销", event); - sendLog(consumer, builder.build()); + consumer.accept(builder.build()); }; } @@ -133,18 +131,4 @@ public class BizlogConfiguration { } return null; } - - /** - * 捕捉RabbitMQ异常,避免客户无法登陆 - * - * @param consumer - * @param record - */ - private void sendLog(CompositeSyslogConsumer consumer, SyslogRecord record) { - try { - consumer.accept(record); - } catch(Exception ex) { - log.error("记录日志失败, ex:{}", ex.getMessage()); - } - } } -- Gitee From 3ae895878790494214afb767efcfa6c9d69e6822 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Fri, 29 Apr 2022 17:13:01 +0800 Subject: [PATCH 216/228] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8Fscheme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/WxAppController.java | 17 ++++++-- .../cn/seqdata/oauth2/wxapp/WxAppService.java | 41 +++++++++++++++---- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 92d963c..127d7ae 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -12,10 +12,7 @@ import org.springframework.lang.Nullable; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import com.fasterxml.jackson.databind.ObjectMapper; import cn.seqdata.oauth2.DefaultRole; @@ -27,6 +24,7 @@ import cn.seqdata.oauth2.wechat.WechatUserProfile; import cn.seqdata.oauth2.wxapp.WxAppService; import cn.seqdata.syslog.Syslog; import cn.seqdata.wxapp.message.Code2SessionResponse; +import cn.seqdata.wxapp.message.SchemeRequest; import cn.seqdata.wxapp.pojo.PhoneInfo; import cn.seqdata.wxapp.pojo.UserInfo; import cn.seqdata.wxapp.util.WxAppUtils; @@ -142,6 +140,17 @@ public class WxAppController { return wxappService.accessToken(id); } + @DeleteMapping("/token") + public void token(@PathVariable("id") String id, @RequestParam("token") String token) { + wxappService.invalidToken(id, token); + } + + @ApiOperation("获取小程序scheme码") + @PostMapping("scheme") + public String urlScheme(@PathVariable("id") String id, @RequestBody SchemeRequest request) { + return wxappService.scheme(id, request); + } + private Map attributes(String id, String code, String iv, String encryptedData) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index 06d3157..0327291 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -1,7 +1,9 @@ package cn.seqdata.oauth2.wxapp; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.TimeUnit; -import lombok.AllArgsConstructor; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; @@ -11,10 +13,8 @@ import com.google.common.cache.LoadingCache; import cn.seqdata.wxapp.WxAppException; import cn.seqdata.wxapp.WxAppFeignClient; -import cn.seqdata.wxapp.message.AccessTokenRequest; -import cn.seqdata.wxapp.message.AccessTokenResponse; -import cn.seqdata.wxapp.message.Code2SessionRequest; -import cn.seqdata.wxapp.message.Code2SessionResponse; +import cn.seqdata.wxapp.message.*; +import lombok.AllArgsConstructor; /** * @author jrxian @@ -26,7 +26,7 @@ public class WxAppService { private final WxAppProvider provider; private final WxAppFeignClient feignClient; //将微信access_token缓存到本地,每小时刷新一次 - private final LoadingCache cache = CacheBuilder.newBuilder() + private final LoadingCache tokenCache = CacheBuilder.newBuilder() .expireAfterWrite(1, TimeUnit.HOURS) .build(new CacheLoader() { @Override @@ -41,9 +41,36 @@ public class WxAppService { return response.getAccess_token(); } }); + private final LoadingCache, String> schemeCache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.HOURS) + .build(new CacheLoader, String>() { + @Override + public String load(@NonNull Map request) { + String appid = request.keySet() + .stream() + .findFirst() + .orElseThrow(() -> new RuntimeException("appid不存在")); + String token = tokenCache.getUnchecked(appid); + SchemeResponse response = feignClient.generateScheme(token, request.get(appid)); + WxAppException.check(response); + return response.getOpenlink(); + } + }); public String accessToken(String appid) { - return cache.getUnchecked(appid); + return tokenCache.getUnchecked(appid); + } + + public void invalidToken(String appid, String oldToken) { + String newToken = tokenCache.getUnchecked(appid); + if(!Objects.equals(oldToken, newToken)) { + return; + } + tokenCache.invalidate(appid); + } + + public String scheme(String appid, SchemeRequest request) { + return schemeCache.getUnchecked(Collections.singletonMap(appid, request)); } public Code2SessionResponse signin(String id, String code) { -- Gitee From dfd7fc719204178741cd240fe58f5ad8c06300f0 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Thu, 5 May 2022 17:39:46 +0800 Subject: [PATCH 217/228] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8Fticket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/WxAppController.java | 8 +++++++- .../cn/seqdata/oauth2/wxapp/WxAppService.java | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 127d7ae..44f0872 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -146,11 +146,17 @@ public class WxAppController { } @ApiOperation("获取小程序scheme码") - @PostMapping("scheme") + @PostMapping("/scheme") public String urlScheme(@PathVariable("id") String id, @RequestBody SchemeRequest request) { return wxappService.scheme(id, request); } + @ApiOperation("获取小程序ticket") + @GetMapping("/ticket") + public String ticket(@PathVariable("id") String id) { + return wxappService.ticket(id); + } + private Map attributes(String id, String code, String iv, String encryptedData) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index 0327291..61e9c6d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -56,6 +56,19 @@ public class WxAppService { return response.getOpenlink(); } }); + private final LoadingCache ticketCache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.HOURS) + .build(new CacheLoader() { + @Override + public String load(@NonNull String appid) { + String token = tokenCache.getUnchecked(appid); + TicketRequest request = new TicketRequest(); + request.setAccess_token(token); + TicketResponse response = feignClient.getTicket(request); + WxAppException.check(response); + return response.getTicket(); + } + }); public String accessToken(String appid) { return tokenCache.getUnchecked(appid); @@ -73,6 +86,10 @@ public class WxAppService { return schemeCache.getUnchecked(Collections.singletonMap(appid, request)); } + public String ticket(String appid) { + return ticketCache.getUnchecked(appid); + } + public Code2SessionResponse signin(String id, String code) { Code2SessionRequest request = new Code2SessionRequest(); ClientProperties clientProps = provider.apply(id); -- Gitee From c78add66351744050ff4cf675fa4690676ff8615 Mon Sep 17 00:00:00 2001 From: "zhanglin@voltmao.com" Date: Wed, 1 Jun 2022 14:05:48 +0800 Subject: [PATCH 218/228] =?UTF-8?q?=E6=96=B0=E5=A2=9Epost=20search?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 + seqdata-cloud-gateway/pom.xml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index dff1488..384f4aa 100644 --- a/pom.xml +++ b/pom.xml @@ -26,6 +26,7 @@ org.projectlombok lombok + 1.18.24 compile true diff --git a/seqdata-cloud-gateway/pom.xml b/seqdata-cloud-gateway/pom.xml index 46825c1..3b749b2 100644 --- a/seqdata-cloud-gateway/pom.xml +++ b/seqdata-cloud-gateway/pom.xml @@ -38,6 +38,10 @@ spring-boot-configuration-processor true + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + cn.seqdata.cloud -- Gitee From 263c2e5e8b6ab5a9a1bff2b37833b520601ca789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=9E=97?= Date: Fri, 15 Jul 2022 09:40:11 +0800 Subject: [PATCH 219/228] =?UTF-8?q?=E9=92=88=E5=AF=B9=E5=8C=BF=E5=90=8D?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E7=9A=84=E4=BD=BF=E7=94=A8=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/gateway/filter/authc/AllowlistPredicate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java index 65da015..c3940f6 100644 --- a/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java +++ b/seqdata-cloud-gateway/src/main/java/cn/seqdata/gateway/filter/authc/AllowlistPredicate.java @@ -20,7 +20,7 @@ import org.springframework.util.PathMatcher; public class AllowlistPredicate implements Predicate { protected static final PathMatcher pathMatcher = new AntPathMatcher(); private static final String[] defAllowlist = {"/authz/**" - , "/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/v2/api-docs/**"}; + , "/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/v2/api-docs/**", "/**/anonymous/**"}; public final Set allowlist = new HashSet<>(); public final Set anonymous = new HashSet<>(); -- Gitee From 9157d6605de61ea4a46fa48f91fb0ac697a46e7a Mon Sep 17 00:00:00 2001 From: wangwenrui <1058642006@qq.com> Date: Thu, 13 Oct 2022 18:29:48 +0800 Subject: [PATCH 220/228] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=20Url=5Flink=20=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oauth2/controller/WxAppController.java | 7 +++++++ .../cn/seqdata/oauth2/wxapp/WxAppService.java | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java index 44f0872..3ae9608 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/WxAppController.java @@ -25,6 +25,7 @@ import cn.seqdata.oauth2.wxapp.WxAppService; import cn.seqdata.syslog.Syslog; import cn.seqdata.wxapp.message.Code2SessionResponse; import cn.seqdata.wxapp.message.SchemeRequest; +import cn.seqdata.wxapp.message.UrlLinkRequest; import cn.seqdata.wxapp.pojo.PhoneInfo; import cn.seqdata.wxapp.pojo.UserInfo; import cn.seqdata.wxapp.util.WxAppUtils; @@ -157,6 +158,12 @@ public class WxAppController { return wxappService.ticket(id); } + @ApiOperation("获取小程序 url_link") + @PostMapping("/url/link") + public String urlLink(@PathVariable("id") String id, @RequestBody UrlLinkRequest request) { + return wxappService.urlLink(id, request); + } + private Map attributes(String id, String code, String iv, String encryptedData) throws IOException { Code2SessionResponse response = wxappService.signin(id, code); String decryptedData = WxAppUtils.decryptData(response.session_key, iv, encryptedData); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java index 61e9c6d..d7dc94c 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/wxapp/WxAppService.java @@ -70,6 +70,22 @@ public class WxAppService { } }); + private final LoadingCache, String> urlLinkCache = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.HOURS) + .build(new CacheLoader, String>() { + @Override + public String load(@NonNull Map request) { + String appid = request.keySet() + .stream() + .findFirst() + .orElseThrow(() -> new RuntimeException("appid不存在")); + String token = tokenCache.getUnchecked(appid); + UrlLinkResponse response = feignClient.generateUrlLink(token, request.get(appid)); + WxAppException.check(response); + return response.getUrl_link(); + } + }); + public String accessToken(String appid) { return tokenCache.getUnchecked(appid); } @@ -90,6 +106,10 @@ public class WxAppService { return ticketCache.getUnchecked(appid); } + public String urlLink(String appid, UrlLinkRequest request) { + return urlLinkCache.getUnchecked(Collections.singletonMap(appid, request)); + } + public Code2SessionResponse signin(String id, String code) { Code2SessionRequest request = new Code2SessionRequest(); ClientProperties clientProps = provider.apply(id); -- Gitee From 01741d8bcd412fa4ae0f20a599c974fde01c2a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=9E=97?= Date: Fri, 14 Oct 2022 10:22:46 +0800 Subject: [PATCH 221/228] =?UTF-8?q?registration=E6=96=B0=E5=A2=9E=EF=BC=9A?= =?UTF-8?q?=E6=96=B0=E7=94=A8=E6=88=B7=E6=97=B6=E5=88=86=E9=85=8D=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seqdata/oauth2/service/UserService.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 38fe6b5..04fe3c3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -1,27 +1,34 @@ package cn.seqdata.oauth2.service; import java.security.Principal; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; - +import org.springframework.util.CollectionUtils; import cn.seqdata.oauth2.OAuth2Constants; +import cn.seqdata.oauth2.jpa.oauth.RegistrationAuthorityId; import cn.seqdata.oauth2.jpa.oauth.UserDetail; import cn.seqdata.oauth2.jpa.oauth.UserDetailId; import cn.seqdata.oauth2.jpa.rbac.User; import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.jpa.rbac.UserAuthority; import cn.seqdata.oauth2.provider.UserProfile; +import cn.seqdata.oauth2.repos.oauth.RegistrationAuthorityRepo; import cn.seqdata.oauth2.repos.oauth.UserDetailRepo; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.repos.rbac.UserAuthorityRepo; import cn.seqdata.oauth2.repos.rbac.UserRepo; import cn.seqdata.oauth2.util.SecurityUtils; import cn.seqdata.oauth2.wechat.WechatUserProfile; + import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -36,6 +43,8 @@ public class UserService { private final UserAccountRepo accountRepo; private final UserRepo userRepo; private final UserDetailRepo userDetailRepo; + private final UserAuthorityRepo userAuthorityRepo; + private final RegistrationAuthorityRepo registrationAuthorityRepo; /** * 用户名密码登录:grantType='password' @@ -76,10 +85,11 @@ public class UserService { } } - //没有找到微信unionid-全新的用户 + //没有找到微信unionid-全新的用户+分配默认角色 if(Objects.isNull(userDetail.getUserId())) { Long userId = saveAsUser(profile); saveAsUserAccount(userId, profile); + saveDefaultAuthorities(userId, clientId); userDetail.setUserId(userId); } @@ -221,4 +231,17 @@ public class UserService { } accountRepo.save(account); } + + private void saveDefaultAuthorities(long user, String clientId) { + List userAuthorities = registrationAuthorityRepo.findByIdRegistration(clientId) + .stream() + .map(x -> { + RegistrationAuthorityId id = Objects.requireNonNull(x.getId()); + return new UserAuthority(user, id.getAuthority()); + }) + .collect(Collectors.toList()); + if(!CollectionUtils.isEmpty(userAuthorities)) { + userAuthorityRepo.saveAll(userAuthorities); + } + } } -- Gitee From b1d234bec0f799b2d6e1b1eb7b985ac27fb4bd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=9E=97?= Date: Wed, 19 Oct 2022 17:59:25 +0800 Subject: [PATCH 222/228] =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E4=B8=80=E4=BD=93=E5=8C=96+=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E8=A7=92=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/seqdata/oauth2/NonceConfiguration.java | 9 ++++--- .../oauth2/controller/ConnectController.java | 8 +++--- .../oauth2/nonce/EmailNonceHandler.java | 9 +++++-- .../oauth2/nonce/MobileNonceHandler.java | 20 ++++++++++++--- .../cn/seqdata/oauth2/nonce/NonceHandler.java | 2 +- .../oauth2/nonce/NonceHandlerImpl.java | 17 ++++++------- .../seqdata/oauth2/service/UserService.java | 25 ++++++++++--------- 7 files changed, 55 insertions(+), 35 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java index 632e95a..9484443 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/NonceConfiguration.java @@ -11,12 +11,12 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.core.AuthenticationException; - import cn.seqdata.oauth2.nonce.AliyunSmsService; import cn.seqdata.oauth2.nonce.EmailNonceHandler; import cn.seqdata.oauth2.nonce.MobileNonceHandler; import cn.seqdata.oauth2.nonce.NonceHandler; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.service.UserService; /** * Author: jrxian @@ -34,8 +34,9 @@ public class NonceConfiguration { @Bean @ConditionalOnBean(AliyunSmsService.class) - public NonceHandler mobileNonceService(UserAccountRepo accountRepo, AliyunSmsService smsService, StringRedisTemplate redisTemplate) { - return new MobileNonceHandler(accountRepo, smsService, redisTemplate); + public NonceHandler mobileNonceService(UserAccountRepo accountRepo, UserService userService, + AliyunSmsService smsService, StringRedisTemplate redisTemplate) { + return new MobileNonceHandler(accountRepo, userService, smsService, redisTemplate); } @Bean @@ -55,7 +56,7 @@ public class NonceConfiguration { } @Override - public void nonce(String mobile, int expire) { + public void nonce(String mobile, int expire, String clientId) { throw new AuthenticationServiceException("未实现验证码登录功能"); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index bef9561..57da1b4 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -24,7 +24,6 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; - import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.crypto.exchanger.KeyExchanger; import cn.seqdata.oauth2.OAuth2Constants; @@ -85,11 +84,11 @@ public class ConnectController { @ApiOperation("发送验证码") @RequestMapping(value = "/nonce", method = {RequestMethod.GET, RequestMethod.POST}) @PermitAll - public ResponseEntity nonce(String username) { + public ResponseEntity nonce(String username, @RequestParam(required = false) String clientId) { boolean isSend = false; for(NonceHandler nonceHandler : nonceHandlers) { if(nonceHandler.support(username)) { - nonceHandler.nonce(username, 2 * DateTimeConstants.SECONDS_PER_MINUTE); + nonceHandler.nonce(username, 2 * DateTimeConstants.SECONDS_PER_MINUTE, clientId); isSend = true; } } @@ -134,8 +133,9 @@ public class ConnectController { public ResponseEntity verify(String username, int nonce){ try { for(NonceHandler nonceHandler : nonceHandlers) { - if(nonceHandler.support(username)) + if(nonceHandler.support(username)) { nonceHandler.check(username, nonce); + } } return ResponseEntity.ok("验证成功!"); } catch(Exception e) { diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java index e7e2597..9621260 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/EmailNonceHandler.java @@ -7,7 +7,7 @@ import org.springframework.boot.autoconfigure.mail.MailProperties; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.mail.MailSender; import org.springframework.mail.SimpleMailMessage; - +import org.springframework.web.server.ServerWebInputException; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; @@ -32,10 +32,15 @@ public class EmailNonceHandler extends NonceHandlerImpl { } @Override - protected Optional findUserAccount(String username) { + protected Optional findAccount(String username) { return accountRepo.findByEmail(username); } + @Override + protected UserAccount saveAccount(String username, String clientId) { + throw new ServerWebInputException("账户不存在"); + } + @Override protected void sendNonce(String username, int nonce) { SimpleMailMessage mailMessage = new SimpleMailMessage(); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java index ca32b4e..5323ff6 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java @@ -1,12 +1,13 @@ package cn.seqdata.oauth2.nonce; +import java.util.Objects; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.StringRedisTemplate; - import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import cn.seqdata.oauth2.service.UserService; /** * @author jrxian @@ -14,10 +15,13 @@ import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; */ public class MobileNonceHandler extends NonceHandlerImpl { private final AliyunSmsService smsService; + private final UserService userService; - public MobileNonceHandler(UserAccountRepo accountRepo, AliyunSmsService smsService, StringRedisTemplate redisTemplate) { + public MobileNonceHandler(UserAccountRepo accountRepo, UserService userService, + AliyunSmsService smsService, StringRedisTemplate redisTemplate) { super(accountRepo, redisTemplate); this.smsService = smsService; + this.userService = userService; } @Override @@ -26,10 +30,20 @@ public class MobileNonceHandler extends NonceHandlerImpl { } @Override - protected Optional findUserAccount(String username) { + protected Optional findAccount(String username) { return accountRepo.findByMobile(username); } + @Override + protected UserAccount saveAccount(String username, String clientId) { + UserAccount user = new UserAccount(); + user.setMobile(username); + user = accountRepo.save(user); + // 分配默认角色 + userService.saveDefaultAuthorities(Objects.requireNonNull(user.getId()), clientId); + return user; + } + @Override protected void sendNonce(String username, int nonce) { smsService.accept(username, nonce); diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java index e61346b..0eb18c3 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandler.java @@ -10,7 +10,7 @@ public interface NonceHandler { /** * 生成一次性验证码 */ - void nonce(String username, int expire); + void nonce(String username, int expire, String clientId); /** * 生成一次性验证码-存入redis diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index 3771bc3..6d2ba7e 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -14,12 +14,10 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.www.NonceExpiredException; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.server.ServerWebInputException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; /** * @author jrxian @@ -31,7 +29,9 @@ public abstract class NonceHandlerImpl implements NonceHandler { private final ObjectMapper objectMapper = new ObjectMapper(); private static final String NONCE = "username_to_nonce:"; - protected abstract Optional findUserAccount(String username); + protected abstract Optional findAccount(String username); + + protected abstract UserAccount saveAccount(String username, String clientId); protected abstract void sendNonce(String username, int nonce); @@ -42,9 +42,8 @@ public abstract class NonceHandlerImpl implements NonceHandler { @Transactional @Override - public void nonce(String username, int expire) { - UserAccount account = findUserAccount(username) - .orElseThrow(() -> new ServerWebInputException("账户不存在")); + public void nonce(String username, int expire, String clientId) { + UserAccount account = findAccount(username).orElse(saveAccount(username, clientId)); int nonce = RandomUtils.nextInt(100000, 999999); DateTime now = DateTime.now(); @@ -89,7 +88,7 @@ public abstract class NonceHandlerImpl implements NonceHandler { } private RuntimeException checkNonceAccount(String username, int nonce) { - Optional accountOptional = findUserAccount(username); + Optional accountOptional = findAccount(username); if(!accountOptional.isPresent()) { return new UsernameNotFoundException("无法用此账号登录"); } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 04fe3c3..4f24e9d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -200,6 +200,19 @@ public class UserService { //TODO 设置角色-group-user(后端url权限) } + public void saveDefaultAuthorities(long user, String clientId) { + List userAuthorities = registrationAuthorityRepo.findByIdRegistration(clientId) + .stream() + .map(x -> { + RegistrationAuthorityId id = Objects.requireNonNull(x.getId()); + return new UserAuthority(user, id.getAuthority()); + }) + .collect(Collectors.toList()); + if(!CollectionUtils.isEmpty(userAuthorities)) { + userAuthorityRepo.saveAll(userAuthorities); + } + } + private Long saveAsUser(UserProfile profile) { User user = new User(); user.setName(profile.getNickname()); @@ -232,16 +245,4 @@ public class UserService { accountRepo.save(account); } - private void saveDefaultAuthorities(long user, String clientId) { - List userAuthorities = registrationAuthorityRepo.findByIdRegistration(clientId) - .stream() - .map(x -> { - RegistrationAuthorityId id = Objects.requireNonNull(x.getId()); - return new UserAuthority(user, id.getAuthority()); - }) - .collect(Collectors.toList()); - if(!CollectionUtils.isEmpty(userAuthorities)) { - userAuthorityRepo.saveAll(userAuthorities); - } - } } -- Gitee From c97ce6ad41b9e3a21d75dd99f14dbe3ccbf2f830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=9E=97?= Date: Tue, 25 Oct 2022 10:27:02 +0800 Subject: [PATCH 223/228] User --- .../java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java | 6 +++--- .../main/java/cn/seqdata/oauth2/service/UserService.java | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java index 5323ff6..b58a70a 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java @@ -1,6 +1,5 @@ package cn.seqdata.oauth2.nonce; -import java.util.Objects; import java.util.Optional; import org.apache.commons.lang3.StringUtils; @@ -36,11 +35,12 @@ public class MobileNonceHandler extends NonceHandlerImpl { @Override protected UserAccount saveAccount(String username, String clientId) { - UserAccount user = new UserAccount(); + long userId = userService.saveUser(username, null); + UserAccount user = new UserAccount(userId); user.setMobile(username); user = accountRepo.save(user); // 分配默认角色 - userService.saveDefaultAuthorities(Objects.requireNonNull(user.getId()), clientId); + userService.saveDefaultAuthorities(userId, clientId); return user; } diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java index 4f24e9d..fd60ecc 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/service/UserService.java @@ -87,7 +87,7 @@ public class UserService { //没有找到微信unionid-全新的用户+分配默认角色 if(Objects.isNull(userDetail.getUserId())) { - Long userId = saveAsUser(profile); + Long userId = saveUser(profile.getNickname(), profile.getAvatar()); saveAsUserAccount(userId, profile); saveDefaultAuthorities(userId, clientId); userDetail.setUserId(userId); @@ -213,10 +213,10 @@ public class UserService { } } - private Long saveAsUser(UserProfile profile) { + public Long saveUser(String name, String avatar) { User user = new User(); - user.setName(profile.getNickname()); - user.setAvatar(profile.getAvatar()); + user.setName(name); + user.setAvatar(avatar); User entity = userRepo.save(user); return entity.getId(); } -- Gitee From eeeaf5d211cd881c32b0e487eda007a0f3aaf376 Mon Sep 17 00:00:00 2001 From: wangwenrui <1058642006@qq.com> Date: Tue, 25 Oct 2022 10:39:15 +0800 Subject: [PATCH 224/228] =?UTF-8?q?=E6=89=8B=E6=9C=BA=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=BB=98=E8=AE=A4=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/controller/ConnectController.java | 2 ++ .../main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java index 57da1b4..3b8c65d 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/controller/ConnectController.java @@ -24,6 +24,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; + import cn.seqdata.core.error.ErrorAttributes; import cn.seqdata.crypto.exchanger.KeyExchanger; import cn.seqdata.oauth2.OAuth2Constants; @@ -102,6 +103,7 @@ public class ConnectController { /** * 向手机或邮箱发送一次性验证码 */ + @Deprecated @Syslog @ApiOperation("发送验证码") @RequestMapping(value = "/v2/nonce", method = {RequestMethod.GET, RequestMethod.POST}) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java index b58a70a..0a5f7b5 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java @@ -4,6 +4,7 @@ import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.StringRedisTemplate; + import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; @@ -35,7 +36,8 @@ public class MobileNonceHandler extends NonceHandlerImpl { @Override protected UserAccount saveAccount(String username, String clientId) { - long userId = userService.saveUser(username, null); + String name = String.format("手机用户%s", StringUtils.substring(username, username.length() - 4)); + long userId = userService.saveUser(name, null); UserAccount user = new UserAccount(userId); user.setMobile(username); user = accountRepo.save(user); -- Gitee From ade764575555d17b8ad614441520abee5651aa0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=9E=97?= Date: Tue, 25 Oct 2022 13:08:34 +0800 Subject: [PATCH 225/228] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=8B=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java | 4 ++-- .../main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java index 0a5f7b5..b7299c9 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/MobileNonceHandler.java @@ -4,7 +4,6 @@ import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.StringRedisTemplate; - import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import cn.seqdata.oauth2.service.UserService; @@ -38,7 +37,8 @@ public class MobileNonceHandler extends NonceHandlerImpl { protected UserAccount saveAccount(String username, String clientId) { String name = String.format("手机用户%s", StringUtils.substring(username, username.length() - 4)); long userId = userService.saveUser(name, null); - UserAccount user = new UserAccount(userId); + UserAccount user = accountRepo.findById(userId) + .orElse(new UserAccount(userId)); user.setMobile(username); user = accountRepo.save(user); // 分配默认角色 diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index 6d2ba7e..cc2fe22 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -13,6 +13,7 @@ import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.www.NonceExpiredException; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import cn.seqdata.oauth2.jpa.rbac.UserAccount; import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; @@ -40,7 +41,7 @@ public abstract class NonceHandlerImpl implements NonceHandler { this.redisTemplate = redisTemplate; } - @Transactional + @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) @Override public void nonce(String username, int expire, String clientId) { UserAccount account = findAccount(username).orElse(saveAccount(username, clientId)); -- Gitee From 227babb47893389dc59b44c88143bfd536156113 Mon Sep 17 00:00:00 2001 From: wangwenrui <1058642006@qq.com> Date: Tue, 25 Oct 2022 16:22:02 +0800 Subject: [PATCH 226/228] =?UTF-8?q?=E5=88=A4=E6=96=AD=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=98=AF=E5=90=A6=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java index cc2fe22..58c5cb0 100644 --- a/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java +++ b/seqdata-cloud-authz/src/main/java/cn/seqdata/oauth2/nonce/NonceHandlerImpl.java @@ -15,11 +15,12 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.www.NonceExpiredException; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import cn.seqdata.oauth2.jpa.rbac.UserAccount; -import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import cn.seqdata.oauth2.jpa.rbac.UserAccount; +import cn.seqdata.oauth2.repos.rbac.UserAccountRepo; + /** * @author jrxian * @date 2020-06-21 21:26 @@ -44,7 +45,7 @@ public abstract class NonceHandlerImpl implements NonceHandler { @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) @Override public void nonce(String username, int expire, String clientId) { - UserAccount account = findAccount(username).orElse(saveAccount(username, clientId)); + UserAccount account = findAccount(username).orElseGet(() -> saveAccount(username, clientId)); int nonce = RandomUtils.nextInt(100000, 999999); DateTime now = DateTime.now(); -- Gitee From 073296b3ed1e12f0014313847dbdd771d70dd154 Mon Sep 17 00:00:00 2001 From: DC <931256788@qq.com> Date: Mon, 30 Jan 2023 09:34:39 +0800 Subject: [PATCH 227/228] =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/messages_zh_CN.properties | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 seqdata-cloud-authz/src/main/resources/messages_zh_CN.properties diff --git a/seqdata-cloud-authz/src/main/resources/messages_zh_CN.properties b/seqdata-cloud-authz/src/main/resources/messages_zh_CN.properties new file mode 100644 index 0000000..0fcdf8f --- /dev/null +++ b/seqdata-cloud-authz/src/main/resources/messages_zh_CN.properties @@ -0,0 +1,47 @@ +AbstractAccessDecisionManager.accessDenied=\u4E0D\u5141\u8BB8\u8BBF\u95EE +AbstractLdapAuthenticationProvider.emptyPassword=\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef +AbstractSecurityInterceptor.authenticationNotFound=\u672A\u5728SecurityContext\u4E2D\u67E5\u627E\u5230\u8BA4\u8BC1\u5BF9\u8C61 +AbstractUserDetailsAuthenticationProvider.badCredentials=\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef +AbstractUserDetailsAuthenticationProvider.credentialsExpired=\u7528\u6237\u51ED\u8BC1\u5DF2\u8FC7\u671F +AbstractUserDetailsAuthenticationProvider.disabled=\u7528\u6237\u5DF2\u5931\u6548 +AbstractUserDetailsAuthenticationProvider.expired=\u7528\u6237\u5E10\u53F7\u5DF2\u8FC7\u671F +AbstractUserDetailsAuthenticationProvider.locked=\u7528\u6237\u5E10\u53F7\u5DF2\u88AB\u9501\u5B9A +AbstractUserDetailsAuthenticationProvider.onlySupports=\u4EC5\u4EC5\u652F\u6301UsernamePasswordAuthenticationToken +AccountStatusUserDetailsChecker.credentialsExpired=\u7528\u6237\u51ED\u8BC1\u5DF2\u8FC7\u671F +AccountStatusUserDetailsChecker.disabled=\u7528\u6237\u5DF2\u5931\u6548 +AccountStatusUserDetailsChecker.expired=\u7528\u6237\u5E10\u53F7\u5DF2\u8FC7\u671F +AccountStatusUserDetailsChecker.locked=\u7528\u6237\u5E10\u53F7\u5DF2\u88AB\u9501\u5B9A +AclEntryAfterInvocationProvider.noPermission=\u7ED9\u5B9A\u7684Authentication\u5BF9\u8C61({0})\u6839\u672C\u65E0\u6743\u64CD\u63A7\u9886\u57DF\u5BF9\u8C61({1}) +AnonymousAuthenticationProvider.incorrectKey=\u5C55\u793A\u7684AnonymousAuthenticationToken\u4E0D\u542B\u6709\u9884\u671F\u7684key +BindAuthenticator.badCredentials=\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef +BindAuthenticator.emptyPassword=\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef +CasAuthenticationProvider.incorrectKey=\u5C55\u793A\u7684CasAuthenticationToken\u4E0D\u542B\u6709\u9884\u671F\u7684key +CasAuthenticationProvider.noServiceTicket=\u672A\u80FD\u591F\u6B63\u786E\u63D0\u4F9B\u5F85\u9A8C\u8BC1\u7684CAS\u670D\u52A1\u7968\u6839 +ConcurrentSessionControlAuthenticationStrategy.exceededAllowed=\u5DF2\u7ECF\u8D85\u8FC7\u4E86\u5F53\u524D\u4E3B\u4F53({0})\u88AB\u5141\u8BB8\u7684\u6700\u5927\u4F1A\u8BDD\u6570\u91CF +DigestAuthenticationFilter.incorrectRealm=\u54CD\u5E94\u7ED3\u679C\u4E2D\u7684Realm\u540D\u5B57({0})\u540C\u7CFB\u7EDF\u6307\u5B9A\u7684Realm\u540D\u5B57({1})\u4E0D\u543B\u5408 +DigestAuthenticationFilter.incorrectResponse=\u9519\u8BEF\u7684\u54CD\u5E94\u7ED3\u679C +DigestAuthenticationFilter.missingAuth=\u9057\u6F0F\u4E86\u9488\u5BF9'auth' QOP\u7684\u3001\u5FC5\u987B\u7ED9\u5B9A\u7684\u6458\u8981\u53D6\u503C; \u63A5\u6536\u5230\u7684\u5934\u4FE1\u606F\u4E3A{0} +DigestAuthenticationFilter.missingMandatory=\u9057\u6F0F\u4E86\u5FC5\u987B\u7ED9\u5B9A\u7684\u6458\u8981\u53D6\u503C; \u63A5\u6536\u5230\u7684\u5934\u4FE1\u606F\u4E3A{0} +DigestAuthenticationFilter.nonceCompromised=Nonce\u4EE4\u724C\u5DF2\u7ECF\u5B58\u5728\u95EE\u9898\u4E86\uFF0C{0} +DigestAuthenticationFilter.nonceEncoding=Nonce\u672A\u7ECF\u8FC7Base64\u7F16\u7801; \u76F8\u5E94\u7684nonce\u53D6\u503C\u4E3A {0} +DigestAuthenticationFilter.nonceExpired=Nonce\u5DF2\u7ECF\u8FC7\u671F/\u8D85\u65F6 +DigestAuthenticationFilter.nonceNotNumeric=Nonce\u4EE4\u724C\u7684\u7B2C1\u90E8\u5206\u5E94\u8BE5\u662F\u6570\u5B57\uFF0C\u4F46\u7ED3\u679C\u5374\u662F{0} +DigestAuthenticationFilter.nonceNotTwoTokens=Nonce\u5E94\u8BE5\u7531\u4E24\u90E8\u5206\u53D6\u503C\u6784\u6210\uFF0C\u4F46\u7ED3\u679C\u5374\u662F{0} +DigestAuthenticationFilter.usernameNotFound=\u7528\u6237\u540D{0}\u672A\u627E\u5230 +JdbcDaoImpl.noAuthority=\u6CA1\u6709\u4E3A\u7528\u6237{0}\u6307\u5B9A\u89D2\u8272 +JdbcDaoImpl.notFound=\u672A\u627E\u5230\u7528\u6237{0} +LdapAuthenticationProvider.badCredentials=\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef +LdapAuthenticationProvider.credentialsExpired=\u7528\u6237\u51ED\u8BC1\u5DF2\u8FC7\u671F +LdapAuthenticationProvider.disabled=\u7528\u6237\u5DF2\u5931\u6548 +LdapAuthenticationProvider.expired=\u7528\u6237\u5E10\u53F7\u5DF2\u8FC7\u671F +LdapAuthenticationProvider.locked=\u7528\u6237\u5E10\u53F7\u5DF2\u88AB\u9501\u5B9A +LdapAuthenticationProvider.emptyUsername=\u7528\u6237\u540D\u4E0D\u5141\u8BB8\u4E3A\u7A7A +LdapAuthenticationProvider.onlySupports=\u4EC5\u4EC5\u652F\u6301UsernamePasswordAuthenticationToken +PasswordComparisonAuthenticator.badCredentials=\u8d26\u53f7\u6216\u5bc6\u7801\u9519\u8bef +#PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack. +ProviderManager.providerNotFound=\u672A\u67E5\u627E\u5230\u9488\u5BF9{0}\u7684AuthenticationProvider +RememberMeAuthenticationProvider.incorrectKey=\u5C55\u793ARememberMeAuthenticationToken\u4E0D\u542B\u6709\u9884\u671F\u7684key +RunAsImplAuthenticationProvider.incorrectKey=\u5C55\u793A\u7684RunAsUserToken\u4E0D\u542B\u6709\u9884\u671F\u7684key +SubjectDnX509PrincipalExtractor.noMatching=\u672A\u5728subjectDN\: {0}\u4E2D\u627E\u5230\u5339\u914D\u7684\u6A21\u5F0F +SwitchUserFilter.noCurrentUser=\u4E0D\u5B58\u5728\u5F53\u524D\u7528\u6237 +SwitchUserFilter.noOriginalAuthentication=\u4E0D\u80FD\u591F\u67E5\u627E\u5230\u539F\u5148\u7684\u5DF2\u8BA4\u8BC1\u5BF9\u8C61 -- Gitee From 0bc4619729bec1a8e34d3dfbdbd33baad7466c46 Mon Sep 17 00:00:00 2001 From: DC <931256788@qq.com> Date: Mon, 30 Jan 2023 09:58:35 +0800 Subject: [PATCH 228/228] =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 3 ++ .../main/resources/i18n/messages.properties | 47 +++++++++++++++++++ .../{ => i18n}/messages_zh_CN.properties | 0 3 files changed, 50 insertions(+) create mode 100644 seqdata-cloud-authz/src/main/resources/i18n/messages.properties rename seqdata-cloud-authz/src/main/resources/{ => i18n}/messages_zh_CN.properties (100%) diff --git a/seqdata-cloud-authz/src/main/resources/application.yml b/seqdata-cloud-authz/src/main/resources/application.yml index c082566..fff4e43 100644 --- a/seqdata-cloud-authz/src/main/resources/application.yml +++ b/seqdata-cloud-authz/src/main/resources/application.yml @@ -8,6 +8,9 @@ logging: io.swagger: error springfox.documentation: error spring: + messages: + basename: i18n/messages + fallback-to-system-locale: true application: name: authz profiles: diff --git a/seqdata-cloud-authz/src/main/resources/i18n/messages.properties b/seqdata-cloud-authz/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..665a742 --- /dev/null +++ b/seqdata-cloud-authz/src/main/resources/i18n/messages.properties @@ -0,0 +1,47 @@ +AbstractAccessDecisionManager.accessDenied=Access is denied +AbstractLdapAuthenticationProvider.emptyPassword=Empty Password +AbstractSecurityInterceptor.authenticationNotFound=An Authentication object was not found in the SecurityContext +AbstractUserDetailsAuthenticationProvider.badCredentials=Bad credentials +AbstractUserDetailsAuthenticationProvider.credentialsExpired=User credentials have expired +AbstractUserDetailsAuthenticationProvider.disabled=User is disabled +AbstractUserDetailsAuthenticationProvider.expired=User account has expired +AbstractUserDetailsAuthenticationProvider.locked=User account is locked +AbstractUserDetailsAuthenticationProvider.onlySupports=Only UsernamePasswordAuthenticationToken is supported +AccountStatusUserDetailsChecker.credentialsExpired=User credentials have expired +AccountStatusUserDetailsChecker.disabled=User is disabled +AccountStatusUserDetailsChecker.expired=User account has expired +AccountStatusUserDetailsChecker.locked=User account is locked +AclEntryAfterInvocationProvider.noPermission=Authentication {0} has NO permissions to the domain object {1} +AnonymousAuthenticationProvider.incorrectKey=The presented AnonymousAuthenticationToken does not contain the expected key +BindAuthenticator.badCredentials=Bad credentials +BindAuthenticator.emptyPassword=Empty Password +CasAuthenticationProvider.incorrectKey=The presented CasAuthenticationToken does not contain the expected key +CasAuthenticationProvider.noServiceTicket=Failed to provide a CAS service ticket to validate +ConcurrentSessionControlAuthenticationStrategy.exceededAllowed=Maximum sessions of {0} for this principal exceeded +DigestAuthenticationFilter.incorrectRealm=Response realm name {0} does not match system realm name of {1} +DigestAuthenticationFilter.incorrectResponse=Incorrect response +DigestAuthenticationFilter.missingAuth=Missing mandatory digest value for 'auth' QOP; received header {0} +DigestAuthenticationFilter.missingMandatory=Missing mandatory digest value; received header {0} +DigestAuthenticationFilter.nonceCompromised=Nonce token compromised {0} +DigestAuthenticationFilter.nonceEncoding=Nonce is not encoded in Base64; received nonce {0} +DigestAuthenticationFilter.nonceExpired=Nonce has expired/timed out +DigestAuthenticationFilter.nonceNotNumeric=Nonce token should have yielded a numeric first token, but was {0} +DigestAuthenticationFilter.nonceNotTwoTokens=Nonce should have yielded two tokens but was {0} +DigestAuthenticationFilter.usernameNotFound=Username {0} not found +JdbcDaoImpl.noAuthority=User {0} has no GrantedAuthority +JdbcDaoImpl.notFound=User {0} not found +LdapAuthenticationProvider.badCredentials=Bad credentials +LdapAuthenticationProvider.credentialsExpired=User credentials have expired +LdapAuthenticationProvider.disabled=User is disabled +LdapAuthenticationProvider.expired=User account has expired +LdapAuthenticationProvider.locked=User account is locked +LdapAuthenticationProvider.emptyUsername=Empty username not allowed +LdapAuthenticationProvider.onlySupports=Only UsernamePasswordAuthenticationToken is supported +PasswordComparisonAuthenticator.badCredentials=Bad credentials +PersistentTokenBasedRememberMeServices.cookieStolen=Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack. +ProviderManager.providerNotFound=No AuthenticationProvider found for {0} +RememberMeAuthenticationProvider.incorrectKey=The presented RememberMeAuthenticationToken does not contain the expected key +RunAsImplAuthenticationProvider.incorrectKey=The presented RunAsUserToken does not contain the expected key +SubjectDnX509PrincipalExtractor.noMatching=No matching pattern was found in subjectDN: {0} +SwitchUserFilter.noCurrentUser=No current user associated with this request +SwitchUserFilter.noOriginalAuthentication=Could not find original Authentication object diff --git a/seqdata-cloud-authz/src/main/resources/messages_zh_CN.properties b/seqdata-cloud-authz/src/main/resources/i18n/messages_zh_CN.properties similarity index 100% rename from seqdata-cloud-authz/src/main/resources/messages_zh_CN.properties rename to seqdata-cloud-authz/src/main/resources/i18n/messages_zh_CN.properties -- Gitee