From 7dc8fac2cfeb79dd81e2e18585694b5ec3514445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=B0=E8=89=BA=E9=B9=8F?= <18382071280@163.com> Date: Wed, 23 Aug 2023 16:30:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(*)=20=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4Au?= =?UTF-8?q?th=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + Auth/pom.xml | 153 +++++ .../cn/crtech/cloud/auth/AuthApplication.java | 36 ++ .../auth/component/JwtTokenEnhancer.java | 36 ++ .../cloud/auth/config/CrossInterceptor.java | 42 ++ .../cloud/auth/config/Oauth2ServerConfig.java | 176 ++++++ .../auth/config/RedisRepositoryConfig.java | 32 + .../config/UserAuthenticationProvider.java | 132 ++++ .../auth/config/UserByNameServiceWrapper.java | 41 ++ .../cloud/auth/config/UserTokenServices.java | 306 ++++++++++ .../cloud/auth/config/WebSecurityConfig.java | 69 +++ .../cloud/auth/config/WxMaConfiguration.java | 70 +++ .../cloud/auth/controller/AuthController.java | 59 ++ .../auth/controller/KeyPairController.java | 32 + .../auth/controller/WxMaUserController.java | 116 ++++ .../crtech/cloud/auth/dto/MisComRoleDto.java | 26 + .../cloud/auth/dto/MisComRoleUserDto.java | 29 + .../crtech/cloud/auth/dto/MisComUserDto.java | 65 ++ .../crtech/cloud/auth/dto/MisRoleApiDto.java | 51 ++ .../cloud/auth/launcher/NacosConfig.java | 51 ++ .../cloud/auth/mapper/AuthorizeMapper.java | 275 +++++++++ .../auth/mapper/WxMaAuthorizeMapper.java | 105 ++++ .../crtech/cloud/auth/pojo/AuthorizeUser.java | 50 ++ .../cn/crtech/cloud/auth/pojo/MisApp.java | 118 ++++ .../cn/crtech/cloud/auth/pojo/MisUser.java | 157 +++++ .../crtech/cloud/auth/pojo/Oauth2Token.java | 34 ++ .../cn/crtech/cloud/auth/pojo/RawDataDO.java | 20 + .../crtech/cloud/auth/pojo/RegisterUser.java | 17 + .../crtech/cloud/auth/pojo/SecurityUser.java | 115 ++++ .../java/cn/crtech/cloud/auth/pojo/User.java | 92 +++ .../cloud/auth/pojo/WechatLoginRequest.java | 16 + .../cloud/auth/pojo/WxAppletConfig.java | 55 ++ .../crtech/cloud/auth/pojo/WxAppletUser.java | 87 +++ .../crtech/cloud/auth/pojo/WxGzhConfig.java | 49 ++ .../cloud/auth/service/ResourceService.java | 198 ++++++ .../auth/service/system/AuthService.java | 143 +++++ .../auth/service/system/AuthUserService.java | 9 + .../wx/applet/WxAppletUserService.java | 9 + .../wx/applet/WxAppletUserServiceImpl.java | 132 ++++ .../service/wx/applet/WxMaUserService.java | 574 ++++++++++++++++++ .../service/wx/chat/WxChatUserService.java | 6 + .../wx/chat/WxChatUserServiceImpl.java | 131 ++++ .../crtech/cloud/auth/utils/TencentUtils.java | 65 ++ .../WxAppletAuthenticationFailureHandler.java | 17 + .../applet/WxAppletAuthenticationFilter.java | 71 +++ .../WxAppletAuthenticationProvider.java | 50 ++ .../WxAppletAuthenticationSuccessHandler.java | 66 ++ .../applet/WxAppletAuthenticationToken.java | 60 ++ .../WxAppletSecurityConfigurerConfig.java | 30 + .../WxChatAuthenticationFailureHandler.java | 17 + .../wx/chat/WxChatAuthenticationFilter.java | 59 ++ .../wx/chat/WxChatAuthenticationProvider.java | 44 ++ .../WxChatAuthenticationSuccessHandler.java | 66 ++ .../wx/chat/WxChatAuthenticationToken.java | 55 ++ .../chat/WxChatSecurityConfigurerConfig.java | 30 + Auth/src/main/resources/application-dev.yml | 28 + Auth/src/main/resources/bootstrap-dev.yml | 17 + Auth/src/main/resources/bootstrap.yml | 6 + Auth/src/main/resources/jwt.jks | Bin 0 -> 2200 bytes Auth/src/main/resources/logback.xml | 56 ++ 60 files changed, 4653 insertions(+) create mode 100644 Auth/pom.xml create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/AuthApplication.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/component/JwtTokenEnhancer.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/CrossInterceptor.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/Oauth2ServerConfig.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/RedisRepositoryConfig.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/UserAuthenticationProvider.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/UserByNameServiceWrapper.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/UserTokenServices.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/WebSecurityConfig.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/config/WxMaConfiguration.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/controller/AuthController.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/controller/KeyPairController.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/controller/WxMaUserController.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleDto.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleUserDto.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComUserDto.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/dto/MisRoleApiDto.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/launcher/NacosConfig.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/mapper/AuthorizeMapper.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/mapper/WxMaAuthorizeMapper.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/AuthorizeUser.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisApp.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisUser.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/Oauth2Token.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/RawDataDO.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/RegisterUser.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/SecurityUser.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/User.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/WechatLoginRequest.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletConfig.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletUser.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxGzhConfig.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/ResourceService.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthService.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthUserService.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserService.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserServiceImpl.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxMaUserService.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserService.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserServiceImpl.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/utils/TencentUtils.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFailureHandler.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFilter.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationProvider.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationSuccessHandler.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationToken.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletSecurityConfigurerConfig.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFailureHandler.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFilter.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationProvider.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationSuccessHandler.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationToken.java create mode 100644 Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatSecurityConfigurerConfig.java create mode 100644 Auth/src/main/resources/application-dev.yml create mode 100644 Auth/src/main/resources/bootstrap-dev.yml create mode 100644 Auth/src/main/resources/bootstrap.yml create mode 100644 Auth/src/main/resources/jwt.jks create mode 100644 Auth/src/main/resources/logback.xml diff --git a/.gitignore b/.gitignore index 1e33ce4..f5294db 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ /ResourceManager/target/ /WXEngine/WXEngine.iml /WXEngine/target/ +/Auth/src/main/resources/application-test.yml +/Auth/src/main/resources/bootstrap-test.yml diff --git a/Auth/pom.xml b/Auth/pom.xml new file mode 100644 index 0000000..3dcabfc --- /dev/null +++ b/Auth/pom.xml @@ -0,0 +1,153 @@ + + + 4.0.0 + + cn.crtech.cloud.auth + Auth + 1.0.1 + + + + cn.crtech.cloud.dependencies + Dependencies + 1.0.1 + + + + + + 8 + 8 + 1.8 + UTF-8 + UTF-8 + + 9.14 + 2.2.5.RELEASE + 1.0.1 + + 4.4.0 + 4.3.0 + 3.1.322 + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-data-redis + + + io.lettuce + lettuce-core + + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + commons-io + commons-io + + + + + + + com.nimbusds + nimbus-jose-jwt + ${nimbus.jwt.version} + + + + + org.springframework.cloud + spring-cloud-starter-oauth2 + ${oauth2.version} + + + bcpkix-jdk15on + org.bouncycastle + + + + + + + cn.crtech.cloud.common + Common + ${cn.crtech.cloud.common} + + + + + redis.clients + jedis + + + + + com.github.binarywang + weixin-java-miniapp + ${weixin-java-miniapp.version} + + + guava + com.google.guava + + + + + + + com.github.binarywang + weixin-java-mp + ${weixin.mp.version} + + + com.google.guava + guava + + + commons-io + commons-io + + + weixin-java-common + com.github.binarywang + + + + + + + com.tencentcloudapi + tencentcloud-sdk-java + ${tencentcloud.sdk.version} + + + com.google.code.gson + gson + + + okio + com.squareup.okio + + + + + diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/AuthApplication.java b/Auth/src/main/java/cn/crtech/cloud/auth/AuthApplication.java new file mode 100644 index 0000000..e0a4ac0 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/AuthApplication.java @@ -0,0 +1,36 @@ +package cn.crtech.cloud.auth; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import tk.mybatis.spring.annotation.MapperScan; + +/** + * Author : yj + * Date : 2021-01-13 + * Description: + */ + +//微服务启动时使用 begin +@SpringBootApplication +@EnableDiscoveryClient +@MapperScan("cn.crtech.cloud.auth.mapper") +public class AuthApplication { + public static void main(String[] args) { + SpringApplication.run(AuthApplication.class, args); + } +} +//微服务启动时使用 end + +//使用launcher启动时使用 begin +//launcher.NacosConfig @Component需要放开 + +//@SpringBootApplication +//public class AuthApplication extends SpringBootServletInitializer { +// @Override +// protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { +// return application.sources(AuthApplication.class); +// } +//} + +//使用launcher启动时使用 end diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/component/JwtTokenEnhancer.java b/Auth/src/main/java/cn/crtech/cloud/auth/component/JwtTokenEnhancer.java new file mode 100644 index 0000000..1eec258 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/component/JwtTokenEnhancer.java @@ -0,0 +1,36 @@ +package cn.crtech.cloud.auth.component; + +import cn.crtech.cloud.auth.pojo.SecurityUser; +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 java.util.HashMap; +import java.util.Map; + +/** + * JWT内容增强器 + * token信息的额外信息处理 + */ +@Component +public class JwtTokenEnhancer implements TokenEnhancer { + @Override + public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { + SecurityUser securityUser = (SecurityUser) authentication.getPrincipal(); + Map info = new HashMap<>(); + //把用户ID设置到JWT中 + info.put("id", securityUser.getId()); + info.put("company_code", securityUser.getCompanyCode()); + info.put("company_name", securityUser.getCompanyName()); + info.put("mobile", securityUser.getMobile()); + info.put("nick_name", securityUser.getNickName()); + info.put("email", securityUser.getEmail()); + info.put("company_admin", securityUser.getIsCompanyAdmin()); + info.put("data_source", securityUser.getDataSource()); + info.put("data_source_id", securityUser.getDataSourceId()); + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); + return accessToken; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/CrossInterceptor.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/CrossInterceptor.java new file mode 100644 index 0000000..463f89e --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/CrossInterceptor.java @@ -0,0 +1,42 @@ +package cn.crtech.cloud.auth.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.lang.Nullable; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 跨域拦截器,用于401处理 + *

