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 参数对象
+ *
+ * - userName: 用户名
+ * - mobile: 手机号码
+ * - idCard: 身份证号码
+ *
+ * @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 extends GrantedAuthority> 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 extends GrantedAuthority> 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 extends GrantedAuthority> 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