+ * Author : yj + * Date : 2021-03-11 + * Description: + */ + +@Configuration +public class CrossInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String origin = request.getHeader(HttpHeaders.ORIGIN); + if (origin != null) { + response.setHeader("Access-Control-Allow-Origin", origin); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD"); + response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Access-Token"); + response.setHeader("Access-Control-Max-Age", "3600"); + } + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception { + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/Oauth2ServerConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/Oauth2ServerConfig.java new file mode 100644 index 0000000..97d528c --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/Oauth2ServerConfig.java @@ -0,0 +1,176 @@ +package cn.crtech.cloud.auth.config; + +import cn.crtech.cloud.auth.component.JwtTokenEnhancer; +import cn.crtech.cloud.auth.mapper.AuthorizeMapper; +import cn.crtech.cloud.auth.pojo.MisApp; +import cn.crtech.cloud.auth.service.system.AuthService; +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.config.annotation.builders.ClientDetailsServiceBuilder; +import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder; +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.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +import org.springframework.security.rsa.crypto.KeyStoreKeyFactory; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider; + +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Oauth2认证服务器配置 + * AuthorizationServer配置的适配类 + */ + +@AllArgsConstructor +@Configuration +@EnableAuthorizationServer//开启认证服务 +public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter { + @Autowired + AuthorizeMapper authorizeMapper; + + private final static int ACCESS_TOKEN_VALIDITY_SECONDS = 3600; + private final static int REFRESH_TOKEN_VALIDITY_SECONDS = 86400; + private final PasswordEncoder passwordEncoder; + private final AuthService authService; + private final AuthenticationManager authenticationManager; + private final JwtTokenEnhancer jwtTokenEnhancer; + + /** + * 配置前端应用认证 + * 配置从哪里获取ClientDetails信息 + * + * @param clients + * @throws Exception + */ + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + InMemoryClientDetailsServiceBuilder builder = clients.inMemory(); + List applicationList = authorizeMapper.getAll(); + + //将客户端信息存储在内存中 + applicationList.forEach(application -> { + ClientDetailsServiceBuilder.ClientBuilder clientBuilder = builder.withClient(application.getCode()) + .secret(passwordEncoder.encode(application.getClientSecret())) + .scopes(application.getScope())// 允许的授权范围 all + .authorizedGrantTypes(application.getAuthorizedGrantTypes().split(",")) //"password", "refresh_token" + .accessTokenValiditySeconds(application.getAccessTokenValidity() == null ? ACCESS_TOKEN_VALIDITY_SECONDS : application.getAccessTokenValidity()) + .refreshTokenValiditySeconds(application.getRefreshTokenValidity() == null ? REFRESH_TOKEN_VALIDITY_SECONDS : application.getRefreshTokenValidity()); + if (!StrUtil.isEmpty(application.getWebServerRedirectUri())) { + clientBuilder.redirectUris(application.getWebServerRedirectUri()); + } + if (!StrUtil.isEmpty(application.getResourceIds())) { + clientBuilder.resourceIds(application.getResourceIds()); + } + if (!StrUtil.isEmpty(application.getAuthorities())) { + clientBuilder.authorities(application.getAuthorities()); + } + if (!StrUtil.isEmpty(application.getAdditionalInformation())) { + clientBuilder.additionalInformation(application.getAdditionalInformation()); + } + if (!StrUtil.isEmpty(application.getAutoApprove())) { + clientBuilder.autoApprove(application.getAutoApprove()); + } + }); + } + + /** + * 注入相关配置: + * 1. 密码模式下配置认证管理器 AuthenticationManager + * 2. userDetailsService注入 + */ + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); + List delegates = new ArrayList<>(); + + delegates.add(jwtTokenEnhancer); + + delegates.add(accessTokenConverter()); + enhancerChain.setTokenEnhancers(delegates); //配置JWT的内容增强器 + + endpoints.authenticationManager(authenticationManager) + .userDetailsService(authService) //配置加载用户信息的服务 + .accessTokenConverter(accessTokenConverter()) + .tokenEnhancer(enhancerChain) + .tokenServices(customTokenServices(endpoints)); //配置token增强 + } + + public UserTokenServices customTokenServices(AuthorizationServerEndpointsConfigurer endpoints) { + UserTokenServices tokenServices = new UserTokenServices(); + tokenServices.setTokenStore(endpoints.getTokenStore()); + tokenServices.setSupportRefreshToken(true); + tokenServices.setReuseRefreshToken(true); + tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); + // 设置自定义的CustomUserDetailsByNameServiceWrapper + if (authService != null) { + PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); + provider.setPreAuthenticatedUserDetailsService(new UserByNameServiceWrapper<>(authService)); + tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider))); + } + return tokenServices; + } + + /** + * 配置:安全检查流程,用来配置令牌端点(Token Endpoint)的安全与权限访问 + * 默认过滤器:BasicAuthenticationFilter + * 1、oauth_client_details表中clientSecret字段加密【ClientDetails属性secret】 + * 2、CheckEndpoint类的接口 oauth/check_token 无需经过过滤器过滤,默认值:denyAll() + * 对以下的几个端点进行权限配置: + * /oauth/authorize:授权端点 + * /oauth/token:令牌端点 + * /oauth/confirm_access:用户确认授权提交端点 + * /oauth/error:授权服务错误信息端点 + * /oauth/check_token:用于资源服务访问的令牌解析端点 + * /oauth/token_key:提供公有密匙的端点,如果使用JWT令牌的话 + **/ + @Override + public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { +// security//客户端校验token访问许可 /oauth/check_key公开 +// .checkTokenAccess("permitAll()") +// //客户端token调用许可 /oauth/check_token公开 +// .tokenKeyAccess("permitAll()") +// //表单认证,申请令牌 +// .allowFormAuthenticationForClients() +// .passwordEncoder(new BCryptPasswordEncoder()); + security.allowFormAuthenticationForClients(); + } + + /** + * jwttoken转换器 + * + * @return + */ + @Bean + public JwtAccessTokenConverter accessTokenConverter() { + JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); + jwtAccessTokenConverter.setKeyPair(keyPair()); + return jwtAccessTokenConverter; + } + + /** + * 采用非对称加密 + * + * @return + */ + @Bean + public KeyPair keyPair() { + //从classpath下的证书中获取秘钥对 + KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); + return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray()); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/RedisRepositoryConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/RedisRepositoryConfig.java new file mode 100644 index 0000000..2450a52 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/RedisRepositoryConfig.java @@ -0,0 +1,32 @@ +package cn.crtech.cloud.auth.config; + +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.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * Redis相关配置 + */ +@Configuration +@EnableRedisRepositories +public class RedisRepositoryConfig { + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(connectionFactory); + + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + + redisTemplate.setKeySerializer(stringRedisSerializer); + redisTemplate.setHashKeySerializer(stringRedisSerializer); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/UserAuthenticationProvider.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/UserAuthenticationProvider.java new file mode 100644 index 0000000..8037425 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/UserAuthenticationProvider.java @@ -0,0 +1,132 @@ +package cn.crtech.cloud.auth.config; + +import cn.crtech.cloud.auth.service.system.AuthUserService; +import cn.hutool.core.lang.Assert; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsPasswordService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; + +import javax.annotation.Resource; +import java.util.Map; + +public class UserAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { + private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; + + private PasswordEncoder passwordEncoder; + + private volatile String userNotFoundEncodedPassword; + + private UserDetailsPasswordService userDetailsPasswordService; + + @Resource + private AuthUserService authUserService; + + public UserAuthenticationProvider() { + this.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()); + } + + @Override + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + if (authentication.getCredentials() == null) { + this.logger.debug("Authentication failed: no credentials provided"); + throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } else { + String presentedPassword = authentication.getCredentials().toString(); + if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { + this.logger.debug("Authentication failed: password does not match stored value"); + throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); + } + } + } + + protected void doAfterPropertiesSet() { + Assert.notNull(this.authUserService, "A UserDetailsService must be set"); + } + + @Override + protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + this.prepareTimingAttackProtection(); + // 自定义添加 + Map map = (Map) authentication.getDetails(); + try { + // 自定义添加 + UserDetails loadedUser = getAuthUserService().loadUserByUsername(username, map.get("companyCode")); + if (loadedUser == null) { + throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation"); + } else { + return loadedUser; + } + } catch (UsernameNotFoundException var4) { + this.mitigateAgainstTimingAttack(authentication); + throw var4; + } catch (InternalAuthenticationServiceException var5) { + throw var5; + } catch (Exception var6) { + throw new InternalAuthenticationServiceException(var6.getMessage(), var6); + } + } + + protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) { + boolean upgradeEncoding = this.userDetailsPasswordService != null && this.passwordEncoder.upgradeEncoding(user.getPassword()); + if (upgradeEncoding) { + String presentedPassword = authentication.getCredentials().toString(); + String newPassword = this.passwordEncoder.encode(presentedPassword); + user = this.userDetailsPasswordService.updatePassword(user, newPassword); + } + + return super.createSuccessAuthentication(principal, authentication, user); + } + + private void prepareTimingAttackProtection() { + if (this.userNotFoundEncodedPassword == null) { + this.userNotFoundEncodedPassword = this.passwordEncoder.encode("userNotFoundPassword"); + } + + } + + private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) { + if (authentication.getCredentials() != null) { + String presentedPassword = authentication.getCredentials().toString(); + this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword); + } + + } + + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); + this.passwordEncoder = passwordEncoder; + this.userNotFoundEncodedPassword = null; + } + + public String getUserNotFoundEncodedPassword() { + return userNotFoundEncodedPassword; + } + + public void setUserNotFoundEncodedPassword(String userNotFoundEncodedPassword) { + this.userNotFoundEncodedPassword = userNotFoundEncodedPassword; + } + + public UserDetailsPasswordService getUserDetailsPasswordService() { + return userDetailsPasswordService; + } + + public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) { + this.userDetailsPasswordService = userDetailsPasswordService; + } + + public AuthUserService getAuthUserService() { + return authUserService; + } + + public void setAuthUserService(AuthUserService authUserService) { + this.authUserService = authUserService; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/UserByNameServiceWrapper.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/UserByNameServiceWrapper.java new file mode 100644 index 0000000..05f78d1 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/UserByNameServiceWrapper.java @@ -0,0 +1,41 @@ +package cn.crtech.cloud.auth.config; + +import cn.crtech.cloud.auth.service.system.AuthService; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.util.Assert; + +import java.util.Map; + +public class UserByNameServiceWrapper implements AuthenticationUserDetailsService, InitializingBean { + private AuthService authService = null; + + public UserByNameServiceWrapper() { + } + + public UserByNameServiceWrapper(final AuthService authService) { + Assert.notNull(authService, "userDetailsService cannot be null."); + this.authService = authService; + } + + public void afterPropertiesSet() { + Assert.notNull(this.authService, "UserDetailsService must be set"); + } + + public UserDetails loadUserDetails(T authentication) throws UsernameNotFoundException { + // ----------添加自定义的内容---------- + AbstractAuthenticationToken principal = (AbstractAuthenticationToken) authentication.getPrincipal(); + // ----------添加自定义的内容---------- + Map map = (Map) principal.getDetails(); + return this.authService.loadUserByUsername(authentication.getName(), map.get("companyCode")); // 使用自定义的userDetailsService + } + + public void setUserDetailsService(AuthService authService) { + this.authService = authService; + } +} + diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/UserTokenServices.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/UserTokenServices.java new file mode 100644 index 0000000..67945ab --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/UserTokenServices.java @@ -0,0 +1,306 @@ +package cn.crtech.cloud.auth.config; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +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.InvalidScopeException; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.*; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import java.util.Date; +import java.util.Set; +import java.util.UUID; + +public class UserTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices, ConsumerTokenServices, InitializingBean { + private int refreshTokenValiditySeconds = 2592000; + private int accessTokenValiditySeconds = 43200; + private boolean supportRefreshToken = false; + private boolean reuseRefreshToken = true; + private TokenStore tokenStore; + private ClientDetailsService clientDetailsService; + private TokenEnhancer accessTokenEnhancer; + private AuthenticationManager authenticationManager; + + public UserTokenServices() { + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(this.tokenStore, "tokenStore must be set"); + } + + @Transactional + public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException { + OAuth2AccessToken existingAccessToken = this.tokenStore.getAccessToken(authentication); + OAuth2RefreshToken refreshToken = null; + if (existingAccessToken != null) { + if (!existingAccessToken.isExpired()) { + this.tokenStore.storeAccessToken(existingAccessToken, authentication); + return existingAccessToken; + } + + if (existingAccessToken.getRefreshToken() != null) { + refreshToken = existingAccessToken.getRefreshToken(); + this.tokenStore.removeRefreshToken(refreshToken); + } + + this.tokenStore.removeAccessToken(existingAccessToken); + } + + if (refreshToken == null) { + refreshToken = this.createRefreshToken(authentication); + } else if (refreshToken instanceof ExpiringOAuth2RefreshToken) { + ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken; + if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { + refreshToken = this.createRefreshToken(authentication); + } + } + + OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken); + this.tokenStore.storeAccessToken(accessToken, authentication); + refreshToken = accessToken.getRefreshToken(); + if (refreshToken != null) { + this.tokenStore.storeRefreshToken(refreshToken, authentication); + } + + return accessToken; + } + + @Transactional( + noRollbackFor = {InvalidTokenException.class, InvalidGrantException.class} + ) + public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) throws AuthenticationException { + if (!this.supportRefreshToken) { + throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); + } else { + OAuth2RefreshToken refreshToken = this.tokenStore.readRefreshToken(refreshTokenValue); + if (refreshToken == null) { + throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue); + } else { + OAuth2Authentication authentication = this.tokenStore.readAuthenticationForRefreshToken(refreshToken); + if (this.authenticationManager != null && !authentication.isClientOnly()) { + // OAuth2Authentication 中的 Authentication userAuthentication 丢失了 Detail的信息,需要补上 + // 1.从tokenRequest中获取请求的信息,并重新构造成 UsernamePasswordAuthenticationToken + // 2.设置好了Detail的信息再传入构造 PreAuthenticatedAuthenticationToken 交由后面的验证 + tokenRequest.getRequestParameters(); + Object details = tokenRequest.getRequestParameters(); + UsernamePasswordAuthenticationToken userAuthentication = (UsernamePasswordAuthenticationToken) authentication.getUserAuthentication(); + userAuthentication.setDetails(details); + // 去掉原来的,使用自己重新构造的 userAuthentication +// Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities()); + Authentication user = new PreAuthenticatedAuthenticationToken(userAuthentication, "", authentication.getAuthorities()); + user = this.authenticationManager.authenticate(user); + authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user); + authentication.setDetails(details); + } + + String clientId = authentication.getOAuth2Request().getClientId(); + if (clientId != null && clientId.equals(tokenRequest.getClientId())) { + this.tokenStore.removeAccessTokenUsingRefreshToken(refreshToken); + if (this.isExpired(refreshToken)) { + this.tokenStore.removeRefreshToken(refreshToken); + throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken); + } else { + authentication = this.createRefreshedAuthentication(authentication, tokenRequest); + if (!this.reuseRefreshToken) { + this.tokenStore.removeRefreshToken(refreshToken); + refreshToken = this.createRefreshToken(authentication); + } + + OAuth2AccessToken accessToken = this.createAccessToken(authentication, refreshToken); + this.tokenStore.storeAccessToken(accessToken, authentication); + if (!this.reuseRefreshToken) { + this.tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication); + } + + return accessToken; + } + } else { + throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue); + } + } + } + } + + public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { + return this.tokenStore.getAccessToken(authentication); + } + + private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) { + Set scope = request.getScope(); + OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request); + if (scope != null && !scope.isEmpty()) { + Set originalScope = clientAuth.getScope(); + if (originalScope == null || !originalScope.containsAll(scope)) { + throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope + ".", originalScope); + } + + clientAuth = clientAuth.narrowScope(scope); + } + + OAuth2Authentication narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication()); + return narrowed; + } + + protected boolean isExpired(OAuth2RefreshToken refreshToken) { + if (!(refreshToken instanceof ExpiringOAuth2RefreshToken)) { + return false; + } else { + ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken; + return expiringToken.getExpiration() == null || System.currentTimeMillis() > expiringToken.getExpiration().getTime(); + } + } + + public OAuth2AccessToken readAccessToken(String accessToken) { + return this.tokenStore.readAccessToken(accessToken); + } + + public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException, InvalidTokenException { + OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(accessTokenValue); + if (accessToken == null) { + throw new InvalidTokenException("Invalid access token: " + accessTokenValue); + } else if (accessToken.isExpired()) { + this.tokenStore.removeAccessToken(accessToken); + throw new InvalidTokenException("Access token expired: " + accessTokenValue); + } else { + OAuth2Authentication result = this.tokenStore.readAuthentication(accessToken); + if (result == null) { + throw new InvalidTokenException("Invalid access token: " + accessTokenValue); + } else { + if (this.clientDetailsService != null) { + String clientId = result.getOAuth2Request().getClientId(); + + try { + this.clientDetailsService.loadClientByClientId(clientId); + } catch (ClientRegistrationException var6) { + throw new InvalidTokenException("Client not valid: " + clientId, var6); + } + } + + return result; + } + } + } + + public String getClientId(String tokenValue) { + OAuth2Authentication authentication = this.tokenStore.readAuthentication(tokenValue); + if (authentication == null) { + throw new InvalidTokenException("Invalid access token: " + tokenValue); + } else { + OAuth2Request clientAuth = authentication.getOAuth2Request(); + if (clientAuth == null) { + throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue); + } else { + return clientAuth.getClientId(); + } + } + } + + public boolean revokeToken(String tokenValue) { + OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(tokenValue); + if (accessToken == null) { + return false; + } else { + if (accessToken.getRefreshToken() != null) { + this.tokenStore.removeRefreshToken(accessToken.getRefreshToken()); + } + + this.tokenStore.removeAccessToken(accessToken); + return true; + } + } + + private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) { + if (!this.isSupportRefreshToken(authentication.getOAuth2Request())) { + return null; + } else { + int validitySeconds = this.getRefreshTokenValiditySeconds(authentication.getOAuth2Request()); + String value = UUID.randomUUID().toString(); + return (OAuth2RefreshToken) (validitySeconds > 0 ? new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis() + (long) validitySeconds * 1000L)) : new DefaultOAuth2RefreshToken(value)); + } + } + + private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) { + DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); + int validitySeconds = this.getAccessTokenValiditySeconds(authentication.getOAuth2Request()); + if (validitySeconds > 0) { + token.setExpiration(new Date(System.currentTimeMillis() + (long) validitySeconds * 1000L)); + } + + token.setRefreshToken(refreshToken); + token.setScope(authentication.getOAuth2Request().getScope()); + return (OAuth2AccessToken) (this.accessTokenEnhancer != null ? this.accessTokenEnhancer.enhance(token, authentication) : token); + } + + protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) { + if (this.clientDetailsService != null) { + ClientDetails client = this.clientDetailsService.loadClientByClientId(clientAuth.getClientId()); + Integer validity = client.getAccessTokenValiditySeconds(); + if (validity != null) { + return validity; + } + } + + return this.accessTokenValiditySeconds; + } + + protected int getRefreshTokenValiditySeconds(OAuth2Request clientAuth) { + if (this.clientDetailsService != null) { + ClientDetails client = this.clientDetailsService.loadClientByClientId(clientAuth.getClientId()); + Integer validity = client.getRefreshTokenValiditySeconds(); + if (validity != null) { + return validity; + } + } + + return this.refreshTokenValiditySeconds; + } + + protected boolean isSupportRefreshToken(OAuth2Request clientAuth) { + if (this.clientDetailsService != null) { + ClientDetails client = this.clientDetailsService.loadClientByClientId(clientAuth.getClientId()); + return client.getAuthorizedGrantTypes().contains("refresh_token"); + } else { + return this.supportRefreshToken; + } + } + + public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) { + this.accessTokenEnhancer = accessTokenEnhancer; + } + + public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) { + this.refreshTokenValiditySeconds = refreshTokenValiditySeconds; + } + + public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) { + this.accessTokenValiditySeconds = accessTokenValiditySeconds; + } + + public void setSupportRefreshToken(boolean supportRefreshToken) { + this.supportRefreshToken = supportRefreshToken; + } + + public void setReuseRefreshToken(boolean reuseRefreshToken) { + this.reuseRefreshToken = reuseRefreshToken; + } + + public void setTokenStore(TokenStore tokenStore) { + this.tokenStore = tokenStore; + } + + public void setAuthenticationManager(AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public void setClientDetailsService(ClientDetailsService clientDetailsService) { + this.clientDetailsService = clientDetailsService; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/WebSecurityConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/WebSecurityConfig.java new file mode 100644 index 0000000..01caf3d --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/WebSecurityConfig.java @@ -0,0 +1,69 @@ +package cn.crtech.cloud.auth.config; + +import cn.crtech.cloud.auth.service.system.AuthService; +import cn.crtech.cloud.auth.wx.applet.WxAppletSecurityConfigurerConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * SpringSecurity配置 + */ + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + AuthService authService; + + @Autowired + private WxAppletSecurityConfigurerConfig wxAppletSecurityConfigurerConfig; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() + .antMatchers("/oauth/token").permitAll() + .antMatchers("/wx/**").permitAll() + .antMatchers("/oauth/initRedis").permitAll() + .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() + .antMatchers("/rsa/publicKey").permitAll() + .anyRequest().authenticated().and().httpBasic().and() + .apply(wxAppletSecurityConfigurerConfig).and() + .csrf().disable();// 关闭跨域 + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public UserAuthenticationProvider userAuthenticationProvider() { + UserAuthenticationProvider userAuthenticationProvider = new UserAuthenticationProvider(); + userAuthenticationProvider.setAuthUserService(authService); + userAuthenticationProvider.setHideUserNotFoundExceptions(false); + userAuthenticationProvider.setPasswordEncoder(passwordEncoder()); + return userAuthenticationProvider; + } + + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(userAuthenticationProvider()); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/config/WxMaConfiguration.java b/Auth/src/main/java/cn/crtech/cloud/auth/config/WxMaConfiguration.java new file mode 100644 index 0000000..06c80db --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/config/WxMaConfiguration.java @@ -0,0 +1,70 @@ +package cn.crtech.cloud.auth.config; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl; +import cn.crtech.cloud.auth.mapper.WxMaAuthorizeMapper; +import cn.crtech.cloud.auth.pojo.WxAppletConfig; +import cn.crtech.cloud.auth.pojo.WxGzhConfig; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxRuntimeException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Configuration +public class WxMaConfiguration { + + @Autowired + WxMaAuthorizeMapper wxMaConfigMapper; + + + @Bean + public WxMaService wxMaService() { + List configs = wxMaConfigMapper.getWxAppletConfigList(); + if (configs == null) { + throw new WxRuntimeException("小程序配置出错,添加下相关配置,注意别配错了!"); + } + WxMaService maService = new WxMaServiceImpl(); + maService.setMultiConfigs( + configs.stream() + .map(a -> { + WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl(); +// WxMaDefaultConfigImpl config = new WxMaRedisConfigImpl(new JedisPool()); + // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常 + config.setAppid(a.getAppId()); + config.setSecret(a.getAppSecret()); + config.setToken(a.getToken()); + config.setAesKey(a.getEncodingAesKey()); + config.setMsgDataFormat(a.getMsgDataFormat()); + return config; + }).collect(Collectors.toMap(WxMaDefaultConfigImpl::getAppid, a -> a, (o, n) -> o))); + return maService; + } + + @Bean + public WxMpService wxMpService() { + List configs = wxMaConfigMapper.getWxGzhConfigList(); + WxMpService wxMpService = new WxMpServiceImpl(); + wxMpService.setMultiConfigStorages( + configs.stream() + .map(a -> { + WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl(); + // 使用上面的配置时,需要同时引入jedis-lock的依赖,否则会报类无法找到的异常 + wxMpConfigStorage.setAppId(a.getAppId()); + wxMpConfigStorage.setSecret(a.getAppSecret()); + wxMpConfigStorage.setToken(a.getAppToken()); + wxMpConfigStorage.setAesKey(a.getAppAeskey()); + return wxMpConfigStorage; + }).collect(Collectors.toMap(WxMpDefaultConfigImpl::getAppId, a -> a, (o, n) -> o))); + return wxMpService; + } + +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/controller/AuthController.java b/Auth/src/main/java/cn/crtech/cloud/auth/controller/AuthController.java new file mode 100644 index 0000000..8d33f9a --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/controller/AuthController.java @@ -0,0 +1,59 @@ +package cn.crtech.cloud.auth.controller; + +import cn.crtech.cloud.auth.pojo.Oauth2Token; +import cn.crtech.cloud.auth.service.ResourceService; +import cn.crtech.cloud.common.api.CommonResult; +import cn.crtech.cloud.common.dto.Result; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; +import org.springframework.web.HttpRequestMethodNotSupportedException; +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 java.security.Principal; +import java.util.Map; + +/** + * 自定义Oauth2获取令牌接口 + */ + +@RestController +@RequestMapping("/oauth") +public class AuthController { + private TokenEndpoint tokenEndpoint; + private ResourceService resourceService; + + @Autowired + public AuthController(TokenEndpoint tokenEndpoint, ResourceService resourceService) { + this.tokenEndpoint = tokenEndpoint; + this.resourceService = resourceService; + } + + /** + * Oauth2登录认证 + */ + @PostMapping("/token") + public CommonResult postAccessToken(Principal principal, @RequestParam Map parameters) throws HttpRequestMethodNotSupportedException { + OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody(); + Oauth2Token oauth2TokenDto = Oauth2Token.builder() + .token(oAuth2AccessToken.getValue()) + .refreshToken(oAuth2AccessToken.getRefreshToken().getValue()) + .expiresIn(oAuth2AccessToken.getExpiresIn()) + .tokenHead("Bearer ").build(); + return CommonResult.success(oauth2TokenDto); + } + + /** + * 权限缓存内容更新 + * + * @return 返回操作结果 + */ + @PostMapping("/initRedis") + public Result initRedis() { + resourceService.initData(); + return Result.success(true); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/controller/KeyPairController.java b/Auth/src/main/java/cn/crtech/cloud/auth/controller/KeyPairController.java new file mode 100644 index 0000000..0e73a97 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/controller/KeyPairController.java @@ -0,0 +1,32 @@ +package cn.crtech.cloud.auth.controller; + +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.KeyPair; +import java.security.interfaces.RSAPublicKey; +import java.util.Map; + +/** + * 获取RSA公钥接口 + */ + +@RestController +public class KeyPairController { + private KeyPair keyPair; + + @Autowired + private void setKeyPair(KeyPair keyPair) { + this.keyPair = keyPair; + } + + @GetMapping("/rsa/publicKey") + public Map getKey() { + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + RSAKey key = new RSAKey.Builder(publicKey).build(); + return new JWKSet(key).toJSONObject(); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/controller/WxMaUserController.java b/Auth/src/main/java/cn/crtech/cloud/auth/controller/WxMaUserController.java new file mode 100644 index 0000000..9993f4e --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/controller/WxMaUserController.java @@ -0,0 +1,116 @@ +package cn.crtech.cloud.auth.controller; + +import cn.crtech.cloud.auth.pojo.RegisterUser; +import cn.crtech.cloud.auth.pojo.WechatLoginRequest; +import cn.crtech.cloud.auth.service.wx.applet.WxMaUserService; +import cn.crtech.cloud.common.dto.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 微信小程序用户接口 + */ + +@Slf4j +@RestController +@RequestMapping("/wx") +public class WxMaUserController { + private WxMaUserService wxMaUserService; + + @Autowired + public WxMaUserController(WxMaUserService wxMaUserService) { + this.wxMaUserService = wxMaUserService; + } + + /** + * 校验是否关注、绑定 + * + * @param wxAppletCode 微信小程序标识 + * @param code 参数标识 + * @param appId 公众号AppID + * @return 返回校验结果 + */ + @GetMapping("/checkUser") + public Result checkUser(String wxAppletCode, String code, String appId) { + return wxMaUserService.checkUser(wxAppletCode, code, appId); + } + + /** + * 获取微信用户对应绑定openID + * + * @param wxAppletCode 微信小程序标识 + * @param appId 公众号AppID + * @return 返回查询结果 + */ + @GetMapping("/getWxAppletOpenId") + public Result getWxAppletOpenId(String wxAppletCode, String appId) { + return wxMaUserService.getWxAppletOpenId(wxAppletCode, appId); + } + + /** + * 小程序绑定 + * + * @param wechatLoginRequest 微信请求参数信息 + * @return 返回结果 + */ + @PostMapping("/bindWxApplet") + public Result bindWxApplet(@RequestBody WechatLoginRequest wechatLoginRequest) { + return wxMaUserService.bindWxApplet(wechatLoginRequest); + } + + /** + * 实名认证 + * + * @param params 参数对象 + *

+ * @return 返回操作结果 + */ + @PostMapping("/userMobileVerify") + public Result userMobileVerify(@RequestBody Map params) { + return wxMaUserService.userMobileVerify(params); + } + + /** + * 微信用户登录 + * + * @param code 登录参数 + * @return 返回登录结果 + */ + @GetMapping("/login") + public Result login(String code) { + return wxMaUserService.login(code); + } + + /** + * 通过openId校验人员是否在该公司 + * + * @param wxAppletCode 小程序标识 + * @param code 登录标识 + * @param appId 小程序APPId + * @param companyCode 公司标识 + * @param companyName 公司名称 + * @return 返回校验结果 + */ + @GetMapping("/checkWxRegisterUser") + public Result checkWxRegisterUser(String wxAppletCode, String code, String appId, String companyCode, String companyName) { + return wxMaUserService.checkWxRegisterUser(wxAppletCode, code, appId, companyCode, companyName); + } + + /** + * 小程序 用户注册 + * + * @param registerUser 参数对象 + * @return 返回操作结果 + */ + @PostMapping("/registerCompanyUser") + public Result registerCompanyUser(@RequestBody RegisterUser registerUser) { + return wxMaUserService.registerCompanyUser(registerUser); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleDto.java b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleDto.java new file mode 100644 index 0000000..06d87ec --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleDto.java @@ -0,0 +1,26 @@ +package cn.crtech.cloud.auth.dto; + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import lombok.*; + +import java.io.Serializable; + +/** + * desc + * + * @author TYP + * @since 2023-08-08 10:20 + */ + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class MisComRoleDto implements Serializable { + @DataExportAnnotation("公司标识") + private String companyCode; + + @DataExportAnnotation("角色标识") + private String roleCode; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleUserDto.java b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleUserDto.java new file mode 100644 index 0000000..e98e46d --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComRoleUserDto.java @@ -0,0 +1,29 @@ +package cn.crtech.cloud.auth.dto; + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import lombok.*; + +import java.io.Serializable; + +/** + * 企业角色实体 + * + * @author TYP + * @since 2023-08-01 9:24 + */ + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class MisComRoleUserDto implements Serializable { + @DataExportAnnotation("所属公司标识") + private String companyCode; + + @DataExportAnnotation("角色ID") + private Integer roleId; + + @DataExportAnnotation("用户ID") + private Integer userId; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComUserDto.java b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComUserDto.java new file mode 100644 index 0000000..5aeaf20 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisComUserDto.java @@ -0,0 +1,65 @@ +package cn.crtech.cloud.auth.dto; + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import lombok.*; + +import java.io.Serializable; + +/** + * desc + * + * @author TYP + * @since 2023-08-08 10:20 + */ + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class MisComUserDto implements Serializable { + @DataExportAnnotation("公司标识") + private String companyCode; + + @DataExportAnnotation("公司名称") + private String companyName; + + @DataExportAnnotation("公司图标") + private String logo; + + @DataExportAnnotation("所在省地址") + private String provinceName; + + @DataExportAnnotation("所在市地址") + private String cityName; + + @DataExportAnnotation("所在区/县地址") + private String countyName; + + @DataExportAnnotation("详细地址") + private String address; + + @DataExportAnnotation("用户ID") + private Integer userId; + + @DataExportAnnotation("是否公司所有人") + private Boolean isOwner; + + @DataExportAnnotation("职位") + private String position; + + @DataExportAnnotation("是否默认公司") + private Integer isDefault; + + @DataExportAnnotation("状态") + private Integer state; + + @DataExportAnnotation("职员编号") + private String serialNo; + + @DataExportAnnotation("用户别名") + private String nickName; + + @DataExportAnnotation("用户手机号码") + private String mobile; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisRoleApiDto.java b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisRoleApiDto.java new file mode 100644 index 0000000..8b38834 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/dto/MisRoleApiDto.java @@ -0,0 +1,51 @@ +package cn.crtech.cloud.auth.dto; + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import lombok.*; + +import java.io.Serializable; +import java.util.List; + +/** + * 授权角色API权限实体DTO + * + * @author TYP + * @since 2023-08-09 11:15 + */ + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +public class MisRoleApiDto implements Serializable { + @DataExportAnnotation("对应产品系列ID") + private Integer seriesId; + + @DataExportAnnotation("对应产品系列标识") + private String seriesCode; + + @DataExportAnnotation("对应产品ID") + private Integer appId; + + @DataExportAnnotation("对应产品标识") + private String appCode; + + @DataExportAnnotation("对应产品版本ID") + private Integer versionId; + + @DataExportAnnotation("对应产品版本") + private String version; + + @DataExportAnnotation("对应产品菜单ID") + private Integer popedomId; + + @DataExportAnnotation("对应API路由") + private String controller; + + @DataExportAnnotation("对应角色名称") + private String role; + + @DataExportAnnotation("授权权限数据集合") + private List authorityList; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/launcher/NacosConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/launcher/NacosConfig.java new file mode 100644 index 0000000..b7338b4 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/launcher/NacosConfig.java @@ -0,0 +1,51 @@ +package cn.crtech.cloud.auth.launcher; + +import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration; +import com.alibaba.cloud.nacos.registry.NacosRegistration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; + +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.Query; +import javax.servlet.ServletContext; +import java.lang.management.ManagementFactory; +import java.util.Set; + +//使用launcher启动时使用 begin +//@Component +//使用launcher启动时使用 end +public class NacosConfig implements ApplicationRunner { + @Autowired + private ServletContext servletContext; + + @Autowired + private NacosRegistration registration; + + @Autowired(required = false) + private NacosAutoServiceRegistration nacosAutoServiceRegistration; + + public Integer getTomcatPort() throws MalformedObjectNameException { + MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer(); + Set objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"), Query.match(Query.attr("protocol"), Query.value("HTTP/1.1"))); + String port = objectNames.iterator().next().getKeyProperty("port"); + + return new Integer(port); + } + + @Override + public void run(ApplicationArguments args) throws Exception { + if (registration != null && servletContext != null) { + try { + Integer tomcatPort = getTomcatPort(); + registration.setPort(tomcatPort); + registration.getMetadata().put("management.context-path", servletContext.getContextPath()); + nacosAutoServiceRegistration.start(); + } catch (MalformedObjectNameException e) { + e.printStackTrace(); + } + } + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/mapper/AuthorizeMapper.java b/Auth/src/main/java/cn/crtech/cloud/auth/mapper/AuthorizeMapper.java new file mode 100644 index 0000000..31736f1 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/mapper/AuthorizeMapper.java @@ -0,0 +1,275 @@ +package cn.crtech.cloud.auth.mapper; + +import cn.crtech.cloud.auth.dto.MisComRoleUserDto; +import cn.crtech.cloud.auth.dto.MisComUserDto; +import cn.crtech.cloud.auth.dto.MisRoleApiDto; +import cn.crtech.cloud.auth.pojo.MisApp; +import cn.crtech.cloud.auth.pojo.MisUser; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + +@Repository +public interface AuthorizeMapper { + @Select("SELECT " + + "a.id , a.nick_name as nickName , a.mobile , a.email , a.avatar , a.password , a.name , " + + "a.id_card as idCard , a.id_card_prev as idCardPrev , a.id_card_next as idCardNext , " + + "a.reg_time as regTime , a.state, a.lastModifier , a.lastModifyTime , a.real_name as realName , " + + "a.is_system_admin as isSystemAdmin " + + "FROM mis_user a " + + "where a.mobile= #{mobile} and a.state = 1 ") + @ResultType(MisUser.class) + MisUser queryUserByMobile(@Param("mobile") String mobile); + + @Select("") + @ResultType(MisComUserDto.class) + List queryCompanyUserByUserId(Map params); + + @Select("select " + + "a.company_code as companyCode , a.role_id as roleId , a.user_id as userId " + + "from mis_com_role_user a " + + "where a.company_code = #{companyCode} and a.user_id = #{userId}") + @ResultType(MisComRoleUserDto.class) + List queryCompanyRoleUser(@Param("userId") int userId, + @Param("companyCode") String companyCode); + + @Select("select " + + "GROUP_CONCAT(b.role_code) " + + "from mis_com_role_user a " + + "left join mis_com_role b on a.role_id = b.id " + + "where a.company_code = #{companyCode} and a.user_id = #{userId} " + + "and b.state = 1 " + + "group by a.user_id ") + @ResultType(String.class) + String queryUserRole(@Param("userId") int userId, @Param("companyCode") String companyCode); + + @Select("select " + + "GROUP_CONCAT( DISTINCT CONCAT(d.code , '_' , c.code , '_' , b.code)) as roles " + + "from mis_app_defaultrole_user a " + + "inner join mis_app_defaultrole b on a.role_id = b.id " + + "inner join mis_app c on c.id = b.app_id " + + "inner join mis_app_series d on d.id = c.series_id " + + "where a.company_code = #{companyCode} and a.user_id = #{userId} " + + "and b.state = 1 and c.state = 1 and d.state = 1" + + "group by a.user_id") + @ResultType(String.class) + String queryUserDefaultRole(@Param("userId") int userId, @Param("companyCode") String companyCode); + + @Insert({"insert into mis_user " + + "(nick_name, mobile, email, avatar, password, name, state, real_name, reg_time) " + + "values " + + "(#{nickName}, #{mobile}, #{email}, #{avatar}, #{password}, #{name}, " + + " #{state}, #{realName}, #{regTime, jdbcType=TIMESTAMP})"}) + @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id") + int insertUser(MisUser user); + + @Update("update mis_user " + + "set " + + "real_name = #{realName}, " + + "name = #{name}, " + + "nick_name = #{nickName}, " + + "avatar = #{avatar}, " + + "email = #{email} " + + "where mobile = #{mobile}") + int updateRegisterUser(MisUser user); + + @Insert({"insert into mis_company_user " + + "(company_code, user_id, serial_no, is_default, state,is_owner) " + + "values " + + "(#{companyCode}, #{userId}, #{serialNo}, #{isDefault}, #{state}, #{isOwner})"}) + int insertCompanyUser(MisComUserDto companyUser); + + @Update("update mis_company_user " + + "set " + + "serial_no = #{serialNo}, " + + "state = #{state} " + + "where company_code = #{companyCode} and user_id=#{userId}") + int updateCompanyUser(MisComUserDto companyUser); + + @Update("update mis_user " + + "set id_card =#{idCard}, " + + "real_name = #{realName} " + + "where mobile = #{mobile}") + int updateUserIdCard(@Param("idCard") String idCard, @Param("realName") String realName, @Param("mobile") String mobile); + + @Select("select " + + "a.id , b.type , a.code , a.name , a.description , a.logo , a.state , a.sort , " + + "a.resource_ids as resourceIds, a.client_secret as clientSecret , a.scope , " + + "a.authorized_grant_types as authorizedGrantTypes , " + + "a.access_token_validity as accessTokenValidity, " + + "a.refresh_token_validity as refreshTokenValidity, " + + "a.web_server_redirect_uri as webServerRedirectUri, " + + "a.authorities , a.auto_approve as autoApprove " + + "from mis_app a " + + "inner join mis_app_series b on a.series_id = b.id " + + "where b.type = 1 and a.state = 1 and b.state = 1") + @ResultType(MisApp.class) + List getAll(); + + @Select("select " + + "CONCAT('/' ,b.code, a.authority_code) as controller , " + + "CONCAT(c.code ,'_' , b.code) as role " + + "from mis_app_popedom_authority a " + + "inner join mis_app b on b.id = a.app_id " + + "inner join mis_app_series c on c.id = b.series_id " + + "where a.state = 1 and b.state = 1 and c.state = 1 ") + @ResultType(MisRoleApiDto.class) + List listLimitApi(); + + @Select("select " + + "c.id as seriesId , c.code as seriesCode , b.code as appCode , b.id as appId , " + + "a.id as versionId , a.version " + + "from mis_app_grade a " + + "inner join mis_app b on b.id = a.app_id " + + "inner join mis_app_series c on c.id = b.series_id " + + "where a.need_price = 0 and a.state = 1 and b.state = 1 and c.state = 1 ") + @ResultType(MisRoleApiDto.class) + List listFreeApp(); + + @Select("select " + + "e.id as seriesId , e.code as seriesCode , c.code as appCode , c.id as appId , " + + "b.id as versionId , b.version , d.id as popedomId , " + + "case when d.route = '/' then '' else CONCAT('/' ,c.code, d.route,'/**') end as controller, " + + "'ALL' as role " + + "from mis_app_grade_popedom a " + + "inner join mis_app_grade b on a.grade_id = b.id and b.need_price = 0 " + + "inner join mis_app c on c.id = b.app_id " + + "inner join mis_app_popedom d on d.id = a.popedom_id " + + "inner join mis_app_series e on e.id = c.series_id " + + "where d.route <> '/' and d.route <> '#' " + + "and b.state = 1 and c.state = 1 and d.state = 1 and e.state = 1 ") + @ResultType(MisRoleApiDto.class) + List listFreeController(); + + @Select("select " + + "e.id as seriesId , e.code as seriesCode , c.code as appCode , c.id as appId , " + + "b.id as versionId , b.version , d.popedom_id as popedomId, " + + "CONCAT('/' ,c.code, d.authority_code) as controller , 'ALL' as role " + + "from mis_app_grade_popedom_authority a " + + "inner join mis_app_grade b on a.grade_id = b.id and b.need_price = 0 " + + "inner join mis_app c on c.id = b.app_id " + + "inner join mis_app_popedom_authority d on d.id = a.authority_id " + + "inner join mis_app_series e on e.id = c.series_id " + + "where b.state = 1 and c.state = 1 and d.state = 1 and e.state = 1 ") + @ResultType(MisRoleApiDto.class) + List listFreeAuthority(); + + @Select("select " + + "CONCAT('/',code , '/' , code ,'Feign/**') as controller , 'ALL' as role " + + "from mis_app " + + "where state = 1 ") + @ResultType(MisRoleApiDto.class) + List listAppFeignController(); + + @Select("select " + + "case when c.route = '/' then '' else CONCAT('/' ,d.code, c.route,'/**') end as controller, " + + "GROUP_CONCAT(DISTINCT concat( b.company_code, '_', b.id )) as role, " + + "e.id as seriesId , e.code as seriesCode , d.code as appCode , d.id as appId , " + + "c.id as popedomId " + + "from mis_com_role_popedom a " + + "inner join mis_com_role b on b.id = a.role_id " + + "inner join mis_app_popedom c on a.popedom_id = c.id " + + "inner join mis_app d on d.id = c.app_id " + + "inner join mis_app_series e on e.id = d.series_id " + + "where c.route <> '/' and c.route <> '#' " + + "and b.state = 1 and c.state = 1 and d.state = 1 and e.state = 1 " + + "GROUP BY c.route") + @ResultType(MisRoleApiDto.class) + List listRoleController(); + + @Select("select " + + "CONCAT('/' ,d.code, c.authority_code) as controller, " + + "GROUP_CONCAT(DISTINCT concat( b.company_code, '_', b.id )) as role, " + + "e.id as seriesId , e.code as seriesCode , d.code as appCode , d.id as appId , " + + "c.id as popedomId " + + "from mis_com_role_popedom_authority a " + + "inner join mis_com_role b on b.id = a.role_id " + + "inner join mis_app_popedom_authority c on a.authority_id = c.id " + + "inner join mis_app d on d.id = c.app_id " + + "inner join mis_app_series e on e.id = d.series_id " + + "where b.state = 1 and c.state = 1 and d.state = 1 and e.state = 1 " + + "GROUP BY c.authority_code") + @ResultType(MisRoleApiDto.class) + List listRoleAuthorityController(); + + @Select("select " + + "case when d.route = '/' then '' else CONCAT('/' ,e.code, d.route , '/**') end as controller, " + + "GROUP_CONCAT(DISTINCT concat( b.code, '_admin' )) as role, " + + "f.id as seriesId , f.code as seriesCode , e.code as appCode , e.id as appId , " + + "d.id as popedomId " + + "from mis_com_app a " + + "inner join mis_company b on a.company_id = b.id " + + "left join mis_com_app_popedom c on c.company_id = b.id and c.app_id = a.app_id " + + "inner join mis_app_popedom d on d.id = c.popedom_id " + + "inner join mis_app e on e.id = a.app_id " + + "inner join mis_app_series f on f.id = e.series_id " + + "where d.route <> '/' and d.route <> '#' and a.state = 1 and a.expire_date >= now() " + + "and d.state = 1 and e.state = 1 and f.state = 1 " + + "group by d.route") + @ResultType(MisRoleApiDto.class) + List getCompanyAdminController(); + + @Select("select " + + "CONCAT('/' ,e.code, d.authority_code ) as controller, " + + "GROUP_CONCAT(DISTINCT concat( b.code, '_admin' )) as role, " + + "f.id as seriesId , f.code as seriesCode , e.code as appCode , e.id as appId ,d.id as popedomId " + + "from mis_com_app a " + + "inner join mis_company b on a.company_id = b.id " + + "left join mis_com_app_popedom_authority c on c.company_id = b.id and c.app_id = a.app_id " + + "inner join mis_app_popedom_authority d on d.id = c.authority_id " + + "inner join mis_app e on e.id = a.app_id " + + "inner join mis_app_series f on f.id = e.series_id " + + "where a.state = 1 and a.expire_date >= now() and d.state = 1 and e.state = 1 and f.state = 1 " + + "group by d.authority_code") + @ResultType(MisRoleApiDto.class) + List getCompanyAdminAuthorityController(); + + @Select("select " + + "case when e.route = '/' then '' else CONCAT('/' ,b.code, e.route , '/**') end as controller, " + + "GROUP_CONCAT(DISTINCT concat(c.code , '_' , b.code , '_' , a.code)) as role, " + + "c.id as seriesId , c.code as seriesCode , b.code as appCode , b.id as appId , " + + "e.id as popedomId " + + "from mis_app_defaultrole a " + + "inner join mis_app b on a.app_id = b.id " + + "inner join mis_app_series c on c.id = b.series_id " + + "left join mis_app_defaultrole_popedom d on d.role_id = a.id " + + "inner join mis_app_popedom e on e.id = d.popedom_id " + + "where e.route <> '/' and e.route <> '#' and a.state = 1 and b.state = 1 " + + "and c.state = 1 and e.state = 1 " + + "group by e.route") + @ResultType(MisRoleApiDto.class) + List listDefaultRoleController(); + + @Select("select " + + "CONCAT('/' ,b.code, e.authority_code) as controller, " + + "GROUP_CONCAT(DISTINCT concat( c.code , '_' , b.code , '_' , a.code)) as role, " + + "c.id as seriesId , c.code as seriesCode , b.code as appCode , b.id as appId , " + + "e.id as popedomId " + + "from mis_app_defaultrole a " + + "inner join mis_app b on a.app_id = b.id " + + "inner join mis_app_series c on c.id = b.series_id " + + "left join mis_app_defaultrole_popedom_authority d on d.role_id = a.id " + + "inner join mis_app_popedom_authority e on e.id = d.authority_id " + + "where a.state = 1 and b.state = 1 and c.state = 1 and e.state = 1 " + + "group by e.authority_code") + @ResultType(MisRoleApiDto.class) + List listDefaultRoleAuthorityController(); + +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/mapper/WxMaAuthorizeMapper.java b/Auth/src/main/java/cn/crtech/cloud/auth/mapper/WxMaAuthorizeMapper.java new file mode 100644 index 0000000..f76e283 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/mapper/WxMaAuthorizeMapper.java @@ -0,0 +1,105 @@ +package cn.crtech.cloud.auth.mapper; + +import cn.crtech.cloud.auth.dto.MisComUserDto; +import cn.crtech.cloud.auth.pojo.WxAppletConfig; +import cn.crtech.cloud.auth.pojo.WxAppletUser; +import cn.crtech.cloud.auth.pojo.WxGzhConfig; +import org.apache.ibatis.annotations.*; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface WxMaAuthorizeMapper { + @Select("SELECT " + + "a.id, a.nick_name as nickName, a.mobile as mobile, " + + "a.open_id as openId, a.wx_open_id as wxOpenId " + + "FROM wx_user a " + + "where open_id = #{openId}") + @ResultType(WxAppletUser.class) + List getUserByWxAppletOpenId(@Param("openId") String openId); + + @Select("SELECT " + + "a.id , a.app_id as appId , a.app_name as appName , a.app_secret as appSecret, " + + "a.encoding_aes_key as encodingAesKey , a.token , a.msg_data_format as msgDataFormat " + + "FROM wx_applet_config a " + + "where status = 1 ") + @ResultType(WxAppletConfig.class) + List getWxAppletConfigList(); + + @Select("SELECT " + + "a.id, a.app_id as appId, a.name, a.app_secret as appSecret, a.app_aeskey as appAeskey," + + "a.app_token as appToken " + + "FROM wx_gzh_config as a " + + "where status = 1 ") + @ResultType(WxGzhConfig.class) + List getWxGzhConfigList(); + + @Select("SELECT " + + "a.id, a.nick_name as nickName, a.mobile as mobile, a.open_id as openId, " + + "a.wx_open_id as wxOpenId " + + "FROM wx_user a " + + "where a.open_id = #{openId} " + + "and a.company_code = #{companyCode}") + @ResultType(WxAppletUser.class) + WxAppletUser getWxAppletUser(@Param("openId") String openId, @Param("companyCode") String companyCode); + + @Select("SELECT " + + "a.id, a.nick_name as nickName, a.mobile as mobile, a.open_id as openId, " + + "a.wx_open_id as wxOpenId " + + "FROM wx_user a " + + "where wx_open_id = #{wxOpenId}") + @ResultType(WxAppletUser.class) + WxAppletUser getUserByWxOpenId(@Param("wxOpenId") String wxOpenId); + + @Insert({"insert into wx_user " + + "(nick_name, avatar_url, gender, country,province, city, mobile,open_id, union_id, " + + "created, app_id,wx_open_id, company_code, company_name) " + + "values" + + "(#{nickName}, #{avatarUrl}, #{gender}, #{country}, #{province}, #{city}, #{mobile}, " + + "#{openId}, #{unionId}, #{created, jdbcType=TIMESTAMP}, #{appId}, #{wxOpenId}, " + + "#{companyCode}, #{companyName})"}) + int insertWxAppletUser(WxAppletUser wxAppletUser); + + @Update("update wx_user " + + "set " + + "nick_name = #{nickName}, " + + "avatar_url = #{avatarUrl}, " + + "gender = #{gender}, " + + "country = #{country}, " + + "province = #{province}, " + + "city = #{city}, " + + "mobile = #{mobile}, " + + "union_id = #{unionId}, " + + "updated = #{updated} " + + "where open_id = #{openId}") + int updateWxAppletUser(WxAppletUser wxAppletUser); + + @Select("select " + + "a.id as userId,a.nick_name as nickName, a.mobile as mobile, " + + "b.company_code as companyCode, b.state " + + "from mis_user a " + + "inner join mis_company_user b on a.id = b.user_id " + + "where a.mobile = #{mobile} and b.company_code=#{companyCode}") + @ResultType(MisComUserDto.class) + MisComUserDto queryCompanyUser(@Param("companyCode") String companyCode, + @Param("mobile") String mobile); + + @Select("SELECT " + + "a.id as userId, a.nick_name as nickName, a.mobile as mobile, " + + "b.company_code as companyCode, b.state " + + "FROM mis_user a " + + "inner join mis_company_user b on a.id = b.user_id " + + "where a.mobile = #{mobile}") + @ResultType(MisComUserDto.class) + List getUserCompanyList(@Param("mobile") String mobile); + + @Update("update mis_company_user " + + "set " + + "is_default = #{isDefault} " + + "where company_code = #{companyCode} " + + "and user_id = #{userId}") + int updateCompanyDefault(@Param("companyCode") String companyCode, + @Param("userId") int userId, + @Param("isDefault") int isDefault); +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/AuthorizeUser.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/AuthorizeUser.java new file mode 100644 index 0000000..a352445 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/AuthorizeUser.java @@ -0,0 +1,50 @@ +package cn.crtech.cloud.auth.pojo; + +import lombok.Data; + +import java.util.List; + +@Data +public class AuthorizeUser { + private Integer id; + + private String userName; + + private String passWord; + + private Integer status; + + private List roles; + + private String companyCode; + + private String companyName; + + private String mobile; + + private String nickName; + + private String email; + + private Boolean isCompanyAdmin; + + private String dataSource; + + private Integer dataSourceId; + + + public AuthorizeUser(Integer userId, String passWord, Integer status, String mobile, String nickName, String email, String companyName, boolean isCompanyAdmin) { + this.id = userId; + this.userName = mobile; + this.passWord = passWord; + this.status = status; + this.mobile = mobile; + this.nickName = nickName; + this.email = email; + this.isCompanyAdmin = isCompanyAdmin; + this.companyName = companyName; + } + + public AuthorizeUser() { + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisApp.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisApp.java new file mode 100644 index 0000000..effa26b --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisApp.java @@ -0,0 +1,118 @@ +package cn.crtech.cloud.auth.pojo; + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.*; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.Date; + +/** + * MIS授权产品实体对象 + * + * @author TYP + * @since 2023-07-13 15:34 + */ + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "mis_app") +public class MisApp implements Serializable { + @Id + @DataExportAnnotation("主键id") + private Integer id; + + @Column(name = "series_id") + @DataExportAnnotation("产品所属系列ID") + private Integer seriesId; + + @Column(name = "name") + @DataExportAnnotation("产品名称") + private String name; + + @Column(name = "code") + @DataExportAnnotation("产品标识") + private String code; + + @Column(name = "description") + @DataExportAnnotation("产品描述信息") + private String description; + + @Column(name = "logo") + @DataExportAnnotation("产品图标超链接") + private String logo; + + @Column(name = "sort") + @DataExportAnnotation("产品排序") + private String sort; + + @Column(name = "resource_ids") + private String resourceIds; + + @Column(name = "client_secret") + @DataExportAnnotation("产品秘钥") + private String clientSecret; + + @Builder.Default + @Column(name = "scope") + @DataExportAnnotation("产品授权范围") + private String scope = "all"; + + @Column(name = "web_server_redirect_uri") + @DataExportAnnotation("") + private String webServerRedirectUri; + + @Column(name = "authorities") + @DataExportAnnotation("") + private String authorities; + + @Column(name = "additional_information") + @DataExportAnnotation("") + private String additionalInformation; + + @Builder.Default + @Column(name = "authorized_grant_types") + @DataExportAnnotation("产品授权加密类型") + private String authorizedGrantTypes = "password,refresh_token"; + + @Builder.Default + @Column(name = "access_token_validity") + @DataExportAnnotation("访问授权TOKEN有效期") + private Integer accessTokenValidity = 86400; + + @Builder.Default + @Column(name = "refresh_token_validity") + @DataExportAnnotation("TOKEN刷新有效期") + private Integer refreshTokenValidity = 86400; + + @Column(name = "auto_approve") + @DataExportAnnotation("") + private String autoApprove; + + @Column(name = "manager") + @DataExportAnnotation("产品管理产品经理ID") + private Integer manager; + + @Column(name = "created") + @DataExportAnnotation("创建时间") + @JsonFormat(pattern = "YYYY-MM-dd HH:mm:ss", locale = "cn", timezone = "GMT+8") + private Date created; + + @Column(name = "is_default_role") + @DataExportAnnotation("是否含有默认角色") + private Boolean isDefaultRole; + + @Column(name = "is_show") + @DataExportAnnotation("是否默认展示在产品展示列表") + private Boolean isShow; + + @Column(name = "state") + @DataExportAnnotation("状态 1正常 0停用 -1删除") + private Integer state; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisUser.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisUser.java new file mode 100644 index 0000000..f48f7ae --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/MisUser.java @@ -0,0 +1,157 @@ +package cn.crtech.cloud.auth.pojo; + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.*; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Transient; +import java.io.Serializable; +import java.util.Date; + +/** + * 系统用户实体 + * + * @author TYP + * @since 2023-07-24 16:13 + */ + +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "mis_user") +public class MisUser implements Serializable { + @Id + @DataExportAnnotation("主键id") + private Integer id; + + @Column(name = "name") + @DataExportAnnotation("用户名") + private String name; + + @Column(name = "mobile") + @DataExportAnnotation("绑定手机号码") + private String mobile; + + @Column(name = "nick_name") + @DataExportAnnotation("昵称") + private String nickName; + + @Column(name = "email") + @DataExportAnnotation("绑定邮箱地址") + private String email; + + @Column(name = "avatar") + @DataExportAnnotation("头像地址") + private String avatar; + + @Column(name = "password") + @DataExportAnnotation("密码") + private String password; + + @Column(name = "id_card") + @DataExportAnnotation("身份证号码") + private String idCard; + + @Column(name = "id_card_prev") + @DataExportAnnotation("身份证正面图片地址") + private String idCardPrev; + + @Column(name = "id_card_next") + @DataExportAnnotation("身份证反面图片地址") + private String idCardNext; + + @Column(name = "reg_time") + @DataExportAnnotation("注册时间") + @JsonFormat(pattern = "YYYY-MM-dd HH:mm:ss", locale = "cn", timezone = "GMT+8") + private Date regTime; + + @Column(name = "lastModifier") + @DataExportAnnotation("最后一次修改人用户ID") + private Integer lastModifier; + + @Column(name = "lastModifyTime") + @DataExportAnnotation("最后一次修改时间") + @JsonFormat(pattern = "YYYY-MM-dd HH:mm:ss", locale = "cn", timezone = "GMT+8") + private Date lastModifyTime; + + @Column(name = "is_system_admin") + @DataExportAnnotation("是否系统管理员") + private Boolean isSystemAdmin; + + @Column(name = "real_name") + @DataExportAnnotation("用户真实姓名") + private String realName; + + @Column(name = "state") + @DataExportAnnotation("状态 1正常 0停用 -1删除 ") + private Integer state; + + @Transient + @DataExportAnnotation("查询条件 分页页码") + private Integer pageNum; + + @Transient + @DataExportAnnotation("查询条件 分页个数") + private Integer pageSize; + + @Transient + @DataExportAnnotation("修改内容 旧密码") + private String oldPass; + + @Transient + @DataExportAnnotation("修改内容 新密码") + private String newPass; + + @Transient + @DataExportAnnotation("修改内容 校验密码:二次输入新密码") + private String checkPass; + + @Transient + @DataExportAnnotation("返回结果 所属企业名称") + private String companyName; + + @Transient + @DataExportAnnotation("查询条件 产品标识") + private String applicationCode; + + @Transient + @DataExportAnnotation("查询条件 角色标识") + private String roleCode; + + @Transient + @DataExportAnnotation("修改内容 旧密码") + private String oldPwd; + + @Transient + @DataExportAnnotation("修改内容 新密码") + private String newPwd; + + @Transient + @DataExportAnnotation("修改内容 校验密码:二次输入新密码") + private String checkNewPwd; + + @Transient + @DataExportAnnotation("参数内容 公司标识") + private String companyCode; + + @Transient + @DataExportAnnotation("参数内容 用户ID") + private Integer userId; + + @Transient + @DataExportAnnotation("参数内容 用户名称") + private String userName; + + @Transient + @DataExportAnnotation("参数内容 员工编号") + private String serialNo; + + @Transient + @DataExportAnnotation("返回结果 是否已经绑定角色") + private Integer isBindRole; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/Oauth2Token.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/Oauth2Token.java new file mode 100644 index 0000000..992f90f --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/Oauth2Token.java @@ -0,0 +1,34 @@ +package cn.crtech.cloud.auth.pojo; + +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * Oauth2获取Token返回信息封装 + */ + +@Data +@EqualsAndHashCode(callSuper = false) +@Builder +public class Oauth2Token { + /** + * 访问令牌 + */ + private String token; + + /** + * 刷新令牌 + */ + private String refreshToken; + + /** + * 访问令牌头前缀 + */ + private String tokenHead; + + /** + * 有效时间(秒) + */ + private int expiresIn; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/RawDataDO.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/RawDataDO.java new file mode 100644 index 0000000..06fa3b1 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/RawDataDO.java @@ -0,0 +1,20 @@ +package cn.crtech.cloud.auth.pojo; + +import lombok.Data; + +@Data +public class RawDataDO { + + private String nickName; + + private String avatarUrl; + + private Integer gender; + + private String city; + + private String country; + + private String province; + +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/RegisterUser.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/RegisterUser.java new file mode 100644 index 0000000..0cada07 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/RegisterUser.java @@ -0,0 +1,17 @@ +package cn.crtech.cloud.auth.pojo; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class RegisterUser implements Serializable { + private Integer userId; + private String nickName; + private String email; + private String serialNo; + private String mobile; + private String headImgUrl; + private String companyCode; + private String companyName; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/SecurityUser.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/SecurityUser.java new file mode 100644 index 0000000..a158336 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/SecurityUser.java @@ -0,0 +1,115 @@ +package cn.crtech.cloud.auth.pojo; + + +import lombok.Data; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * 登录用户信息 + */ +@Data +public class SecurityUser implements UserDetails { + /** + * ID + */ + private Integer id; + /** + * 用户名 + */ + private String userName; + + private String companyCode; + + private String companyName; + + /** + * 用户密码 + */ + private String password; + + /** + * 用户状态 + */ + private Boolean enabled; + + private String mobile; + + private String nickName; + + private String email; + + private Boolean isCompanyAdmin; + + private String dataSource; + + private Integer dataSourceId; + + /** + * 权限数据 + */ + private Collection authorities; + + public SecurityUser() { + + } + + public SecurityUser(AuthorizeUser authorizeUser) { + this.setId(authorizeUser.getId()); + this.setUserName(authorizeUser.getUserName()); + this.setCompanyCode(authorizeUser.getCompanyCode()); + this.setPassword(authorizeUser.getPassWord()); + this.setMobile(authorizeUser.getMobile()); + this.setNickName(authorizeUser.getNickName()); + this.setEnabled(authorizeUser.getStatus() == 1); + this.setEmail(authorizeUser.getEmail()); + this.setIsCompanyAdmin(authorizeUser.getIsCompanyAdmin()); + this.setCompanyName(authorizeUser.getCompanyName()); + this.setDataSource(authorizeUser.getDataSource()); + this.setDataSourceId(authorizeUser.getDataSourceId()); + if (authorizeUser.getRoles() != null) { + authorities = new ArrayList<>(); + authorizeUser.getRoles().forEach(item -> authorities.add(new SimpleGrantedAuthority(item))); + } + } + + @Override + public Collection getAuthorities() { + return this.authorities; + } + + @Override + public String getPassword() { + return this.password; + } + + @Override + public String getUsername() { + return this.userName; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/User.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/User.java new file mode 100644 index 0000000..98d2874 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/User.java @@ -0,0 +1,92 @@ +package cn.crtech.cloud.auth.pojo; + + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; + +/** + * Author : yj + * Date : 2021-01-14 + * Description: + */ +@Data +@Table(name = "user") +@DataExportAnnotation("用户表") +public class User implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @DataExportAnnotation("主键id") + private Integer id; + + @Column(name = "name") + @DataExportAnnotation("姓名") + private String name; + + @Column(name = "id_card") + @DataExportAnnotation("身份证") + private String idCard; + + @Column(name = "nick_name") + @DataExportAnnotation("昵称") + private String nickName; + + @Column(name = "lastModifier") + @DataExportAnnotation("最后修改人") + private Long lastModifier; + + @Column(name = "lastModifyTime") + @DataExportAnnotation("最后修时间") + private Date lastModifyTime; + + @Column(name = "state") + @DataExportAnnotation("状态") + private Integer state; + + @Column(name = "mobile") + @DataExportAnnotation("手机号码") + private String mobile; + + @Column(name = "email") + @DataExportAnnotation("邮箱") + private String email; + + @Column(name = "avatar") + @DataExportAnnotation("头像") + private String avatar; + + @Column(name = "password") + @DataExportAnnotation("密码") + private String password; + + @Column(name = "id_card_prev") + @DataExportAnnotation("身份证正面") + private String idCardPrev; + + @Column(name = "id_card_next") + @DataExportAnnotation("身份证反面") + private String idCardNext; + + @Column(name = "real_name") + @DataExportAnnotation("真实姓名") + private String realName; + + @Column(name = "reg_time") + @DataExportAnnotation("注册时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date regTime; + + @Transient + private String companyCode; + + @Transient + private String companyName; + + public void setId(Integer id) { + this.id = id; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WechatLoginRequest.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WechatLoginRequest.java new file mode 100644 index 0000000..01259f2 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WechatLoginRequest.java @@ -0,0 +1,16 @@ +package cn.crtech.cloud.auth.pojo; + +import lombok.Data; + +@Data +public class WechatLoginRequest { + private String rawData; + private String signature; + private String encryptedData; + private String iv; + private String username; + private String password; + private String accessToken; + private String companyCode; + private String companyName; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletConfig.java new file mode 100644 index 0000000..b1b6b81 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletConfig.java @@ -0,0 +1,55 @@ +package cn.crtech.cloud.auth.pojo; + + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import cn.crtech.cloud.common.utils.IDGenerator; +import lombok.Data; +import tk.mybatis.mapper.annotation.KeySql; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; + + +@Data +@Table(name = "wx_applet_config") +@DataExportAnnotation("小程序配置") +public class WxAppletConfig implements Serializable { + @Id + @KeySql(genId = IDGenerator.class) + @DataExportAnnotation("主键") + private String id; + + @Column(name = "app_id") + @DataExportAnnotation("应用id") + private String appId; + + @Column(name = "app_name") + @DataExportAnnotation("小程序名称") + private String appName; + + @Column(name = "app_secret") + @DataExportAnnotation("秘钥") + private String appSecret; + + @Column(name = "encoding_aes_key") + @DataExportAnnotation("消息加密key") + private String encodingAesKey; + + @Column(name = "token") + @DataExportAnnotation("消息令牌") + private String token; + + @Column(name = "status") + @DataExportAnnotation("状态") + private Integer status; + + /** + * 消息格式,XML或者JSON + */ + @Column(name = "msg_data_format") + @DataExportAnnotation("消息格式") + private String msgDataFormat; + +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletUser.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletUser.java new file mode 100644 index 0000000..5909d05 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxAppletUser.java @@ -0,0 +1,87 @@ +package cn.crtech.cloud.auth.pojo; + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import javax.persistence.*; +import java.util.Date; + +@Data +@Table(name = "wx_user") +@DataExportAnnotation("小程序用户") +public class WxAppletUser { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @DataExportAnnotation("主键id") + private Integer id; + + @Column(name = "nick_name") + @DataExportAnnotation("用户昵称") + private String nickName; + + @Column(name = "avatar_url") + @DataExportAnnotation("用户头像") + private String avatarUrl; + + @Column(name = "gender") + @DataExportAnnotation("性别") + private int gender; + + @Column(name = "country") + @DataExportAnnotation("所在国家") + private String country; + + @Column(name = "province") + @DataExportAnnotation("省份") + private String province; + + @Column(name = "city") + @DataExportAnnotation("城市") + private String city; + + @Column(name = "mobile") + @DataExportAnnotation("手机号码") + private String mobile; + + @Column(name = "open_id") + @DataExportAnnotation("小程序openId") + private String openId; + + @Column(name = "union_id") + @DataExportAnnotation("小程序unionId") + private String unionId; + + @Column(name = "app_id") + @DataExportAnnotation("应用id") + private String appId; + + @Column(name = "wx_open_id") + @DataExportAnnotation("微信openId") + private String wxOpenId; + + @Column(name = "created") + @DataExportAnnotation("注册时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date created; + + @Column(name = "updated") + @DataExportAnnotation("更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updated; + + @Column(name = "qr_open_id") + @DataExportAnnotation("扫二维码openId") + private String qrOpenId; + + @Column(name = "company_code") + @DataExportAnnotation("企业标识") + private String companyCode; + + @Column(name = "company_name") + @DataExportAnnotation("企业标识") + private String companyName; + + @Transient + private String serialNo; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxGzhConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxGzhConfig.java new file mode 100644 index 0000000..b4f5d50 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/pojo/WxGzhConfig.java @@ -0,0 +1,49 @@ +package cn.crtech.cloud.auth.pojo; + + +import cn.crtech.cloud.common.annotation.DataExportAnnotation; +import cn.crtech.cloud.common.utils.IDGenerator; +import lombok.Data; +import tk.mybatis.mapper.annotation.KeySql; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.persistence.Table; +import java.io.Serializable; + + +@Data +@Table(name = "wx_gzh_config") +@DataExportAnnotation("公众号号配置") +public class WxGzhConfig implements Serializable { + @Id + @KeySql(genId = IDGenerator.class) + @DataExportAnnotation("主键") + private String id; + + @Column(name = "name") + @DataExportAnnotation("公众号名称") + private String name; + + @Column(name = "app_id") + @DataExportAnnotation("应用id") + private String appId; + + @Column(name = "app_secret") + @DataExportAnnotation("秘钥") + private String appSecret; + + @Column(name = "app_token") + @DataExportAnnotation("消息令牌") + private String appToken; + + + @Column(name = "app_aeskey") + @DataExportAnnotation("消息加密key") + private String appAeskey; + + @Column(name = "status") + @DataExportAnnotation("状态") + private Integer status; + +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/ResourceService.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/ResourceService.java new file mode 100644 index 0000000..c1ee6f5 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/ResourceService.java @@ -0,0 +1,198 @@ +package cn.crtech.cloud.auth.service; + +import cn.crtech.cloud.auth.dto.MisRoleApiDto; +import cn.crtech.cloud.auth.mapper.AuthorizeMapper; +import cn.crtech.cloud.common.constant.AuthConstant; +import cn.crtech.cloud.common.constant.RedisConstant; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +/** + * 资源与角色匹配关系管理业务类 + */ +@Component +public class ResourceService { + private RedisTemplate redisTemplate; + private AuthorizeMapper authorizeMapper; + + @Autowired + public ResourceService(RedisTemplate redisTemplate, AuthorizeMapper authorizeMapper) { + this.redisTemplate = redisTemplate; + this.authorizeMapper = authorizeMapper; + } + + /** + * 初始化加载并将权限信息注入缓存 + */ + @PostConstruct + public void initData() { + // 特殊权限授权内容加载处理 + initLimitAuthority(); + + // 免费角色权限数据内容加载处理 + initFreeController(); + + // 企业角色权限数据内容加载处理 + initComRoleController(); + + // 企业管理员权限数据内容加载处理 + initComAdminController(); + + // 产品默认角色权限数据内容加载处理 + initAppDefaultRoleController(); + } + + /** + * 缓存产品特殊权限内容 + */ + private void initLimitAuthority() { + redisTemplate.delete(RedisConstant.LIMIT_AUTHORITY_API_MAP); + List limitAuthority = authorizeMapper.listLimitApi(); + if (!CollectionUtils.isEmpty(limitAuthority)) { + Map limitAuthorityMap = new TreeMap<>(); + limitAuthority.forEach(item -> { + limitAuthorityMap.put(item.getController(), item.getRole()); + }); + redisTemplate.opsForHash().putAll(RedisConstant.LIMIT_AUTHORITY_API_MAP, limitAuthorityMap); + } + } + + /** + * 缓存免费产品及对应产品菜单权限功能 + */ + private void initFreeController() { + // 缓存免费产品及对应产品菜单权限功能 + List freeAppList = authorizeMapper.listFreeApp(); + if (!CollectionUtils.isEmpty(freeAppList)) { + // 相关菜单加载 + List freeControllerList = authorizeMapper.listFreeController(); + // 相关菜单特殊权限加载 + List freeAuthorityList = authorizeMapper.listFreeAuthority(); + + // 相关免费内容处理缓存 (根据产品分组ID、产品ID以及对应版本ID进行分组处理) + freeAppList.forEach(item -> { + String seriesAppVersion = RedisConstant.FREE_VERSION_API + .replace("${SERIESCODE}", item.getSeriesCode()) + .replace("${APPCODE}", item.getAppCode()) + .replace("${VERSION}", item.getVersion()); + redisTemplate.delete(seriesAppVersion); + + // 数据缓存处理 + Map freeResourceMap = new TreeMap<>(); + List tempList = freeControllerList.stream().filter(it -> it.getSeriesCode().equals(item.getSeriesCode()) + && it.getAppCode().equals(item.getAppCode()) + && it.getVersion().equals(item.getVersion())) + .collect(Collectors.toList()); + tempList.forEach(temp -> { + freeResourceMap.put(temp.getController(), temp.getRole()); + }); + + List tempAuthorityList = freeAuthorityList.stream().filter(it -> it.getSeriesCode().equals(item.getSeriesCode()) + && it.getAppCode().equals(item.getAppCode()) + && it.getVersion().equals(item.getVersion())) + .collect(Collectors.toList()); + tempAuthorityList.forEach(temp -> { + freeResourceMap.put(temp.getController() + AuthConstant.APP_SPECIAL_AUTHORITY_SUFFIX, temp.getRole()); + }); + + freeResourceMap.put("/general/**", "ALL"); + freeResourceMap.put("/mns/**", "ALL"); + freeResourceMap.put("/rm/**", "ALL"); + freeResourceMap.put("/main/**", "ALL"); + redisTemplate.opsForHash().putAll(seriesAppVersion, freeResourceMap); + }); + } + + // 缓存产品内服务间调用内容 + redisTemplate.delete(RedisConstant.APP_FEIGN_API_MAP); + List appFeignList = authorizeMapper.listAppFeignController(); + if (!CollectionUtils.isEmpty(appFeignList)) { + Map appFeignMap = new TreeMap<>(); + appFeignList.forEach(item -> { + appFeignMap.put(item.getController(), item.getRole()); + }); + redisTemplate.opsForHash().putAll(RedisConstant.APP_FEIGN_API_MAP, appFeignMap); + } + } + + /** + * 缓存企业角色对应产品菜单权限功能 + */ + private void initComRoleController() { + redisTemplate.delete(RedisConstant.RESOURCE_ROLES_MAP); + // 企业角色相关菜单加载 + List comRoleApiList = authorizeMapper.listRoleController(); + // 企业角色相关菜单特殊权限加载 + List comRoleAuthorityList = authorizeMapper.listRoleAuthorityController(); + // 企业角色相关内容处理缓存 + Map comRoleMap = new TreeMap<>(); + comRoleApiList.forEach(item -> { + comRoleMap.put(item.getController(), item.getRole()); + }); + comRoleAuthorityList.forEach(item -> { + comRoleMap.put(item.getController() + AuthConstant.APP_SPECIAL_AUTHORITY_SUFFIX, item.getRole()); + }); + comRoleMap.put("/general/**", "ALL"); + comRoleMap.put("/mns/**", "ALL"); + comRoleMap.put("/rm/**", "ALL"); + comRoleMap.put("/main/**", "ALL"); + redisTemplate.opsForHash().putAll(RedisConstant.RESOURCE_ROLES_MAP, comRoleMap); + } + + /** + * 缓存企业管理员对应产品菜单权限功能 + */ + private void initComAdminController() { + redisTemplate.delete(RedisConstant.RESOURCE_ADMIN_MAP); + // 企业管理员相关菜单加载 + List comAdminController = authorizeMapper.getCompanyAdminController(); + // 企业管理员相关菜单特殊权限加载 + List comAdminAuthorityController = authorizeMapper.getCompanyAdminAuthorityController(); + // 企业管理员相关内容处理缓存 + Map comAdminMap = new TreeMap<>(); + comAdminController.forEach(item -> { + comAdminMap.put(item.getController(), item.getRole()); + }); + comAdminAuthorityController.forEach(item -> { + comAdminMap.put(item.getController() + AuthConstant.APP_SPECIAL_AUTHORITY_SUFFIX, item.getRole()); + }); + comAdminMap.put("/general/**", "ALL"); + comAdminMap.put("/mns/**", "ALL"); + comAdminMap.put("/rm/**", "ALL"); + comAdminMap.put("/main/**", "ALL"); + redisTemplate.opsForHash().putAll(RedisConstant.RESOURCE_ADMIN_MAP, comAdminMap); + } + + /** + * 缓存产品默认角色对应产品菜单权限功能 + */ + private void initAppDefaultRoleController() { + redisTemplate.delete(RedisConstant.DEFAULT_ROLES_MAP); + + // 产品默认角色相关菜单加载 + List appDefaultRoleApiList = authorizeMapper.listDefaultRoleController(); + // 产品默认角色相关菜单特殊权限加载 + List appDefaultRoleAuthorityApiList = authorizeMapper.listDefaultRoleAuthorityController(); + // 产品默认角色关内容处理缓存 + Map appDefaultRoleMap = new TreeMap<>(); + appDefaultRoleApiList.forEach(item -> { + appDefaultRoleMap.put(item.getController(), item.getRole()); + }); + appDefaultRoleAuthorityApiList.forEach(item -> { + appDefaultRoleMap.put(item.getController() + AuthConstant.APP_SPECIAL_AUTHORITY_SUFFIX, item.getRole()); + }); + appDefaultRoleMap.put("/general/**", "ALL"); + appDefaultRoleMap.put("/mns/**", "ALL"); + appDefaultRoleMap.put("/rm/**", "ALL"); + appDefaultRoleMap.put("/main/**", "ALL"); + redisTemplate.opsForHash().putAll(RedisConstant.DEFAULT_ROLES_MAP, appDefaultRoleMap); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthService.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthService.java new file mode 100644 index 0000000..cfdd73c --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthService.java @@ -0,0 +1,143 @@ +package cn.crtech.cloud.auth.service.system; + +import cn.crtech.cloud.auth.dto.MisComRoleUserDto; +import cn.crtech.cloud.auth.dto.MisComUserDto; +import cn.crtech.cloud.auth.mapper.AuthorizeMapper; +import cn.crtech.cloud.auth.pojo.AuthorizeUser; +import cn.crtech.cloud.auth.pojo.MisUser; +import cn.crtech.cloud.auth.pojo.SecurityUser; +import cn.crtech.cloud.common.constant.MessageConstant; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.LockedException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.util.CollectionUtils; + +import java.util.*; + +/** + * Author : yj + * Date : 2021-01-14 + * Description:初始化登录用户信息,查询用户权限有哪些角色,以及登录接口loadUserByUsername + */ + +@Configuration +@Slf4j +public class AuthService implements AuthUserService { + @Autowired + AuthorizeMapper authorizeMapper; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return null; + } + + /** + * 重写loadUserByUsername方法 后端登录token + * + * @param username 用户登录名称 + * @return 返回登录结果 + * @throws UsernameNotFoundException 用户登录名不存在异常 + */ + @Override + public UserDetails loadUserByUsername(String username, String companyCode) throws UsernameNotFoundException { + // 根据登录账号获取相关用户信息 + MisUser misUser = authorizeMapper.queryUserByMobile(username); + if (ObjectUtils.isEmpty(misUser)) { + throw new UsernameNotFoundException(MessageConstant.USERNAME_PASSWORD_ERROR); + } + + // 数据处理赋值 + Map params = new HashMap<>(); + params.put("userId", misUser.getId()); + AuthorizeUser authorizeUser = new AuthorizeUser(misUser.getId(), misUser.getPassword(), + misUser.getState(), username, misUser.getName(), + misUser.getEmail(), "", false); + if (StringUtils.isNotBlank(companyCode)) { + params.put("companyCode", companyCode); + } + + // 其他授权信息获取处理 + getUserAuthorize(misUser, params, authorizeUser); + + // 安全用户新增赋值 + SecurityUser securityUser = new SecurityUser(authorizeUser); + + // 登录用户异常问题处理 + if (!securityUser.isEnabled()) { + throw new DisabledException(MessageConstant.ACCOUNT_DISABLED); + } + if (!securityUser.isAccountNonLocked()) { + throw new LockedException(MessageConstant.ACCOUNT_LOCKED); + } + if (!securityUser.isAccountNonExpired()) { + throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED); + } + if (!securityUser.isCredentialsNonExpired()) { + throw new CredentialsExpiredException(MessageConstant.CREDENTIALS_EXPIRED); + } + + return securityUser; + } + + /** + * 用户登录授权信息补充处理 + * + * @param user 用户信息对象 + * @param params 其他信息 + * @param authorizeUser 授权用户对象 + */ + private void getUserAuthorize(MisUser user, Map params, AuthorizeUser authorizeUser) { + List companyList = authorizeMapper.queryCompanyUserByUserId(params); + if (CollectionUtils.isEmpty(companyList)) { + List userRoleList = new ArrayList<>(); + userRoleList.add("ALL"); + authorizeUser.setRoles(userRoleList); + authorizeUser.setDataSource(""); + } else { + // 获取默认企业 + MisComUserDto defaultCompany = companyList.get(0); + + authorizeUser.setCompanyCode(defaultCompany.getCompanyCode()); + authorizeUser.setCompanyName(defaultCompany.getCompanyName()); + + List userRoleList = new ArrayList<>(); + userRoleList.add("ALL"); + + //设置企业管理员 + if (defaultCompany.getIsOwner()) { + userRoleList.add(defaultCompany.getCompanyCode() + "_admin"); + authorizeUser.setIsCompanyAdmin(true); + } else { + // 查询获取授权角色数据集合 + List userRoles = authorizeMapper.queryCompanyRoleUser(user.getId(), defaultCompany.getCompanyCode()); + userRoles.forEach(role -> { + userRoleList.add(role.getCompanyCode() + "_" + role.getRoleId()); + }); + + // 查询用户企业授权角色相关内容 + String comRoleCodes = authorizeMapper.queryUserRole(user.getId(), defaultCompany.getCompanyCode()); + if (!StringUtils.isBlank(comRoleCodes)) { + String[] roleCodeArray = comRoleCodes.split(","); + userRoleList.addAll(Arrays.asList(roleCodeArray)); + } + + // 查询用户默认授权角色相关内容 + String defaultRoleCodes = authorizeMapper.queryUserDefaultRole(user.getId(), defaultCompany.getCompanyCode()); + if (!StringUtils.isBlank(defaultRoleCodes)) { + String[] roleCodeArray = defaultRoleCodes.split(","); + userRoleList.addAll(Arrays.asList(roleCodeArray)); + } + } + log.info("userRoleList ==> {}", userRoleList.toString()); + authorizeUser.setRoles(userRoleList); + } + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthUserService.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthUserService.java new file mode 100644 index 0000000..173d30b --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/system/AuthUserService.java @@ -0,0 +1,9 @@ +package cn.crtech.cloud.auth.service.system; + +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +public interface AuthUserService extends UserDetailsService { + UserDetails loadUserByUsername(String username, String companyCode) throws UsernameNotFoundException; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserService.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserService.java new file mode 100644 index 0000000..b366d85 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserService.java @@ -0,0 +1,9 @@ +package cn.crtech.cloud.auth.service.wx.applet; + +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +public interface WxAppletUserService extends UserDetailsService { + UserDetails loadUserByUsername(String username, String companyCode) throws UsernameNotFoundException; +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserServiceImpl.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserServiceImpl.java new file mode 100644 index 0000000..8f4e4c5 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxAppletUserServiceImpl.java @@ -0,0 +1,132 @@ +package cn.crtech.cloud.auth.service.wx.applet; + +import cn.crtech.cloud.auth.dto.MisComRoleUserDto; +import cn.crtech.cloud.auth.dto.MisComUserDto; +import cn.crtech.cloud.auth.mapper.AuthorizeMapper; +import cn.crtech.cloud.auth.mapper.WxMaAuthorizeMapper; +import cn.crtech.cloud.auth.pojo.AuthorizeUser; +import cn.crtech.cloud.auth.pojo.MisUser; +import cn.crtech.cloud.auth.pojo.SecurityUser; +import cn.crtech.cloud.auth.pojo.WxAppletUser; +import cn.crtech.cloud.common.constant.MessageConstant; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.LockedException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.util.CollectionUtils; + +import java.util.*; + +@Slf4j +@Configuration +public class WxAppletUserServiceImpl implements WxAppletUserService { + private AuthorizeMapper authorizeMapper; + private WxMaAuthorizeMapper wxMaAuthorizeMapper; + + @Autowired + public WxAppletUserServiceImpl(WxMaAuthorizeMapper wxMaAuthorizeMapper, AuthorizeMapper authorizeMapper) { + this.wxMaAuthorizeMapper = wxMaAuthorizeMapper; + this.authorizeMapper = authorizeMapper; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return null; + } + + /** + * 重构微信用户登录内容 + * + * @param username 登录账号 + * @param companyCode 公司标识 + * @return 返回登录处理结果 + * @throws UsernameNotFoundException 抛出异常 + */ + @Override + public UserDetails loadUserByUsername(String username, String companyCode) throws UsernameNotFoundException { + // 校验非空参数 + if (StringUtils.isBlank(username)) { + throw new UsernameNotFoundException(MessageConstant.ACCOUNT_SIGNATURE_ERROR); + } + // 查询并校验对应微信用户是否存在 + WxAppletUser selectWxAppletUser = wxMaAuthorizeMapper.getUserByWxAppletOpenId(username).get(0); + if (ObjectUtils.isEmpty(selectWxAppletUser)) { + throw new UsernameNotFoundException(MessageConstant.ACCOUNT_ERROR); + } + // 查询并校验对应系统用户是否存在 + MisUser user = authorizeMapper.queryUserByMobile(selectWxAppletUser.getMobile()); + if (ObjectUtils.isEmpty(user)) { + throw new UsernameNotFoundException(MessageConstant.ACCOUNT_ERROR); + } + + // 生成认证用户信息 + AuthorizeUser authorizeUser = new AuthorizeUser(user.getId(), user.getPassword(), user.getState(), user.getMobile(), user.getName(), user.getEmail(), "", false); + + // 生成用户绑定企业信息相关查询参数 + Map params = new HashMap<>(); + params.put("userId", user.getId()); + if (!StringUtils.isBlank(companyCode)) { + params.put("companyCode", companyCode); + } + + // 查询用户关联企业信息内容 + List comUserList = authorizeMapper.queryCompanyUserByUserId(params); + if (CollectionUtils.isEmpty(comUserList)) { + List userRoleList = new ArrayList<>(); + userRoleList.add("ALL"); + authorizeUser.setRoles(userRoleList); + } else { + MisComUserDto defaultCompany = comUserList.get(0); + List userRoleList = new ArrayList<>(); + // 设置默认公司 + authorizeUser.setCompanyCode(defaultCompany.getCompanyCode()); + authorizeUser.setCompanyName(defaultCompany.getCompanyName()); + + // 判断是否企业管理员 + if (defaultCompany.getIsOwner()) { + authorizeUser.setIsCompanyAdmin(true); + userRoleList.add(defaultCompany.getCompanyCode() + "_admin"); + } else { + List userRoles = authorizeMapper.queryCompanyRoleUser(user.getId(), defaultCompany.getCompanyCode()); + userRoles.forEach(role -> { + userRoleList.add(role.getCompanyCode() + "_" + role.getRoleId()); + }); + + String roleCodes = authorizeMapper.queryUserRole(user.getId(), defaultCompany.getCompanyCode()); + if (!StringUtils.isBlank(roleCodes)) { + String[] roleCodeArray = roleCodes.split(","); + userRoleList.addAll(Arrays.asList(roleCodeArray)); + } + String defaultRoleCodes = authorizeMapper.queryUserDefaultRole(user.getId(), defaultCompany.getCompanyCode()); + if (!StringUtils.isBlank(defaultRoleCodes)) { + String[] roleCodeArray = defaultRoleCodes.split(","); + userRoleList.addAll(Arrays.asList(roleCodeArray)); + } + } + userRoleList.add("ALL"); + authorizeUser.setRoles(userRoleList); + } + + SecurityUser securityUser = new SecurityUser(authorizeUser); + if (!securityUser.isEnabled()) { + throw new DisabledException(MessageConstant.ACCOUNT_DISABLED); + } + if (!securityUser.isAccountNonLocked()) { + throw new LockedException(MessageConstant.ACCOUNT_LOCKED); + } + if (!securityUser.isAccountNonExpired()) { + throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED); + } + if (!securityUser.isCredentialsNonExpired()) { + throw new CredentialsExpiredException(MessageConstant.CREDENTIALS_EXPIRED); + } + return securityUser; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxMaUserService.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxMaUserService.java new file mode 100644 index 0000000..bfae8d8 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/applet/WxMaUserService.java @@ -0,0 +1,574 @@ +package cn.crtech.cloud.auth.service.wx.applet; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.crtech.cloud.auth.dto.MisComUserDto; +import cn.crtech.cloud.auth.mapper.AuthorizeMapper; +import cn.crtech.cloud.auth.mapper.WxMaAuthorizeMapper; +import cn.crtech.cloud.auth.pojo.*; +import cn.crtech.cloud.auth.utils.TencentUtils; +import cn.crtech.cloud.common.dto.Result; +import cn.crtech.cloud.common.utils.EncryptUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.bean.WxOAuth2UserInfo; +import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.bean.result.WxMpUser; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.security.Security; +import java.util.*; + +@Slf4j +@Configuration +public class WxMaUserService { + private static final String secretKey = "www.crtech.com"; + + private WxMpService wxMpService; + private WxMaService wxMaService; + private TencentUtils tencentUtils; + private RedisTemplate redisTemplate; + private PasswordEncoder passwordEncoder; + private AuthorizeMapper authorizeMapper; + private WxMaAuthorizeMapper wxMaConfigMapper; + private WxMaAuthorizeMapper wxMaAuthorizeMapper; + + @Autowired + public WxMaUserService(WxMaService wxMaService, WxMaAuthorizeMapper wxMaConfigMapper, + RedisTemplate redisTemplate, AuthorizeMapper authorizeMapper, + WxMpService wxMpService, PasswordEncoder passwordEncoder, + TencentUtils tencentUtils, WxMaAuthorizeMapper wxMaAuthorizeMapper) { + this.wxMaService = wxMaService; + this.wxMaConfigMapper = wxMaConfigMapper; + this.redisTemplate = redisTemplate; + this.authorizeMapper = authorizeMapper; + this.wxMpService = wxMpService; + this.passwordEncoder = passwordEncoder; + this.tencentUtils = tencentUtils; + this.wxMaAuthorizeMapper = wxMaAuthorizeMapper; + } + + /** + * 校验是否关注、绑定 + * + * @param wxAppletCode 微信小程序标识 + * @param code 参数标识 + * @param appId 公众号AppID + * @return 返回校验结果 + */ + @Transactional(readOnly = true) + public Result checkUser(String wxAppletCode, String code, String appId) { + // 参数校验 + Map params = new HashMap<>(); + if (!wxMaService.switchover(appId)) { + throw new IllegalArgumentException(String.format("未找到对应 AppID=[%s] 的配置,请核实!", appId)); + } + + try { + WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); + String wxOpenId = accessToken.getOpenId(); + WxMpUser wxMpUser = wxMpService.getUserService().userInfo(wxOpenId); + boolean subscribe = wxMpUser.getSubscribe(); + WxOAuth2UserInfo wxOAuth2UserInfo = wxMpService.getOAuth2Service().getUserInfo(accessToken, wxOpenId); + String headImgUrl = wxOAuth2UserInfo.getHeadImgUrl(); + log.info("wxOAuth2UserInfo ===> {}", wxOAuth2UserInfo.toString()); + + // 用户关注公众号校验 + if (!subscribe) { + params.put("code", "unSubscribe"); + return Result.error(params, "未进行公众号绑定!"); + } + + WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(wxAppletCode); + String wxAppletOpenId = session.getOpenid(); + + //校验用户是否注册 + List selectWxAppletUser = wxMaConfigMapper.getUserByWxAppletOpenId(wxAppletOpenId); + if (selectWxAppletUser.isEmpty()) { + //返回对应的token + String sessionKey = session.getSessionKey(); + String systemAccessToken = createToken(wxAppletOpenId, sessionKey, appId, wxOpenId, headImgUrl, null, null); + params.put("accessToken", systemAccessToken); + params.put("code", "unBindApplet"); + return Result.error(params, "未进行系统绑定!"); + } + + //校验通过 返回小程序openId + params.put("openId", wxAppletOpenId); + params.put("wxOpenId", wxOpenId); + return Result.success(params); + } catch (WxErrorException e) { + params.put("code", "systemException"); + return Result.error(params, e.getError().getErrorMsg()); + } + } + + /** + * 获取微信用户对应绑定openID + * + * @param wxAppletCode 微信小程序标识 + * @param appId 公众号AppID + * @return 返回查询结果 + */ + @Transactional(readOnly = true) + public Result getWxAppletOpenId(String wxAppletCode, String appId) { + // 参数校验 + Map params = new HashMap<>(); + if (!wxMaService.switchover(appId)) { + throw new IllegalArgumentException(String.format("未找到对应 AppID=[%s] 的配置,请核实!", appId)); + } + + try { + WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(wxAppletCode); + String wxAppletOpenId = session.getOpenid(); + //校验通过 返回小程序openId + params.put("openId", wxAppletOpenId); + return Result.success(params); + } catch (WxErrorException e) { + e.printStackTrace(); + params.put("code", "systemException"); + return Result.error(params, "非法code!"); + } + } + + /** + * 小程序绑定 + * + * @param request 微信请求参数信息 + * @return 返回结果 + */ + @Transactional(rollbackFor = Exception.class) + public Result bindWxApplet(WechatLoginRequest request) { + // 参数非空校验 + if (StringUtils.isBlank(request.getUsername())) { + return Result.error("", "参数异常: 用户名不能为空!"); + } + if (StringUtils.isBlank(request.getPassword())) { + return Result.error("", "参数异常: 用户密码不能为空!"); + } + + // 其他公用对象内容生成 + String mobile = request.getUsername(); + String passWord = request.getPassword(); + + // 校验查询绑定用户 + MisUser misUser = authorizeMapper.queryUserByMobile(mobile); + if (ObjectUtils.isEmpty(misUser)) { + return Result.error("", "参数异常: 系统未查找到当前账号绑定用户!"); + } + + // 登录密码校验 + if (!passwordEncoder.matches(passWord, misUser.getPassword())) { + return Result.error("", "参数异常: 绑定密码与系统密码不符!"); + } + + // 解析accessToken拆分对应授权信息 + String decryptAccessToken = new String(Objects.requireNonNull( + EncryptUtil.decrypt(EncryptUtil.parseHexStr2Byte(request.getAccessToken()), secretKey)), + StandardCharsets.UTF_8); + Map accessTokenMap = JSONObject.parseObject(decryptAccessToken, Map.class); + String openId = accessTokenMap.get("openId").toString(); + String sessionKey = accessTokenMap.get("sessionKey").toString(); + String appId = accessTokenMap.get("appId").toString(); + String wxOpenId = accessTokenMap.get("wxOpenId").toString(); + String headImgUrl = accessTokenMap.get("headImgUrl").toString(); + + // 创建生成 AppUser + WxAppletUser insertOrUpdateDO = buildWechatUserAuthInfoDO(request.getRawData(), + request.getEncryptedData(), request.getIv(), sessionKey, openId); + insertOrUpdateDO.setMobile(mobile); + + // 用户企业内容请求参数创建 + Map params = new HashMap<>(); + params.put("userId", misUser.getId()); + if (!StringUtils.isBlank(request.getCompanyCode())) { + params.put("companyCode", request.getCompanyCode()); + } + + // 查询相关绑定企业内容并设置企业信息 + List companyList = authorizeMapper.queryCompanyUserByUserId(params); + if (CollectionUtils.isEmpty(companyList)) { + if (StringUtils.isBlank(request.getCompanyCode()) && StringUtils.isBlank(request.getCompanyName())) { + return Result.error(mobile, "绑定异常,请联系相关管理人员!"); + } else { + insertOrUpdateDO.setCompanyCode(request.getCompanyCode()); + insertOrUpdateDO.setCompanyName(request.getCompanyName()); + insertOrUpdateDO.setSerialNo("No" + (int) ((Math.random() * 9 + 1))); + } + } else { + MisComUserDto defaultCompany = companyList.get(0); + if (!ObjectUtil.isEmpty(defaultCompany)) { + insertOrUpdateDO.setCompanyCode(defaultCompany.getCompanyCode()); + insertOrUpdateDO.setCompanyName(defaultCompany.getCompanyName()); + insertOrUpdateDO.setSerialNo(defaultCompany.getSerialNo()); + } + } + + // 校验用户是否存在 不存在则插入 存在则进行更新 + int success = 0; + WxAppletUser selectWxAppletUser = wxMaAuthorizeMapper.getWxAppletUser(openId, insertOrUpdateDO.getCompanyCode()); + if (ObjectUtils.isEmpty(selectWxAppletUser)) { + insertOrUpdateDO.setWxOpenId(wxOpenId); + insertOrUpdateDO.setAppId(appId); + insertOrUpdateDO.setAvatarUrl(headImgUrl); + success = wxMaAuthorizeMapper.insertWxAppletUser(insertOrUpdateDO); + } else { + selectWxAppletUser.setNickName(insertOrUpdateDO.getNickName()); + selectWxAppletUser.setAvatarUrl(headImgUrl); + selectWxAppletUser.setGender(insertOrUpdateDO.getGender()); + selectWxAppletUser.setCountry(insertOrUpdateDO.getCountry()); + selectWxAppletUser.setProvince(insertOrUpdateDO.getProvince()); + selectWxAppletUser.setCity(insertOrUpdateDO.getCity()); + selectWxAppletUser.setMobile(insertOrUpdateDO.getMobile()); + selectWxAppletUser.setOpenId(insertOrUpdateDO.getOpenId()); + selectWxAppletUser.setUnionId(insertOrUpdateDO.getUnionId()); + success = wxMaAuthorizeMapper.updateWxAppletUser(selectWxAppletUser); + } + + if (success <= 0) { + return Result.error(mobile, "绑定失败,请联系相关管理人员!"); + } + + Map resultMap = new HashMap<>(); + resultMap.put("openId", openId); + resultMap.put("wxOpenId", wxOpenId); + resultMap.put("mobile", mobile); + resultMap.put("headImgUrl", headImgUrl); + resultMap.put("userId", misUser.getId()); + resultMap.put("nickName", misUser.getNickName()); + resultMap.put("serialNo", insertOrUpdateDO.getSerialNo()); + resultMap.put("companyCode", insertOrUpdateDO.getCompanyCode()); + resultMap.put("companyName", insertOrUpdateDO.getCompanyName()); + return Result.success(resultMap, "绑定成功!"); + } + + /** + * 实名认证 + * + * @param params 参数对象 + *
    + *
  • userName: 用户名
  • + *
  • mobile: 手机号码
  • + *
  • idCard: 身份证号码
  • + *
+ * @return 返回操作结果 + */ + @Transactional(rollbackFor = Exception.class) + public Result userMobileVerify(Map params) { + String userName = params.get("userName").toString(); + String mobile = params.get("mobile").toString(); + String idCard = params.get("idCard").toString(); + + MisUser misUser = authorizeMapper.queryUserByMobile(mobile); + if (ObjectUtil.isEmpty(misUser)) { + return Result.error(mobile, "未查找到当前用户"); + } else { + if (!StringUtils.isBlank(misUser.getIdCard())) { + return Result.error(mobile, "当前已实名过,请勿重复认证,如需修改,请联系客服!"); + } else { + Result result = tencentUtils.releaseMobileVerify(userName, mobile, idCard); + if (result.isSuccess()) { + authorizeMapper.updateUserIdCard(idCard, userName, mobile); + } + return result; + } + } + } + + /** + * 微信用户登录 + * + * @param code 登录参数 + * @return 返回登录结果 + */ + public Result login(String code) { + Map params = new HashMap<>(); + try { + WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken("wx581b7b669d241777", "2d66509495044eea130e86e49d21be2d", code); + //unionId 做为查询 + String unionId = accessToken.getUnionId(); + String wxOpenId = accessToken.getOpenId(); + WxOAuth2UserInfo wxOAuth2UserInfo = wxMpService.getOAuth2Service() + .getUserInfo(accessToken, wxOpenId); + System.out.println(wxOAuth2UserInfo.toString()); + } catch (WxErrorException e) { + params.put("code", "systemException"); + return Result.error(params, e.getError().getErrorMsg()); + } + return Result.success(params); + } + + /** + * 通过openId校验人员是否在该公司 + * + * @param wxAppletCode 小程序标识 + * @param code 登录标识 + * @param appId 小程序APPId + * @param companyCode 公司标识 + * @param companyName 公司名称 + * @return 返回校验结果 + */ + public Result checkWxRegisterUser(String wxAppletCode, String code, String appId, String companyCode, String companyName) { + // 参数校验 + if (!wxMaService.switchover(appId)) { + throw new IllegalArgumentException(String.format("未找到对应 AppID=[%s] 的配置,请核实!", appId)); + } + + Map params = new HashMap<>(); + try { + WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); + String wxOpenId = accessToken.getOpenId(); + WxMpUser wxMpUser = wxMpService.getUserService().userInfo(wxOpenId); + boolean subscribe = wxMpUser.getSubscribe(); + WxOAuth2UserInfo wxOAuth2UserInfo = wxMpService.getOAuth2Service().getUserInfo(accessToken, wxOpenId); + String headImgUrl = wxOAuth2UserInfo.getHeadImgUrl(); + log.info("微信用户 ===> {}", wxOAuth2UserInfo.toString()); + + // 公众号校验 + if (!subscribe) { + params.put("code", "unSubscribe"); + return Result.error(params, "未进行公众号绑定!"); + } + + // 登录Session + WxMaJscode2SessionResult session = wxMaService.getUserService().getSessionInfo(wxAppletCode); + String wxAppletOpenId = session.getOpenid(); + + // 校验用户是否绑定 + WxAppletUser selectWxAppletUser = wxMaConfigMapper.getWxAppletUser(wxAppletOpenId, companyCode); + if (ObjectUtils.isEmpty(selectWxAppletUser)) { + // 返回对应的token + String sessionKey = session.getSessionKey(); + String loginAccessToken = createToken(wxAppletOpenId, sessionKey, appId, wxOpenId, headImgUrl, + companyCode, companyName); + params.put("accessToken", loginAccessToken); + params.put("code", "unUserRegister"); + return Result.error(params, "未注册!"); + } + + // 校验是否存在对应注册用户 + MisComUserDto misComUserDto = wxMaAuthorizeMapper.queryCompanyUser(companyCode, selectWxAppletUser.getMobile()); + if (ObjectUtils.isEmpty(misComUserDto)) { + // 返回对应的token + String sessionKey = session.getSessionKey(); + String loginAccessToken = createToken(wxAppletOpenId, sessionKey, appId, wxOpenId, headImgUrl, + companyCode, companyName); + params.put("accessToken", loginAccessToken); + params.put("code", "unUserRegister"); + return Result.error(params, "未注册!"); + } + + // 已注册 状态为禁用 需提示 + if (misComUserDto.getState() == 0) { + params.put("code", "userException"); + return Result.error(null, "您已注册过,请勿重复注册,当前账号异常,请联系相关人员!"); + } + + //校验通过 返回小程序openId + params.put("openId", wxAppletOpenId); + params.put("wxOpenId", wxOpenId); + params.put("companyCode", companyCode); + return Result.success(params); + } catch (WxErrorException e) { + params.put("code", "systemException"); + return Result.error(params, e.getError().getErrorMsg()); + } + } + + /** + * 小程序 用户注册 + * + * @param registerUser 参数对象 + * @return 返回操作结果 + */ + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public Result registerCompanyUser(RegisterUser registerUser) { + // 校验对应手机号码是否存在绑定用户 + MisUser misUser = authorizeMapper.queryUserByMobile(registerUser.getMobile()); + Integer userId = null; + if (ObjectUtils.isEmpty(misUser)) { + MisUser user = new MisUser(); + user.setRealName(registerUser.getNickName()); + user.setNickName(registerUser.getNickName()); + user.setName(registerUser.getNickName()); + user.setEmail(StringUtils.isBlank(registerUser.getEmail()) ? "" : registerUser.getEmail()); + user.setMobile(registerUser.getMobile()); + PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); + String password = passwordEncoder.encode(registerUser.getMobile()); + user.setPassword(password.substring(8)); + user.setState(1); + user.setRegTime(new Date()); + authorizeMapper.insertUser(user); + userId = user.getId(); + } else { + misUser.setRealName(registerUser.getNickName()); + misUser.setNickName(registerUser.getNickName()); + misUser.setName(registerUser.getNickName()); + if (!StringUtils.isBlank(registerUser.getEmail())) { + misUser.setEmail(registerUser.getEmail()); + } + //更新 + userId = misUser.getId(); + authorizeMapper.updateRegisterUser(misUser); + } + + // 查询相关公司用户数据并进行处理更新 + List comUserList = wxMaAuthorizeMapper.getUserCompanyList(registerUser.getMobile()); + if (!CollectionUtils.isEmpty(comUserList)) { + for (MisComUserDto companyUser : comUserList) { + wxMaAuthorizeMapper.updateCompanyDefault(companyUser.getCompanyCode(), userId, 0); + } + } + + // 查询当前登录公司用户对象 + MisComUserDto selectCompanyUser = wxMaAuthorizeMapper.queryCompanyUser(registerUser.getCompanyCode(), registerUser.getMobile()); + String serialNo = StringUtils.isBlank(registerUser.getSerialNo()) ? ("No" + (int) ((Math.random() * 9 + 1) * 100000)) : registerUser.getSerialNo(); + if (ObjectUtils.isEmpty(selectCompanyUser)) { + MisComUserDto companyUser = new MisComUserDto(); + companyUser.setUserId(userId); + companyUser.setCompanyCode(registerUser.getCompanyCode()); + companyUser.setSerialNo(serialNo); + companyUser.setState(1); + companyUser.setIsDefault(1); + companyUser.setIsOwner(false); + authorizeMapper.insertCompanyUser(companyUser); + } else { + selectCompanyUser.setSerialNo(serialNo); + selectCompanyUser.setState(1); + selectCompanyUser.setIsDefault(1); + authorizeMapper.updateCompanyUser(selectCompanyUser); + } + + //补足用户信息 + registerUser.setUserId(userId); + registerUser.setSerialNo(serialNo); + return Result.success(registerUser, "注册成功!"); + } + + /** + * 创建访问Token + * + * @param openId 小程序用户openId + * @param sessionKey session关键词 + * @param appId 小程序APPID + * @param wxOpenId 微信用户OpenId + * @param headImgUrl 头像地址 + * @param companyCode 公司标识 + * @param companyName 公司名称 + * @return 返回创建结果 + */ + private static String createToken(String openId, String sessionKey, String appId, String wxOpenId, + String headImgUrl, String companyCode, String companyName) { + Map objectMap = new HashMap<>(); + objectMap.put("openId", openId); + objectMap.put("sessionKey", sessionKey); + objectMap.put("appId", appId); + objectMap.put("wxOpenId", wxOpenId); + objectMap.put("headImgUrl", headImgUrl); + if (StringUtils.isNotEmpty(companyCode)) { + objectMap.put("companyCode", companyCode); + } + if (StringUtils.isNotEmpty(companyName)) { + objectMap.put("companyName", companyName); + } + return EncryptUtil.parseByte2HexStr(Objects.requireNonNull(EncryptUtil.encrypt(JSON.toJSONString(objectMap), secretKey))); + } + + /** + * 创建AppUser + * + * @param rawData 行数据 + * @param encryptData 加密数据 + * @param iv 参数IV + * @param sessionKey session关键词 + * @param openId 用户openId + * @return 返回创建对象 + */ + private WxAppletUser buildWechatUserAuthInfoDO(String rawData, String encryptData, String iv, String sessionKey, String openId) { + WxAppletUser wxAppletUser = new WxAppletUser(); + wxAppletUser.setOpenId(openId); + + if (!StringUtils.isBlank(rawData)) { + RawDataDO rawDataDO = JSON.parseObject(rawData, RawDataDO.class); + wxAppletUser.setNickName(rawDataDO.getNickName()); + wxAppletUser.setGender(rawDataDO.getGender()); + wxAppletUser.setCity(rawDataDO.getCity()); + wxAppletUser.setCountry(rawDataDO.getCountry()); + wxAppletUser.setProvince(rawDataDO.getProvince()); + } + + // 解密加密信息,获取unionID + if (!StringUtils.isBlank(encryptData)) { + JSONObject encryptedData = getEncryptedData(encryptData, sessionKey, iv); + if (encryptedData != null) { + String unionId = encryptedData.getString("unionId"); + wxAppletUser.setUnionId(unionId); + } + } + + return wxAppletUser; + } + + /** + * 加密 + * + * @param encryptedData 加密数据 + * @param sessionKey session关键词 + * @param iv 参数IV + * @return 返回加密结果 + */ + private JSONObject getEncryptedData(String encryptedData, String sessionKey, String iv) { + // 被加密的数据 + byte[] dataByte = Base64.decode(encryptedData); + // 加密秘钥 + byte[] keyByte = Base64.decode(sessionKey); + // 偏移量 + byte[] ivByte = Base64.decode(iv); + try { + // 如果密钥不足16位,那么就补足.这个if中的内容很重要 + int base = 16; + if (keyByte.length % base != 0) { + int groups = keyByte.length / base + 1; + byte[] temp = new byte[groups * base]; + Arrays.fill(temp, (byte) 0); + System.arraycopy(keyByte, 0, temp, 0, keyByte.length); + keyByte = temp; + } + // 初始化 + Security.addProvider(new BouncyCastleProvider()); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); + SecretKeySpec spec = new SecretKeySpec(keyByte, "AES"); + AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES"); + parameters.init(new IvParameterSpec(ivByte)); + cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化 + byte[] resultByte = cipher.doFinal(dataByte); + if (null != resultByte && resultByte.length > 0) { + String result = new String(resultByte, StandardCharsets.UTF_8); + return JSONObject.parseObject(result); + } + } catch (Exception e) { + log.error("解密加密信息报错 ==> {}", e.getMessage()); + } + return null; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserService.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserService.java new file mode 100644 index 0000000..cdd9585 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserService.java @@ -0,0 +1,6 @@ +package cn.crtech.cloud.auth.service.wx.chat; + +import org.springframework.security.core.userdetails.UserDetailsService; + +public interface WxChatUserService extends UserDetailsService { +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserServiceImpl.java b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserServiceImpl.java new file mode 100644 index 0000000..7c83b28 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/service/wx/chat/WxChatUserServiceImpl.java @@ -0,0 +1,131 @@ +package cn.crtech.cloud.auth.service.wx.chat; + +import cn.crtech.cloud.auth.dto.MisComRoleUserDto; +import cn.crtech.cloud.auth.dto.MisComUserDto; +import cn.crtech.cloud.auth.mapper.AuthorizeMapper; +import cn.crtech.cloud.auth.mapper.WxMaAuthorizeMapper; +import cn.crtech.cloud.auth.pojo.*; +import cn.crtech.cloud.common.constant.MessageConstant; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.LockedException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.util.CollectionUtils; + +import java.util.*; + +@Slf4j +@Configuration +public class WxChatUserServiceImpl implements WxChatUserService { + private AuthorizeMapper authorizeMapper; + private WxMaAuthorizeMapper wxMaAuthorizeMapper; + + @Autowired + public WxChatUserServiceImpl(AuthorizeMapper authorizeMapper, WxMaAuthorizeMapper wxMaAuthorizeMapper) { + this.authorizeMapper = authorizeMapper; + this.wxMaAuthorizeMapper = wxMaAuthorizeMapper; + } + + /** + * 重构微信用户登录内容 + * + * @param username 登录账号 + * @return 返回登录处理结果 + * @throws UsernameNotFoundException 抛出异常 + */ + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 校验非空参数 + if (StringUtils.isBlank(username)) { + throw new UsernameNotFoundException(MessageConstant.ACCOUNT_SIGNATURE_ERROR); + } + // 查询并校验对应微信用户是否存在 + WxAppletUser selectWxAppletUser = wxMaAuthorizeMapper.getUserByWxOpenId(username); + if (ObjectUtils.isEmpty(selectWxAppletUser)) { + throw new UsernameNotFoundException(MessageConstant.ACCOUNT_ERROR); + } + // 查询并校验对应系统用户是否存在 + MisUser user = authorizeMapper.queryUserByMobile(selectWxAppletUser.getMobile()); + if (ObjectUtils.isEmpty(user)) { + throw new UsernameNotFoundException(MessageConstant.ACCOUNT_ERROR); + } + + // 生成用户绑定企业信息相关查询参数 + Map params = new HashMap<>(); + params.put("userId", user.getId()); + + //生成认证用户信息 + AuthorizeUser authorizeUser = new AuthorizeUser(user.getId(), user.getPassword(), user.getState(), user.getMobile(), user.getName(), user.getEmail(), "", false); +// getUserAuthorize(user, params, authorizeUser); + + SecurityUser securityUser = new SecurityUser(authorizeUser); + if (!securityUser.isEnabled()) { + throw new DisabledException(MessageConstant.ACCOUNT_DISABLED); + } + if (!securityUser.isAccountNonLocked()) { + throw new LockedException(MessageConstant.ACCOUNT_LOCKED); + } + if (!securityUser.isAccountNonExpired()) { + throw new AccountExpiredException(MessageConstant.ACCOUNT_EXPIRED); + } + if (!securityUser.isCredentialsNonExpired()) { + throw new CredentialsExpiredException(MessageConstant.CREDENTIALS_EXPIRED); + } + return securityUser; + } + + /** + * 用户角色绑定信息补充 + * + * @param user 用户对象 + * @param params 参数对象 + * @param authorizeUser 授权用户对象 + */ + private void getUserAuthorize(User user, Map params, AuthorizeUser authorizeUser) { + List comUserList = authorizeMapper.queryCompanyUserByUserId(params); + if (CollectionUtils.isEmpty(comUserList)) { + List userRoleList = new ArrayList<>(); + userRoleList.add("ALL"); + authorizeUser.setRoles(userRoleList); + authorizeUser.setDataSource(""); + } else { + // 获取当前企业 + MisComUserDto defaultCompany = comUserList.get(0); + + authorizeUser.setCompanyCode(defaultCompany.getCompanyCode()); + authorizeUser.setCompanyName(defaultCompany.getCompanyName()); + List userRoleList = new ArrayList<>(); + userRoleList.add("ALL"); + + // 设置企业管理员 + if (defaultCompany.getIsOwner()) { + userRoleList.add(defaultCompany.getCompanyCode() + "_admin"); + authorizeUser.setIsCompanyAdmin(true); + } else { + List userRoles = authorizeMapper.queryCompanyRoleUser(user.getId(), defaultCompany.getCompanyCode()); + userRoles.forEach(role -> { + userRoleList.add(role.getCompanyCode() + "_" + role.getRoleId()); + }); + + String roleCodes = authorizeMapper.queryUserRole(user.getId(), defaultCompany.getCompanyCode()); + if (!StringUtils.isBlank(roleCodes)) { + String[] roleCodeArray = roleCodes.split(","); + userRoleList.addAll(Arrays.asList(roleCodeArray)); + } + String defaultRoleCodes = authorizeMapper.queryUserDefaultRole(user.getId(), defaultCompany.getCompanyCode()); + if (!StringUtils.isBlank(defaultRoleCodes)) { + String[] roleCodeArray = defaultRoleCodes.split(","); + userRoleList.addAll(Arrays.asList(roleCodeArray)); + } + } + authorizeUser.setRoles(userRoleList); + } + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/utils/TencentUtils.java b/Auth/src/main/java/cn/crtech/cloud/auth/utils/TencentUtils.java new file mode 100644 index 0000000..154510a --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/utils/TencentUtils.java @@ -0,0 +1,65 @@ +package cn.crtech.cloud.auth.utils; + +import cn.crtech.cloud.common.dto.Result; +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.faceid.v20180301.FaceidClient; +import com.tencentcloudapi.faceid.v20180301.models.PhoneVerificationRequest; +import com.tencentcloudapi.faceid.v20180301.models.PhoneVerificationResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class TencentUtils { + @Value("${tencent.release.mobile.secretId}") + private String secretId; + + @Value("${tencent.release.mobile.secretKey}") + private String secretKey; + + public Result releaseMobileVerify(String userName, String mobile, String idCard) { + try { + // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密 + Credential cred = new Credential(secretId, secretKey); + + // 实例化一个http选项,可选的,没有特殊需求可以跳过 + HttpProfile httpProfile = new HttpProfile(); + httpProfile.setEndpoint("faceid.tencentcloudapi.com"); + + // 实例化一个client选项,可选的,没有特殊需求可以跳过 + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + + // 实例化要请求产品的client对象,clientProfile是可选的 + FaceidClient client = new FaceidClient(cred, "ap-guangzhou", clientProfile); + + // 实例化一个请求对象,每个接口都会对应一个request对象 + PhoneVerificationRequest req = new PhoneVerificationRequest(); + req.setName(userName); + req.setIdCard(idCard); + req.setPhone(mobile); + + // 返回的resp是一个PhoneVerificationResponse的实例,与请求对象对应 + PhoneVerificationResponse resp = client.PhoneVerification(req); + + // 输出json格式的字符串回包 + log.info("三要素核验结果 ==> {} ", PhoneVerificationResponse.toJsonString(resp)); + int result = Integer.parseInt(resp.getResult()); + if (result == 0) { + //认证通过 + return Result.success(null, resp.getDescription()); + } else if (result == -4) { + return Result.error(null, "手机号码未匹配!"); + } else { + return Result.error(null, resp.getDescription()); + } + } catch (TencentCloudSDKException e) { + log.info("手机三要素核验异常,错误代码 ==> {} , 错误内容 ==> {}", e.getErrorCode(), e.getMessage()); + return Result.error(null, e.getMessage()); + } + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFailureHandler.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFailureHandler.java new file mode 100644 index 0000000..3e6376f --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFailureHandler.java @@ -0,0 +1,17 @@ +package cn.crtech.cloud.auth.wx.applet; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class WxAppletAuthenticationFailureHandler implements AuthenticationFailureHandler { + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + response.setContentType("text/plain;charset=UTF-8"); + response.getWriter().write("token失效!"); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFilter.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFilter.java new file mode 100644 index 0000000..f579433 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationFilter.java @@ -0,0 +1,71 @@ +package cn.crtech.cloud.auth.wx.applet; + +import com.sun.istack.Nullable; +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.util.matcher.AntPathRequestMatcher; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class WxAppletAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + // 设置拦截/sms/login短信登录接口 + private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/wx/applet/oauth/token", "POST"); + + // 认证 请求参数 + private static final String accessTokenParam = "openId"; + private static final String companyCode = "companyCode"; + private boolean postOnly = true; + + public WxAppletAuthenticationFilter() { + super(DEFAULT_ANT_PATH_REQUEST_MATCHER); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { + if (this.postOnly && !"POST".equals(request.getMethod())) { + throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); + } else { + String accessToken = this.obtainAccessToken(request); + accessToken = accessToken != null ? accessToken : ""; + accessToken = accessToken.trim(); + + String companyCode = this.obtainCompanyCode(request); + companyCode = (companyCode != null) ? companyCode : ""; + + WxAppletAuthenticationToken authRequest = new WxAppletAuthenticationToken(accessToken, companyCode); + this.setDetails(request, authRequest); + // 认证信息 + return this.getAuthenticationManager().authenticate(authRequest); + } + } + + protected String obtainCompanyCode(HttpServletRequest request) { + return request.getParameter(this.companyCode); + } + + @Nullable + protected String obtainAccessToken(HttpServletRequest request) { + return request.getParameter(this.accessTokenParam); + } + + protected void setDetails(HttpServletRequest request, WxAppletAuthenticationToken authRequest) { + authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + } + + public void setPostOnly(boolean postOnly) { + this.postOnly = postOnly; + } + + public final String getUsernameParameter() { + return this.accessTokenParam; + } + + public final String getPasswordParameter() { + return this.companyCode; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationProvider.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationProvider.java new file mode 100644 index 0000000..aae7397 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationProvider.java @@ -0,0 +1,50 @@ +package cn.crtech.cloud.auth.wx.applet; + +import cn.crtech.cloud.auth.service.wx.applet.WxAppletUserServiceImpl; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +@Component +public class WxAppletAuthenticationProvider implements AuthenticationProvider { + private final WxAppletUserServiceImpl wxAppletUserService; + + public WxAppletAuthenticationProvider(WxAppletUserServiceImpl wxAppletUserServiceImpl) { + this.wxAppletUserService = wxAppletUserServiceImpl; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + WxAppletAuthenticationToken wxAppletAuthenticationToken = (WxAppletAuthenticationToken) authentication; + Object principal = authentication.getPrincipal();//token + Object credentials = authentication.getCredentials(); + String accessToken = ""; + if (principal instanceof String) { + accessToken = (String) principal; + } + String companyCode = ""; + if (credentials != null) { + if (credentials instanceof String) { + companyCode = (String) credentials; + } + } + + UserDetails userDetails = wxAppletUserService.loadUserByUsername(accessToken, companyCode); + if (userDetails == null) { + throw new InternalAuthenticationServiceException("认证失败!"); + } + + //重新创建已认证对象, + WxAppletAuthenticationToken authenticationResult = new WxAppletAuthenticationToken(userDetails, companyCode, userDetails.getAuthorities()); + authenticationResult.setDetails(wxAppletAuthenticationToken.getDetails()); + return authenticationResult; + } + + @Override + public boolean supports(Class authentication) { + return WxAppletAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationSuccessHandler.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationSuccessHandler.java new file mode 100644 index 0000000..5bb42a3 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationSuccessHandler.java @@ -0,0 +1,66 @@ +package cn.crtech.cloud.auth.wx.applet; + +import cn.crtech.cloud.auth.component.JwtTokenEnhancer; +import cn.crtech.cloud.auth.pojo.Oauth2Token; +import com.alibaba.fastjson.JSON; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; + +@Component +public class WxAppletAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); + ClientDetailsService clientDetailsService = (ClientDetailsService) factory.getBean("clientDetailsService"); + AuthorizationServerTokenServices authorizationServerTokenServices = (AuthorizationServerTokenServices) factory.getBean("defaultAuthorizationServerTokenServices"); + JwtTokenEnhancer jwtTokenEnhancer = (JwtTokenEnhancer) factory.getBean("jwtTokenEnhancer"); + PasswordEncoder passwordEncoder = (PasswordEncoder) factory.getBean("passwordEncoder"); + + // 1. 从请求头中获取 ClientId + String clientId = request.getParameter("client_id"); + String clientSecret = request.getParameter("client_secret"); + // 2. 通过 ClientDetailsService 获取 ClientDetails + ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); + + TokenRequest tokenRequest = null; + // 3. 校验 ClientId和 ClientSecret的正确性 + if (clientDetails == null) { + throw new UnapprovedClientAuthenticationException("clientId:" + clientId + "对应的信息不存在"); + } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) { + throw new UnapprovedClientAuthenticationException("clientSecret不正确"); + } else { + // 4. 通过 TokenRequest构造器生成 TokenRequest + tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), "custom"); + } + + // 5. 通过 TokenRequest的 createOAuth2Request方法获取 OAuth2Request + OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); + // 6. 通过 Authentication和 OAuth2Request构造出 OAuth2Authentication + OAuth2Authentication auth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); + // 7. 通过 AuthorizationServerTokenServices 生成 OAuth2AccessToken + OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(auth2Authentication); + OAuth2AccessToken resultToken = jwtTokenEnhancer.enhance(token, auth2Authentication); + Oauth2Token oauth2TokenDto = Oauth2Token.builder() + .token(resultToken.getValue()) + .refreshToken(resultToken.getRefreshToken().getValue()) + .expiresIn(resultToken.getExpiresIn()) + .tokenHead("Bearer ").build(); + // 8. 返回 Token + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(JSON.toJSONString(oauth2TokenDto)); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationToken.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationToken.java new file mode 100644 index 0000000..fb538b3 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletAuthenticationToken.java @@ -0,0 +1,60 @@ +package cn.crtech.cloud.auth.wx.applet; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; + +import java.util.Collection; + +public class WxAppletAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + //小程序openId + private final Object principal; + //企业code + private Object credentials; + + /** + * Creates a token with the supplied array of authorities. + * + * @param principal + */ + public WxAppletAuthenticationToken(Object principal, Object credentials) { + super(null); + this.principal = principal; + this.credentials = credentials; + setAuthenticated(false); + } + + public WxAppletAuthenticationToken(Object principal, Object credentials, Collection authorities) { + super(authorities); + this.principal = principal; + this.credentials = credentials; + super.setAuthenticated(true); // must use super, as we override + } + + @Override + public Object getCredentials() { + return this.credentials; + } + + @Override + 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(); + this.credentials = null; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletSecurityConfigurerConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletSecurityConfigurerConfig.java new file mode 100644 index 0000000..ec735fc --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/applet/WxAppletSecurityConfigurerConfig.java @@ -0,0 +1,30 @@ +package cn.crtech.cloud.auth.wx.applet; + +import cn.crtech.cloud.auth.service.wx.applet.WxAppletUserServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.stereotype.Component; + +@Component +public class WxAppletSecurityConfigurerConfig extends SecurityConfigurerAdapter { + @Autowired + private WxAppletUserServiceImpl wxAppletUserServiceImpl; + + @Override + public void configure(HttpSecurity http) throws Exception { + //自定义SmsCodeAuthenticationFilter过滤器 + WxAppletAuthenticationFilter wxAppletAuthenticationFilter = new WxAppletAuthenticationFilter(); + wxAppletAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); + wxAppletAuthenticationFilter.setAuthenticationSuccessHandler(new WxAppletAuthenticationSuccessHandler()); + // 设置认证失败处理Handler + wxAppletAuthenticationFilter.setAuthenticationFailureHandler(new WxAppletAuthenticationFailureHandler()); + WxAppletAuthenticationProvider wxAppletAuthenticationProvider = new WxAppletAuthenticationProvider(wxAppletUserServiceImpl); + //在UsernamePasswordAuthenticationFilter过滤前执行 + http.authenticationProvider(wxAppletAuthenticationProvider) + .addFilterAfter(wxAppletAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFailureHandler.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFailureHandler.java new file mode 100644 index 0000000..a1a71b1 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFailureHandler.java @@ -0,0 +1,17 @@ +package cn.crtech.cloud.auth.wx.chat; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class WxChatAuthenticationFailureHandler implements AuthenticationFailureHandler { + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + response.setContentType("text/plain;charset=UTF-8"); + response.getWriter().write("token失效!"); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFilter.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFilter.java new file mode 100644 index 0000000..d72419b --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationFilter.java @@ -0,0 +1,59 @@ +package cn.crtech.cloud.auth.wx.chat; + +import com.sun.istack.Nullable; +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.util.matcher.AntPathRequestMatcher; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class WxChatAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + // 设置拦截/sms/login短信登录接口 + private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/wx/chat/oauth/token", "POST"); + + // 认证 请求参数 + private static final String accessTokenParam = "openId"; + private boolean postOnly = true; + + public WxChatAuthenticationFilter() { + super(DEFAULT_ANT_PATH_REQUEST_MATCHER); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { + if (this.postOnly && !"POST".equals(request.getMethod())) { + throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); + } else { + String accessToken = this.obtainAccessToken(request); + accessToken = accessToken != null ? accessToken : ""; + accessToken = accessToken.trim(); + + WxChatAuthenticationToken authRequest = new WxChatAuthenticationToken(accessToken); + this.setDetails(request, authRequest); + // 认证信息 + return this.getAuthenticationManager().authenticate(authRequest); + } + } + + @Nullable + protected String obtainAccessToken(HttpServletRequest request) { + return request.getParameter(this.accessTokenParam); + } + + protected void setDetails(HttpServletRequest request, WxChatAuthenticationToken authRequest) { + authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); + } + + public void setPostOnly(boolean postOnly) { + this.postOnly = postOnly; + } + + public final String getUsernameParameter() { + return this.accessTokenParam; + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationProvider.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationProvider.java new file mode 100644 index 0000000..ae76ab5 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationProvider.java @@ -0,0 +1,44 @@ +package cn.crtech.cloud.auth.wx.chat; + +import cn.crtech.cloud.auth.service.wx.chat.WxChatUserServiceImpl; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +@Component +public class WxChatAuthenticationProvider implements AuthenticationProvider { + private final WxChatUserServiceImpl wxChatUserService; + + public WxChatAuthenticationProvider(WxChatUserServiceImpl wxChatUserService) { + this.wxChatUserService = wxChatUserService; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + WxChatAuthenticationToken wxAppletAuthenticationToken = (WxChatAuthenticationToken) authentication; + Object principal = authentication.getPrincipal();//openId + + String accessToken = ""; + if (principal instanceof String) { + accessToken = (String) principal; + } + + UserDetails userDetails = wxChatUserService.loadUserByUsername(accessToken); + if (userDetails == null) { + throw new InternalAuthenticationServiceException("认证失败!"); + } + + //重新创建已认证对象, + WxChatAuthenticationToken authenticationResult = new WxChatAuthenticationToken(userDetails, userDetails.getAuthorities()); + authenticationResult.setDetails(wxAppletAuthenticationToken.getDetails()); + return authenticationResult; + } + + @Override + public boolean supports(Class authentication) { + return WxChatAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationSuccessHandler.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationSuccessHandler.java new file mode 100644 index 0000000..5fffaa1 --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationSuccessHandler.java @@ -0,0 +1,66 @@ +package cn.crtech.cloud.auth.wx.chat; + +import cn.crtech.cloud.auth.component.JwtTokenEnhancer; +import cn.crtech.cloud.auth.pojo.Oauth2Token; +import com.alibaba.fastjson.JSON; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException; +import org.springframework.security.oauth2.provider.*; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; + +@Component +public class WxChatAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); + ClientDetailsService clientDetailsService = (ClientDetailsService) factory.getBean("clientDetailsService"); + AuthorizationServerTokenServices authorizationServerTokenServices = (AuthorizationServerTokenServices) factory.getBean("defaultAuthorizationServerTokenServices"); + JwtTokenEnhancer jwtTokenEnhancer = (JwtTokenEnhancer) factory.getBean("jwtTokenEnhancer"); + PasswordEncoder passwordEncoder = (PasswordEncoder) factory.getBean("passwordEncoder"); + + // 1. 从请求头中获取 ClientId + String clientId = request.getParameter("client_id"); + String clientSecret = request.getParameter("client_secret"); + // 2. 通过 ClientDetailsService 获取 ClientDetails + ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); + + TokenRequest tokenRequest = null; + // 3. 校验 ClientId和 ClientSecret的正确性 + if (clientDetails == null) { + throw new UnapprovedClientAuthenticationException("clientId:" + clientId + "对应的信息不存在"); + } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) { + throw new UnapprovedClientAuthenticationException("clientSecret不正确"); + } else { + // 4. 通过 TokenRequest构造器生成 TokenRequest + tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), "custom"); + } + + // 5. 通过 TokenRequest的 createOAuth2Request方法获取 OAuth2Request + OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); + // 6. 通过 Authentication和 OAuth2Request构造出 OAuth2Authentication + OAuth2Authentication auth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); + // 7. 通过 AuthorizationServerTokenServices 生成 OAuth2AccessToken + OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(auth2Authentication); + OAuth2AccessToken resultToken = jwtTokenEnhancer.enhance(token, auth2Authentication); + Oauth2Token oauth2TokenDto = Oauth2Token.builder() + .token(resultToken.getValue()) + .refreshToken(resultToken.getRefreshToken().getValue()) + .expiresIn(resultToken.getExpiresIn()) + .tokenHead("Bearer ").build(); + // 8. 返回 Token + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(JSON.toJSONString(oauth2TokenDto)); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationToken.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationToken.java new file mode 100644 index 0000000..337bf5c --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatAuthenticationToken.java @@ -0,0 +1,55 @@ +package cn.crtech.cloud.auth.wx.chat; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; + +import java.util.Collection; + +public class WxChatAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + //openId + private final Object principal; + + /** + * Creates a token with the supplied array of authorities. + * + * @param principal + */ + public WxChatAuthenticationToken(Object principal) { + super(null); + this.principal = principal; + setAuthenticated(false); + } + + public WxChatAuthenticationToken(Object principal, Collection authorities) { + super(authorities); + this.principal = principal; + super.setAuthenticated(true); // must use super, as we override + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + 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(); + } +} diff --git a/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatSecurityConfigurerConfig.java b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatSecurityConfigurerConfig.java new file mode 100644 index 0000000..da6713c --- /dev/null +++ b/Auth/src/main/java/cn/crtech/cloud/auth/wx/chat/WxChatSecurityConfigurerConfig.java @@ -0,0 +1,30 @@ +package cn.crtech.cloud.auth.wx.chat; + +import cn.crtech.cloud.auth.service.wx.chat.WxChatUserServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.stereotype.Component; + +@Component +public class WxChatSecurityConfigurerConfig extends SecurityConfigurerAdapter { + @Autowired + private WxChatUserServiceImpl wxChatUserService; + + @Override + public void configure(HttpSecurity http) throws Exception { + //自定义SmsCodeAuthenticationFilter过滤器 + WxChatAuthenticationFilter wxAppletAuthenticationFilter = new WxChatAuthenticationFilter(); + wxAppletAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class)); + wxAppletAuthenticationFilter.setAuthenticationSuccessHandler(new WxChatAuthenticationSuccessHandler()); + // 设置认证失败处理Handler + wxAppletAuthenticationFilter.setAuthenticationFailureHandler(new WxChatAuthenticationFailureHandler()); + WxChatAuthenticationProvider wxAppletAuthenticationProvider = new WxChatAuthenticationProvider(wxChatUserService); + //在UsernamePasswordAuthenticationFilter过滤前执行 + http.authenticationProvider(wxAppletAuthenticationProvider) + .addFilterAfter(wxAppletAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/Auth/src/main/resources/application-dev.yml b/Auth/src/main/resources/application-dev.yml new file mode 100644 index 0000000..f154494 --- /dev/null +++ b/Auth/src/main/resources/application-dev.yml @@ -0,0 +1,28 @@ +server: + port: 9401 + servlet: + encoding: + charset: utf-8 + enabled: true + force: true + tomcat: + uri-encoding: UTF-8 +management: + endpoints: + web: + exposure: + include: "*" +mybatis: + mapper-locations: classpath*:/mapping/*Mapper.xml + type-aliases-package: cn.crtech.cloud.auth.pojo,cn.crtech.cloud.dto +logging: + config: classpath:logback.xml + file: + path: logs/crtech-cloud-auth.log + level: + cn.crtech.cloud.auth: debug +tencent: + release: + mobile: + secretId: AKIDZ5PL3jgQFnshIoD6wpPr32vaptkHDsmY + secretKey: mQu42mHSP4hvsgXAVbYbIgnsC7l8NZLv diff --git a/Auth/src/main/resources/bootstrap-dev.yml b/Auth/src/main/resources/bootstrap-dev.yml new file mode 100644 index 0000000..3b8de15 --- /dev/null +++ b/Auth/src/main/resources/bootstrap-dev.yml @@ -0,0 +1,17 @@ +spring: + application: + name: crtech-cloud-auth # 项目名称尽量用小写 + cloud: + nacos: + discovery: + server-addr: localhost:8848 + redis: + database: 4 + port: 6379 + host: localhost + password: + datasource: + url: jdbc:mysql://chaoran.crtech.cn:9803/cr_cloud_auth_back?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true + username: crtech + password: www.server41.com + diff --git a/Auth/src/main/resources/bootstrap.yml b/Auth/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..1ab53e5 --- /dev/null +++ b/Auth/src/main/resources/bootstrap.yml @@ -0,0 +1,6 @@ +spring: + profiles: + active: dev + + + diff --git a/Auth/src/main/resources/jwt.jks b/Auth/src/main/resources/jwt.jks new file mode 100644 index 0000000000000000000000000000000000000000..6339726c2e10592c5d4bfc05ca93d01ba47c01e1 GIT binary patch literal 2200 zcmcJQ`8(7L7r^H`GX^sud#)@|xU$R)V<}3Q>}D*JC1fqhzDvYpoe2pSA^VzA$XYSU zP*YrkLDASE%P8CtDqU~Sd+*afaDO<@b3W%e=Q+<=o^$qCdn^zL1l||mr$BrnLihFX zJ$fA<2*d#(iO}yzUKsp141fZPV15AL06~e+rDQoM#qLo}qs3FSWS#r8#rRa*5ZB<- zq93ri@u1VqD_1IKzR}Edq+?p26EbVLEEzJSFB$%(7Clih-S_n+r=)LP$Ql0fX9p@= zJ2+P?t=o8-dOAL3MZ$~C7(bV3rsQ`xRH;EhVk6qWrwH?KBfEg65pWaI&jiwVOob#vX-g6Po_=gSZt zzt~98d;FZ+;Irt!fcI}l;F|mq+C8|!OE37HjT-&Kz~>QjCPy0S6}b@^_xf(^;gAr$ z_!efRw%I(9BA-y^tS{SK|z9EUqD zQ9>T$uF_iK#9{RdDBFCTQq1u(Klt?z!1rYn@}2x1mS1e=N#(o~nrs)(_5dx%uX6T? z5Dg~1YC62QF3>74`_#Z{)>n2#chVHfrKv5H)G;028kC}=;Of7#v1=4jEi-*44!vYd zpSkVKbxIZmPEJe~&z;q)*fRf9X!danp4!Rc#@-E9j1votrYkM4AAf?nkHzY_pM$1o zQ#)u%bhn`LXUSiXd}XyrxRwpyyMXIx=Coj-D^ zL_m2`i+}RLg|jXi11QAndAHOx*J*c=+~|zAPvtO_;TX(kdxjlh2v3Y`;iY@5-= zqQx%`lP;8wNkC-FmDAixr3^M~*53{waxzWu!zofw(&`HXIcr`D>z=*`e)K|)ib?sbBR8R0pSO5`n)ugT5!@#GVQkU)N zOOCfKBj-YxPh6e)OX2eE*CW2mVY0k;au#4$Z%;c_SC)kuP8mKA5zm56w;NItrK7g} z<6Dl2S5v<9viOrGl2_v0;sijVoON^5F<*xD#!;x2$u$eF5ZPRhyyGFf!l#V)a)?kK zT}Ovcw5>|&!#jj$jPM&Q+E+z~8F`5y^lLqrJYnb2ByE`({6Jv>w5u?_dBmuGCNK4` zyHG`qc?L$4pIEfDDiveWRvjikTt$q+KmV|>j;+|f8giG`!jTi7ro!DEFvc@K#c__M zCDY*Z3}yKEWgH~%9V2NlcKr#BVzKBT?&h8aX8FRKT4d?5-pRY`Rf|S@wz}L6s*g$! zDo>?5uT;V)CI*v3@ZrM&9!(!Bn#1)FYTr$+6lZj2Jj|M*8rP##SHd2U)gcM*Up7Cj z!SY(mEE>1V(6!fm?fm9P+}VSd145M?y%fVl?KBh@M|F3k-D$D;EvsfVtw>p)k~aSV zUZh7|s9L$-(Z@3>AFKL=b+iR!O9w~=e@%oIjR==@2vQG`N}4-g*DUaT;=E& zyd9#%`~HtE7zR$W9d6?cTUaX@sX*!!Nx%9W7e#xA+8WI?`|ZgR?pyyJUVqLYpeb>> zK5w#Hq#E4S{kJ!;?&DOuOf%N3I?eS0zqt$L;-upFA484jAbSX3+vS#dd-#|>|4Wsd7 zP4wrIA$u!A`$4So33)IC00HGkkWxsA{eemFLPelL$6K^^<3JU%mNCO~ZrbRK$m_es zzcz7ySN59-fJAJRBu8&mDq! z1wLYDt3@cnpklq9A8UrusJQ_G9$}7|LF0Gfx+>w(lXIn z;c9lt(ZEH!@{qu*q6a|T1f%Zi`6E%r_qY3%0#^TnYT0cW={9x1X#bt?cLRl&mI@-iQlK>VeuG)M-#xvnnBl dKNf9G?DbXiAQq<}3H5@eUzygh{37wVzX5cR%Xk0) literal 0 HcmV?d00001 diff --git a/Auth/src/main/resources/logback.xml b/Auth/src/main/resources/logback.xml new file mode 100644 index 0000000..3cebe6e --- /dev/null +++ b/Auth/src/main/resources/logback.xml @@ -0,0 +1,56 @@ + + + + + + logs/crtech-cloud-auth.%d{yyyy-MM-dd}.log + + + + + %d{yyyy-MM-dd_HH:mm:ss} %logger{18} -%msg%n + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + 0 + 1000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file