Compare commits

...

12 Commits

249 changed files with 27437 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
/logs/
/.idea/
/Auth/target/
/Auth/Auth.iml
/Common/Common.iml
/Common/target/
/Dependencies/Dependencies.iml
/Dependencies/target/
/Feign/Feign.iml
/Feign/target/
/Gateway/Gateway.iml
/Gateway/target/
/LicenseAnalysis/LicenseAnalysis.iml
/LicenseAnalysis/target/
/MessageCenter/MessageCenter.iml
/MessageCenter/target/
/ResourceManager/ResourceManager.iml
/ResourceManager/target/
/WXEngine/WXEngine.iml
/WXEngine/target/
/Auth/src/main/resources/application-test.yml
/Auth/src/main/resources/bootstrap-test.yml
/Feign/src/main/resources/application-test.yml
/Feign/src/main/resources/bootstrap-test.yml
/Gateway/src/main/resources/application-test.yml
/ResourceManager/src/main/resources/application-test.yml
/ResourceManager/src/main/resources/bootstrap-test.yml
/WXEngine/src/main/resources/bootstrap-test.yml
/WXEngine/src/main/resources/application-test.yml
/General/src/main/resources/application-test.yml
/General/src/main/resources/bootstrap-test.yml
/General/General.iml
/General/target/

153
Auth/pom.xml Normal file
View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.crtech.cloud.auth</groupId>
<artifactId>Auth</artifactId>
<version>1.0.1</version>
<!-- 父工程 -->
<parent>
<groupId>cn.crtech.cloud.dependencies</groupId>
<artifactId>Dependencies</artifactId>
<version>1.0.1</version>
<relativePath/>
</parent>
<!-- 依赖的版本锁定 -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<nimbus.jwt.version>9.14</nimbus.jwt.version>
<oauth2.version>2.2.5.RELEASE</oauth2.version>
<cn.crtech.cloud.common>1.0.1</cn.crtech.cloud.common>
<weixin-java-miniapp.version>4.4.0</weixin-java-miniapp.version>
<weixin.mp.version>4.3.0</weixin.mp.version>
<tencentcloud.sdk.version>3.1.322</tencentcloud.sdk.version>
</properties>
<dependencies>
<!-- springboot-framework -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring-alibaba-cloud-client -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- jwt -->
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${nimbus.jwt.version}</version>
</dependency>
<!-- oauth2 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>${oauth2.version}</version>
<exclusions>
<exclusion>
<artifactId>bcpkix-jdk15on</artifactId>
<groupId>org.bouncycastle</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 自定义共有内容仓库 -->
<dependency>
<groupId>cn.crtech.cloud.common</groupId>
<artifactId>Common</artifactId>
<version>${cn.crtech.cloud.common}</version>
</dependency>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- 微信小程序JDK工具包 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>${weixin-java-miniapp.version}</version>
<exclusions>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 微信公众号JDK工具包 -->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>${weixin.mp.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</exclusion>
<exclusion>
<artifactId>weixin-java-common</artifactId>
<groupId>com.github.binarywang</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 腾讯云sdk -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>${tencentcloud.sdk.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<artifactId>okio</artifactId>
<groupId>com.squareup.okio</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,36 @@
package cn.crtech.cloud.auth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import tk.mybatis.spring.annotation.MapperScan;
/**
* Author : yj
* Date : 2021-01-13
* Description:
*/
//微服务启动时使用 begin
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("cn.crtech.cloud.auth.mapper")
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
//微服务启动时使用 end
//使用launcher启动时使用 begin
//launcher.NacosConfig @Component需要放开
//@SpringBootApplication
//public class AuthApplication extends SpringBootServletInitializer {
// @Override
// protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// return application.sources(AuthApplication.class);
// }
//}
//使用launcher启动时使用 end

View File

@ -0,0 +1,36 @@
package cn.crtech.cloud.auth.component;
import cn.crtech.cloud.auth.pojo.SecurityUser;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* JWT内容增强器
* token信息的额外信息处理
*/
@Component
public class JwtTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
Map<String, Object> 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;
}
}

View File

@ -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处理
* <p>
* 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 {
}
}

View File

@ -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<MisApp> applicationList = authorizeMapper.getAll();
//将客户端信息存储在内存中
applicationList.forEach(application -> {
ClientDetailsServiceBuilder<InMemoryClientDetailsServiceBuilder>.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<TokenEnhancer> 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());
}
}

View File

@ -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<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> 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;
}
}

View File

@ -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<String, String> map = (Map<String, String>) 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;
}
}

View File

@ -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<T extends Authentication> implements AuthenticationUserDetailsService<T>, 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<String, String> map = (Map<String, String>) principal.getDetails();
return this.authService.loadUserByUsername(authentication.getName(), map.get("companyCode")); // 使用自定义的userDetailsService
}
public void setUserDetailsService(AuthService authService) {
this.authService = authService;
}
}

View File

@ -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<String> scope = request.getScope();
OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
if (scope != null && !scope.isEmpty()) {
Set<String> 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;
}
}

View File

@ -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());
}
}

View File

@ -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<WxAppletConfig> 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<WxGzhConfig> 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;
}
}

View File

@ -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<Oauth2Token> postAccessToken(Principal principal, @RequestParam Map<String, String> 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);
}
}

View File

@ -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<String, Object> getKey() {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAKey key = new RSAKey.Builder(publicKey).build();
return new JWKSet(key).toJSONObject();
}
}

View File

@ -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 参数对象
* <ul>
* <li>userName: 用户名</li>
* <li>mobile: 手机号码</li>
* <li>idCard: 身份证号码</li>
* </ul>
* @return 返回操作结果
*/
@PostMapping("/userMobileVerify")
public Result userMobileVerify(@RequestBody Map<String, Object> 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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<MisRoleApiDto> authorityList;
}

View File

@ -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<ObjectName> 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();
}
}
}
}

View File

@ -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("<script> " +
"select " +
"a.code as companyCode , a.name as companyName , a.logo , " +
"a.province_name as provinceName , a.city_name as cityName , a.county_name as countyName , " +
"a.street as address, b.user_id AS userId, " +
"CASE WHEN a.owner_id = #{userId} then 1 ELSE 0 end as isOwner, " +
"IFNULL(b.position,'超级管理员') as position, " +
"IFNULL(b.is_default,1) AS isDefault, " +
"IFNULL(b.state,1) as state, " +
"b.serial_no as serialNo " +
"from mis_company a " +
"inner join mis_company_user b on b.company_code = a.code " +
"where b.user_id = #{userId} and b.state = 1 " +
" <if test='companyCode != null'> and a.code = #{companyCode} </if> " +
"order by b.is_default desc " +
"</script>")
@ResultType(MisComUserDto.class)
List<MisComUserDto> queryCompanyUserByUserId(Map<String, Object> 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<MisComRoleUserDto> 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<MisApp> 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<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> listFreeAuthority();
@Select("select " +
"CONCAT('/',code , '/' , code ,'Feign/**') as controller , 'ALL' as role " +
"from mis_app " +
"where state = 1 ")
@ResultType(MisRoleApiDto.class)
List<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> listDefaultRoleAuthorityController();
}

View File

@ -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<WxAppletUser> 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<WxAppletConfig> 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<WxGzhConfig> 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<MisComUserDto> 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);
}

View File

@ -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<String> 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() {
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<SimpleGrantedAuthority> 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<MisRoleApiDto> limitAuthority = authorizeMapper.listLimitApi();
if (!CollectionUtils.isEmpty(limitAuthority)) {
Map<String, String> 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<MisRoleApiDto> freeAppList = authorizeMapper.listFreeApp();
if (!CollectionUtils.isEmpty(freeAppList)) {
// 相关菜单加载
List<MisRoleApiDto> freeControllerList = authorizeMapper.listFreeController();
// 相关菜单特殊权限加载
List<MisRoleApiDto> 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<String, String> freeResourceMap = new TreeMap<>();
List<MisRoleApiDto> 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<MisRoleApiDto> 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<MisRoleApiDto> appFeignList = authorizeMapper.listAppFeignController();
if (!CollectionUtils.isEmpty(appFeignList)) {
Map<String, String> 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<MisRoleApiDto> comRoleApiList = authorizeMapper.listRoleController();
// 企业角色相关菜单特殊权限加载
List<MisRoleApiDto> comRoleAuthorityList = authorizeMapper.listRoleAuthorityController();
// 企业角色相关内容处理缓存
Map<String, String> 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<MisRoleApiDto> comAdminController = authorizeMapper.getCompanyAdminController();
// 企业管理员相关菜单特殊权限加载
List<MisRoleApiDto> comAdminAuthorityController = authorizeMapper.getCompanyAdminAuthorityController();
// 企业管理员相关内容处理缓存
Map<String, String> 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<MisRoleApiDto> appDefaultRoleApiList = authorizeMapper.listDefaultRoleController();
// 产品默认角色相关菜单特殊权限加载
List<MisRoleApiDto> appDefaultRoleAuthorityApiList = authorizeMapper.listDefaultRoleAuthorityController();
// 产品默认角色关内容处理缓存
Map<String, String> 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);
}
}

View File

@ -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<String, Object> 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<String, Object> params, AuthorizeUser authorizeUser) {
List<MisComUserDto> companyList = authorizeMapper.queryCompanyUserByUserId(params);
if (CollectionUtils.isEmpty(companyList)) {
List<String> 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<String> userRoleList = new ArrayList<>();
userRoleList.add("ALL");
//设置企业管理员
if (defaultCompany.getIsOwner()) {
userRoleList.add(defaultCompany.getCompanyCode() + "_admin");
authorizeUser.setIsCompanyAdmin(true);
} else {
// 查询获取授权角色数据集合
List<MisComRoleUserDto> 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);
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<String, Object> params = new HashMap<>();
params.put("userId", user.getId());
if (!StringUtils.isBlank(companyCode)) {
params.put("companyCode", companyCode);
}
// 查询用户关联企业信息内容
List<MisComUserDto> comUserList = authorizeMapper.queryCompanyUserByUserId(params);
if (CollectionUtils.isEmpty(comUserList)) {
List<String> userRoleList = new ArrayList<>();
userRoleList.add("ALL");
authorizeUser.setRoles(userRoleList);
} else {
MisComUserDto defaultCompany = comUserList.get(0);
List<String> userRoleList = new ArrayList<>();
// 设置默认公司
authorizeUser.setCompanyCode(defaultCompany.getCompanyCode());
authorizeUser.setCompanyName(defaultCompany.getCompanyName());
// 判断是否企业管理员
if (defaultCompany.getIsOwner()) {
authorizeUser.setIsCompanyAdmin(true);
userRoleList.add(defaultCompany.getCompanyCode() + "_admin");
} else {
List<MisComRoleUserDto> 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;
}
}

View File

@ -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<String, Object> 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<WxAppletUser> 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<String, Object> 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<String, Object> params = new HashMap<>();
params.put("userId", misUser.getId());
if (!StringUtils.isBlank(request.getCompanyCode())) {
params.put("companyCode", request.getCompanyCode());
}
// 查询相关绑定企业内容并设置企业信息
List<MisComUserDto> 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<String, Object> 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 参数对象
* <ul>
* <li>userName: 用户名</li>
* <li>mobile: 手机号码</li>
* <li>idCard: 身份证号码</li>
* </ul>
* @return 返回操作结果
*/
@Transactional(rollbackFor = Exception.class)
public Result userMobileVerify(Map<String, Object> 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<String, Object> 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<String, Object> 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<MisComUserDto> 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<String, Object> 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;
}
}

View File

@ -0,0 +1,6 @@
package cn.crtech.cloud.auth.service.wx.chat;
import org.springframework.security.core.userdetails.UserDetailsService;
public interface WxChatUserService extends UserDetailsService {
}

View File

@ -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<String, Object> 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<String, Object> params, AuthorizeUser authorizeUser) {
List<MisComUserDto> comUserList = authorizeMapper.queryCompanyUserByUserId(params);
if (CollectionUtils.isEmpty(comUserList)) {
List<String> 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<String> userRoleList = new ArrayList<>();
userRoleList.add("ALL");
// 设置企业管理员
if (defaultCompany.getIsOwner()) {
userRoleList.add(defaultCompany.getCompanyCode() + "_admin");
authorizeUser.setIsCompanyAdmin(true);
} else {
List<MisComRoleUserDto> 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);
}
}
}

View File

@ -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 {
// 实例化一个认证对象入参需要传入腾讯云账户secretIdsecretKey,此处还需注意密钥对的保密
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());
}
}
}

View File

@ -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失效!");
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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<DefaultSecurityFilterChain, HttpSecurity> {
@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);
}
}

View File

@ -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失效!");
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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<DefaultSecurityFilterChain, HttpSecurity> {
@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);
}
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,6 @@
spring:
profiles:
active: dev

Binary file not shown.

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/crtech-cloud-auth.%d{yyyy-MM-dd}.log
</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd_HH:mm:ss} %logger{18} -%msg%n
</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>1000</queueSize>
<appender-ref ref="FILE" />
</appender>
<logger name="org" level="info" additivity="false">
<appender-ref ref="FILE"></appender-ref>
<appender-ref ref="STDOUT"></appender-ref>
</logger>
<logger name="com" level="info" additivity="false">
<appender-ref ref="FILE"></appender-ref>
<appender-ref ref="STDOUT"></appender-ref>
</logger>
<logger name="net" level="info" additivity="false">
<appender-ref ref="FILE"></appender-ref>
<appender-ref ref="STDOUT"></appender-ref>
</logger>
<logger name="com.netflix" level="debug" additivity="false">
<appender-ref ref="STDOUT"></appender-ref>
<appender-ref ref="FILE"></appender-ref>
</logger>
<logger name="cn.crtech.cloud.auth" level="debug" additivity="false">
<appender-ref ref="STDOUT"></appender-ref>
<appender-ref ref="FILE"></appender-ref>
</logger>
<root level="INFO">
<appender-ref ref="ASYNC" />
</root>
</configuration>

170
Common/pom.xml Normal file
View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.crtech.cloud.common</groupId>
<artifactId>Common</artifactId>
<version>1.0.1</version>
<!-- 父工程 -->
<parent>
<groupId>cn.crtech.cloud.dependencies</groupId>
<artifactId>Dependencies</artifactId>
<version>1.0.1</version>
<relativePath/>
</parent>
<!-- 依赖的版本锁定 -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<fastjson.version>1.2.83</fastjson.version>
<thumbnailator.version>0.4.9</thumbnailator.version>
<druid.version>1.2.8</druid.version>
<mysql.version>8.0.27</mysql.version>
<jackson.databind.version>2.8.8</jackson.databind.version>
<jackson.mapper.asl.version>1.9.13</jackson.mapper.asl.version>
<wildfly.version>1.5.2.Final</wildfly.version>
<aliyun.sdk.version>4.0.3</aliyun.sdk.version>
</properties>
<dependencies>
<!-- spring -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<!-- tk-mybatis -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<!-- page-helper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- thumbnailator -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>${thumbnailator.version}</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--JsonFormat-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.databind.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.mapper.asl.version}</version>
</dependency>
<!-- wildfly -->
<dependency>
<groupId>org.wildfly.common</groupId>
<artifactId>wildfly-common</artifactId>
<version>${wildfly.version}</version>
</dependency>
<!-- 阿里云短信 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${aliyun.sdk.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,11 @@
package cn.crtech.cloud.common.annotation;
import java.lang.annotation.*;
@Documented // 文档
@Retention(RetentionPolicy.RUNTIME) // 在运行时可以获取
@Target({ ElementType.FIELD, ElementType.TYPE }) // 作用到字段上
public @interface DataExportAnnotation {
//导出字段名称
String value() default "";
}

View File

@ -0,0 +1,35 @@
package cn.crtech.cloud.common.api;
import org.springframework.util.StringUtils;
/**
* 阿里云模板配置
*/
public enum ALiYunSMSEnum {
SMS_133972087("SECTION_CHECK_IN_CODE");
private String typeCode;
public String getTypeCode() {
return typeCode;
}
public void setTypeCode(String typeCode) {
this.typeCode = typeCode;
}
ALiYunSMSEnum(String typeCode) {
this.typeCode = typeCode;
}
public static ALiYunSMSEnum getEnumValue(String typeCode) {
if (!StringUtils.isEmpty(typeCode)) {
for (ALiYunSMSEnum sendCodeEnum : ALiYunSMSEnum.values()) {
if (sendCodeEnum.getTypeCode().equals(typeCode)) {
return sendCodeEnum;
}
}
}
return null;
}
}

View File

@ -0,0 +1,123 @@
package cn.crtech.cloud.common.api;
/**
* 通用返回对象
*/
public class CommonResult<T> {
private long code;
private String message;
private T data;
protected CommonResult() {
}
protected CommonResult(long code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
/**
* 成功返回结果
*
* @param data 获取的数据
*/
public static <T> CommonResult<T> success(T data) {
return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
}
/**
* 成功返回结果
*
* @param data 获取的数据
* @param message 提示信息
*/
public static <T> CommonResult<T> success(T data, String message) {
return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
}
/**
* 失败返回结果
* @param errorCode 错误码
*/
public static <T> CommonResult<T> failed(IErrorCode errorCode) {
return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
}
/**
* 失败返回结果
* @param errorCode 错误码
* @param message 错误信息
*/
public static <T> CommonResult<T> failed(IErrorCode errorCode, String message) {
return new CommonResult<T>(errorCode.getCode(), message, null);
}
/**
* 失败返回结果
* @param message 提示信息
*/
public static <T> CommonResult<T> failed(String message) {
return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
}
/**
* 失败返回结果
*/
public static <T> CommonResult<T> failed() {
return failed(ResultCode.FAILED);
}
/**
* 参数验证失败返回结果
*/
public static <T> CommonResult<T> validateFailed() {
return failed(ResultCode.VALIDATE_FAILED);
}
/**
* 参数验证失败返回结果
* @param message 提示信息
*/
public static <T> CommonResult<T> validateFailed(String message) {
return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
}
/**
* 未登录返回结果
*/
public static <T> CommonResult<T> unauthorized(T data) {
return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
}
/**
* 未授权返回结果
*/
public static <T> CommonResult<T> forbidden(T data) {
return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
}
public long getCode() {
return code;
}
public void setCode(long code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

View File

@ -0,0 +1,10 @@
package cn.crtech.cloud.common.api;
/**
* 封装API的错误码
*/
public interface IErrorCode {
long getCode();
String getMessage();
}

View File

@ -0,0 +1,27 @@
package cn.crtech.cloud.common.api;
/**
* 枚举了一些常用API操作码
*/
public enum ResultCode implements IErrorCode {
SUCCESS(200, "操作成功"),
FAILED(500, "操作失败"),
VALIDATE_FAILED(404, "参数检验失败"),
UNAUTHORIZED(401, "暂未登录或token已经过期"),
FORBIDDEN(403, "没有相关权限");
private long code;
private String message;
private ResultCode(long code, String message) {
this.code = code;
this.message = message;
}
public long getCode() {
return code;
}
public String getMessage() {
return message;
}
}

View File

@ -0,0 +1,9 @@
package cn.crtech.cloud.common.constant;
/**
*/
public class AuthConstant {
public static final String AUTHORITY_PREFIX = "ROLE_";
public static final String AUTHORITY_CLAIM_NAME = "authorities";
public static final String APP_SPECIAL_AUTHORITY_SUFFIX = "_Limit_Authority";
}

View File

@ -0,0 +1,17 @@
package cn.crtech.cloud.common.constant;
/**
* 消息常量
*/
public class MessageConstant {
public static final String LOGIN_SUCCESS = "登录成功!";
public static final String USERNAME_PASSWORD_ERROR = "用户名或密码错误!";
public static final String CREDENTIALS_EXPIRED = "该账户的登录凭证已过期,请重新登录!";
public static final String ACCOUNT_DISABLED = "该账户已被禁用,请联系管理员!";
public static final String ACCOUNT_LOCKED = "该账号已被锁定,请联系管理员!";
public static final String ACCOUNT_EXPIRED = "该账号已过期,请联系管理员!";
public static final String PERMISSION_DENIED = "没有访问权限,请联系管理员!";
public static final String ACCOUNT_SIGNATURE_ERROR = "签名校验失败!";
public static final String ACCOUNT_UN_REGISTER = "当前用户未注册!";
public static final String ACCOUNT_ERROR = "未查找到当前用户!";
}

View File

@ -0,0 +1,16 @@
package cn.crtech.cloud.common.constant;
/**
* Redis常量
*/
public class RedisConstant {
public static final String FREE_SERIES_APP_VERSION = "AUTH:FREE:SERIES:APP:VERSION";
public static final String FREE_VERSION_API = "AUTH:FREE_RESOURCE_ROLES_MAP:${SERIESCODE}:${APPCODE}:${VERSION}";
public static final String RESOURCE_ROLES_MAP = "AUTH:RESOURCE_ROLES_MAP";
public static final String RESOURCE_ADMIN_MAP = "AUTH:RESOURCE_ADMIN_MAP";
public static final String DEFAULT_ROLES_MAP = "AUTH:DEDEFAULT_ROLES_MAPFAULT_ROLES_MAP";
public static final String CURRENT_USREINFO = "USER:CURRENT_USERINFO:";
public static final String CURRENT_WX_USREINFO = "USER:CURRENT_WX_USERINFO:";
public static final String LIMIT_AUTHORITY_API_MAP = "AUTH:LIMIT_AUTHORITY_API_MAP";
public static final String APP_FEIGN_API_MAP = "AUTH:APP_FEIGN_API_MAP";
}

View File

@ -0,0 +1,86 @@
package cn.crtech.cloud.common.dto;
import java.io.Serializable;
/**
* 系统返统一回结果实体类
*/
public class Result implements Serializable {
private String message;
private Object data;
private boolean success;
public Result() {
}
public Result(String message, boolean success) {
this.message = message;
this.success = success;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static Result success() {
Result result = new Result();
result.message = "操作成功";
result.success = true;
return result;
}
public static Result success(Object data) {
Result result = Result.success();
result.data = data;
return result;
}
public static Result success(Object data, String operation) {
Result result = Result.success();
result.data = data;
result.message = operation;
return result;
}
public static Result error() {
Result result = new Result();
result.message = "操作失败";
result.success = false;
return result;
}
public static Result error(Object data) {
Result result = Result.error();
result.data = data;
return result;
}
public static Result error(Object data, String operation) {
Result result = Result.error();
result.data = data;
result.message = operation;
return result;
}
}

View File

@ -0,0 +1,76 @@
package cn.crtech.cloud.common.dto;
import cn.crtech.cloud.common.annotation.DataExportAnnotation;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import java.util.List;
/**
* Author : yj
* Date : 2021-01-14
* Description:
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class UserDto {
private String companyCode;
private String companyName;
private String userName;
private Integer status;
private List<String> roles;
private Boolean companyAdmin;
@DataExportAnnotation("主键id")
private Integer id;
@DataExportAnnotation("姓名")
private String name;
@DataExportAnnotation("身份证")
private String idCard;
@DataExportAnnotation("昵称")
private String nickName;
@DataExportAnnotation("最后修改人")
private Long lastModifier;
@DataExportAnnotation("最后修时间")
private Date lastModifyTime;
@DataExportAnnotation("状态")
private Integer state;
@DataExportAnnotation("手机号码")
private String mobile;
@DataExportAnnotation("邮箱")
private String email;
@DataExportAnnotation("头像")
private String avatar;
@DataExportAnnotation("密码")
private String password;
@DataExportAnnotation("身份证正面")
private String idCardPrev;
@DataExportAnnotation("身份证反面")
private String idCardNext;
@DataExportAnnotation("真实姓名")
private String realName;
@DataExportAnnotation("注册时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date regTime;
}

View File

@ -0,0 +1,88 @@
package cn.crtech.cloud.common.plugins;
import org.slf4j.LoggerFactory;
/**
* 自定义日志实体类
*/
public class Logger {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Logger.class);
public static void info(String msg) {
logger.info(msg);
}
public static void error(String msg) {
logger.error(msg);
}
public static void debug(String msg) {
info(msg);
}
/**
* 格式化
*
* @param jsonStr 字符串
* @return 返回处理结果
*/
public static String formatJson(String jsonStr) {
if (null == jsonStr || "".equals(jsonStr)) {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append("\n");
char last = '\0';
char current = '\0';
int indent = 0;
for (int i = 0; i < jsonStr.length(); i++) {
last = current;
current = jsonStr.charAt(i);
//遇到{ [换行,且下一行缩进
switch (current) {
case '{':
case '[':
sb.append(current);
sb.append('\n');
indent++;
addIndentBlank(sb, indent);
break;
//遇到} ]换行,当前行缩进
case '}':
case ']':
sb.append('\n');
indent--;
addIndentBlank(sb, indent);
sb.append(current);
break;
//遇到,换行
case ',':
sb.append(current);
if (last != '\\') {
sb.append('\n');
addIndentBlank(sb, indent);
}
break;
default:
sb.append(current);
}
}
return sb.toString();
}
/**
* 添加space
*
* @param sb 字符串
* @param indent 位置
*/
private static void addIndentBlank(StringBuilder sb, int indent) {
for (int i = 0; i < indent; i++) {
sb.append('\t');
}
}
}

View File

@ -0,0 +1,88 @@
package cn.crtech.cloud.common.plugins;
import cn.crtech.cloud.common.api.ALiYunSMSEnum;
import cn.crtech.cloud.common.utils.SendSMSUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 阿里短信发送工具类
*/
@Component
public class SendMessageService {
public Map<String, Object> send(Map<String, Object> map) throws Exception {
Map<String, Object> ret = null;
if (!map.containsKey("cretch-cloud")) {
Logger.info("采用凌凯短信平台发送...");
ret = sendHttp(map);
} else {
Logger.info("采用阿里云平台发送...");
ret = sendCloud(map);
}
Logger.info(JSON.toJSONString(ret));
return ret;
}
private Map<String, Object> sendHttp(Map<String, Object> map) throws Exception {
Map<String, Object> resultMap = new HashMap<>();
if (!map.containsKey("mobile")) {
resultMap.put("success", false);
resultMap.put("message", "手机号不能为空!");
return resultMap;
}
if (!map.containsKey("content")) {
resultMap.put("success", false);
resultMap.put("message", "消息内容不能为空!");
return resultMap;
}
int success = SendSMSUtils.sendLingKaiSMS(map);
if (success > 0) {
resultMap.put("success", true);
resultMap.put("message", "短信发送成功!");
} else {
resultMap.put("success", false);
resultMap.put("message", "短信服务器出错!");
}
return resultMap;
}
private Map<String, Object> sendCloud(Map<String, Object> map) {
Map<String, Object> resultMap = new HashMap<>();
if (map.get("mobiles") == null) {
resultMap.put("success", false);
resultMap.put("message", "手机号不能为空!");
return resultMap;
} else if (map.get("templateCode") == null) {
resultMap.put("success", false);
resultMap.put("message", "模版Code不能为空");
return resultMap;
} else {
String code = map.get("templateCode").toString();
ALiYunSMSEnum codeEnum = ALiYunSMSEnum.getEnumValue(code);
if (code == null || codeEnum == null) {
resultMap.put("success", false);
resultMap.put("message", "模版Code错误");
} else {
String mobile = map.get("mobile").toString();
JSONObject jo = SendSMSUtils.sendALiYunSMS(map, mobile, codeEnum.name());
Logger.info(jo.toJSONString());
if ("OK".equals(jo.get("Code"))) {
resultMap.put("success", true);
resultMap.put("message", "短信发送成功!");
} else {
Logger.info(jo.toJSONString());
resultMap.put("success", false);
resultMap.put("message", jo.toJSONString());
}
}
return resultMap;
}
}
}

View File

@ -0,0 +1,27 @@
package cn.crtech.cloud.common.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import javax.persistence.Transient;
import java.util.List;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public abstract class Tree<T> {
@Transient
private Object realId;
@Transient
private Object realParentId;
@Transient
private List<T> children;
//判断是否根节点
public abstract Boolean isRoot();
}

View File

@ -0,0 +1,36 @@
package cn.crtech.cloud.common.utils;
import java.util.UUID;
public class CommonFun {
private static long startValue = 0;
/**
* 获取时间戳式的ID值每秒可取1000个
* 长度16位
* 格式为1639 4620 6212 3001 最大为9999 9999 9999 9 999 -》 2286-11-21 01:46:39.999 + 999
* @return
*/
public static synchronized long getSSID() {
String id;
startValue++;
startValue = startValue % 1000;
java.text.DecimalFormat format = new java.text.DecimalFormat("000");
String sStartValue = format.format(startValue);
String sDate = String.valueOf(System.currentTimeMillis());
id = sDate + sStartValue;
return Long.parseLong(id);
}
/**
* 获取md5式的ID值
* @return
*/
public static synchronized String getUUID() {
String id = UUID.randomUUID().toString();
id = id.replaceAll("-", "");
id = id.toUpperCase();
return id;
}
}

View File

@ -0,0 +1,120 @@
package cn.crtech.cloud.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
public class EncryptUtil {
protected static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);
public EncryptUtil() {
}
public static String parseByte2HexStr(byte[] buf) {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < buf.length; ++i) {
String hex = Integer.toHexString(buf[i] & 255);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1) {
return null;
} else {
byte[] result = new byte[hexStr.length() / 2];
for(int i = 0; i < hexStr.length() / 2; ++i) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte)(high * 16 + low);
}
return result;
}
}
public static byte[] decrypt(byte[] content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(password.getBytes());
kgen.init(128, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(2, key);
byte[] result = cipher.doFinal(content);
return result;
} catch (NoSuchAlgorithmException var9) {
logger.error(var9.getMessage(), var9);
} catch (NoSuchPaddingException var10) {
logger.error(var10.getMessage(), var10);
} catch (InvalidKeyException var11) {
logger.error(var11.getMessage(), var11);
} catch (IllegalBlockSizeException var12) {
logger.error(var12.getMessage(), var12);
} catch (BadPaddingException var13) {
logger.error(var13.getMessage(), var13);
}
return null;
}
public static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(password.getBytes());
kgen.init(128, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(1, key);
byte[] result = cipher.doFinal(byteContent);
return result;
} catch (NoSuchAlgorithmException var10) {
logger.error(var10.getMessage(), var10);
} catch (NoSuchPaddingException var11) {
logger.error(var11.getMessage(), var11);
} catch (InvalidKeyException var12) {
logger.error(var12.getMessage(), var12);
} catch (UnsupportedEncodingException var13) {
logger.error(var13.getMessage(), var13);
} catch (IllegalBlockSizeException var14) {
logger.error(var14.getMessage(), var14);
} catch (BadPaddingException var15) {
logger.error(var15.getMessage(), var15);
}
return null;
}
public static void main(String[] args) {
String content = "www.server41.com";
String password = "THIS SHALL NOT BE SEEN.";
System.out.println("加密前:" + content);
byte[] encryptResult = encrypt(content, password);
String encryptResultStr = parseByte2HexStr(encryptResult);
System.out.println("加密后:" + encryptResultStr);
byte[] decryptFrom = parseHexStr2Byte(encryptResultStr);
byte[] decryptResult = decrypt(decryptFrom, password);
System.out.println("解密后:" + new String(decryptResult));
}
}

View File

@ -0,0 +1,12 @@
package cn.crtech.cloud.common.utils;
import tk.mybatis.mapper.genid.GenId;
import java.util.UUID;
public class IDGenerator implements GenId<String> {
@Override
public String genId(String s, String s1) {
return UUID.randomUUID().toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
package cn.crtech.cloud.common.utils;
import cn.crtech.cloud.common.pojo.Tree;
import java.util.ArrayList;
import java.util.List;
/**
* author 墨衣
* date 2022/3/31
* descript
**/
public class OrganizeTree<T extends Tree> {
private List<T> treeList;
public OrganizeTree(List<T> treeList) {
this.treeList = treeList;
}
//建立树形结构
public List<T> buildTree() {
List<T> tree = new ArrayList<>();
for (T node : getRootNode()) {
node = buildChildTree(node);
tree.add(node);
}
return tree;
}
//递归,建立子树形结构
private T buildChildTree(T pNode) {
List<T> child = new ArrayList<>();
for (T node : treeList) {
//pNode的id必定存在,node可能存在可能为null
String parentId = node.getRealParentId() == null ? "" : node.getRealParentId().toString();
if (pNode.getRealId().toString().equals(parentId)) {
child.add(buildChildTree(node));
}
}
if (child.size() != 0) {
pNode.setChildren(child);
} else {
pNode.setChildren(null);
}
return pNode;
}
//获取根节点
private List<T> getRootNode() {
List<T> root = new ArrayList<>();
for (T node : treeList) {
if (node.isRoot()) {
root.add(node);
}
}
return root;
}
}

View File

@ -0,0 +1,112 @@
package cn.crtech.cloud.common.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
public class SendSMSUtils {
private static final Logger logger = LoggerFactory.getLogger(DebugLogger.class);
private static final SendSMSUtils ourInstance = new SendSMSUtils();
public static SendSMSUtils getInstance() {
return ourInstance;
}
private SendSMSUtils() {
}
/**
* @param map 模板参数
* @param mobiles 手机号码
* @param templateCode 模板code
* signName
* accessKeySecret
* accessKeyId
* domain
* @return 返回处理结果
*/
public static JSONObject sendALiYunSMS(Map<String, Object> map, String mobiles, String templateCode) {
String accessKeyId = map.get("accessKeyId").toString();
String accessKeySecret = map.get("accessKeySecret").toString();
String signName = map.get("signName").toString();
String domain = map.get("domain").toString();
DefaultProfile profile = DefaultProfile.getProfile("default", accessKeyId, accessKeySecret);
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setMethod(MethodType.POST);
request.setDomain(domain);
request.setVersion("2017-05-25");
request.setAction("SendSms");
request.putQueryParameter("PhoneNumbers", mobiles);
request.putQueryParameter("SignName", signName);
request.putQueryParameter("TemplateCode", templateCode);
//移除以下两个不必要的参数
map.remove("templateCode");
map.remove("platform");
String str = JSONObject.toJSONString(map);
logger.info("发文:" + str);
request.putQueryParameter("TemplateParam", str);
JSONObject jo = null;
try {
CommonResponse response = client.getCommonResponse(request);
logger.info(response.getData());
jo = JSON.parseObject(response.getData());
} catch (Exception e) {
e.printStackTrace();
}
logger.info("返回:" + jo);
return jo;
}
/**
* int > 0 发送成功!
*
* mobile 手机号码
* content 内容
* pwd lingKai配置
* domain
* corpId
*
* @return 返回处理结果
* @throws Exception 发送异常
*/
public static int sendLingKaiSMS(Map<String, Object> map) throws Exception {
String mobile = map.get("mobile").toString();
String content = map.get("content").toString();
String corpId = map.get("corpId").toString();
String pwd = map.get("pwd").toString();
String domain = map.get("domain").toString();
String send_content = URLEncoder.encode(content, "GB2312");
URL url = new URL(domain + "?CorpID=" + corpId + "&Pwd=" + pwd + "&Mobile=" + mobile + "&Content=" + send_content);
BufferedReader in;
int inputLine = 0;
try {
logger.info("开始发送短信手机号码为:" + mobile);
in = new BufferedReader(new InputStreamReader(url.openStream()));
inputLine = new Integer(in.readLine());
} catch (Exception e) {
logger.info("网络异常,发送短信失败!");
inputLine = -2;
}
logger.info("发送成功!");
return inputLine;
}
}

View File

@ -0,0 +1,409 @@
package cn.crtech.cloud.common.utils;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Coordinate;
import net.coobird.thumbnailator.geometry.Position;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.util.List;
/**
* Author : yj
* Date : 2021-03-29
* Description:
*/
public class ThumbnailatorUtil {
/**
* 图片尺寸不变,压缩图片文件大小
*
* @param bytes 图片文件二进制流
* @param imageType 图片格式
* @param quality 质量因子 1为最高质量
* @return
*/
public static byte[] compressImage(byte[] bytes, String imageType, float quality) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
// 图片尺寸不变压缩图片文件大小outputQuality实现参数1为最高质量
Thumbnails.of(in).scale(1f).outputFormat(imageType).outputQuality(quality).toOutputStream(bout);
byte[] compressiondata = bout.toByteArray();
return compressiondata;
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 指定宽高压缩图片
*
* @param bytes 图片文件二进制流
* @param width 压缩宽度
* @param height 压缩高度
* @return
*/
public static byte[] compressImageWithWH(byte[] bytes, int width, int height) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
Thumbnails.of(in).size(width, height).toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 根据比列压缩图片
*
* @param bytes
* @param scale
* @return
*/
public static byte[] compressImageWithScale(byte[] bytes, double scale) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
Thumbnails.of(in).scale(scale).toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 转换图片格式
*
* @param bytes 源图片文件流
* @param toformatImageType 转换后图片格式
* @return
*/
public static byte[] formatImage(byte[] bytes, String toformatImageType, int width, int height) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
Thumbnails.of(in).size(width, height).outputFormat(toformatImageType).toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 根据坐标裁剪图片
*
* @param bytes 源图片文件流
* @param x 起始x坐标
* @param y 起始y坐标
* @param x1 结束x坐标
* @param y1 结束y坐标
* @param keepAspectRatio 默认是按照比例缩放的,值为false 时不按比例缩放
* @return
*/
public static byte[] cutImage(byte[] bytes, int x, int y, int x1, int y1, boolean keepAspectRatio) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
int width = x1 - x;
int height = y1 - y;
Thumbnails.of(in).sourceRegion(x, y, x1, y1).size(width, height).keepAspectRatio(keepAspectRatio).toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 添加图片水印
*
* @param bytes 源图片文件流
* @param width 宽度
* @param height 高度
* @param position 位置 Positions.BOTTOM_RIGHT
* @param watermark 水印图片地址
* @param opacity 透明度 0.5f
* @param quality 图片质量 0.8f
* @return
*/
public static byte[] addImageWater(byte[] bytes, int width, int height, Position position, String watermark, float opacity, float quality) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
Thumbnails.of(in)
.size(width, height)
// 加水印 参数1.水印位置 2.水印图片 3.不透明度0.0-1.0
.watermark(position, ImageIO.read(new File(watermark)), opacity)
.outputQuality(quality)
.toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 添加文字水印
*
* @param bytes
* @param position
* @param waterText
* @param rotate
* @param opacity
* @param quality
* @return
*/
public static byte[] addTextWater(byte[] bytes, Position position, String waterText, double rotate, float opacity, float quality) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
// 设置480x160的大小区域显示水印文本
BufferedImage bi = new BufferedImage(480, 160, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
// 设置绘图区域透明
bi = g.getDeviceConfiguration().createCompatibleImage(480, 160, Transparency.TRANSLUCENT);
g.dispose();
g = bi.createGraphics();
// 设置字体类型、大小、加粗、颜色
g.setFont(new Font("微软雅黑", Font.BOLD, 32));
g.setColor(new Color(0, 0, 0));
char[] data = waterText.toCharArray();
// 设置文本显示坐标0,80
g.drawChars(data, 0, data.length, 0, 80);
g.dispose();
Thumbnails.of(in).scale(1).watermark(position, bi, opacity).outputQuality(quality).toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 整屏添加文字水印
*
* @param bytes 源图片二进制流
* @param width 图片宽度
* @param height 图片高度
* @param intervalWidth 间隔宽度
* @param intervalHeight 间隔高度
* @param waterTextList 水印内容列表
* @param fontSize 文字大小
* @param opacity 透明度
* @param quality 质量
* @return
*/
public static byte[] addTextWaterFullScreen(byte[] bytes, int width, int height, int intervalWidth, int intervalHeight, List<String> waterTextList, int fontSize, float opacity, float quality) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
// 设置图片大小区域显示水印文本
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 创建一个Graphics2D的对象
Graphics2D g = bi.createGraphics();
// 设置绘图区域透明,即背景透明
bi = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g.dispose();
g = bi.createGraphics();
// 设置字体类型,加粗,字体大小
Font font = new Font("微软雅黑", Font.BOLD, fontSize);
g.setFont(font);
// 旋转角度单位弧度以圆点0,0为圆心正代表顺时针负代表逆时针
g.rotate(Math.toRadians(-30), 0, 0);
// 设置字体颜色
g.setColor(new Color(0, 0, 0));
int distance = fontSize + 12;
int size = waterTextList.size();
// 设置文字字体显示坐标位置
for (int i = 0; i < size; i++) {
char[] data = waterTextList.get(i).toCharArray();
g.drawChars(data, 0, data.length, 0, height / 2 + i * distance);
}
g.dispose();
Thumbnails.Builder<? extends InputStream> builder = Thumbnails.of(in).scale(1);
// 添加文字水印
int wMod = (int) Math.ceil(width / intervalWidth);
int hMod = (int) Math.ceil(height / intervalHeight);
for (int i = 0; i <= wMod; i++) {
for (int j = 0; j <= hMod; j++) {
int x = (i) * intervalWidth - intervalWidth / 2;
int y = (j) * intervalHeight - intervalHeight / 2;
System.out.println(x + "," + y);
builder.watermark(new Coordinate(x, y), bi, opacity);
}
}
builder.outputQuality(quality).toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 旋转 ,正数:顺时针 负数:逆时针
*
* @param bytes 源图片文件流
* @param width 宽
* @param height 高
* @param rotate 角度
*/
public static byte[] rotateImage(byte[] bytes, int width, int height, double rotate) {
InputStream in = null;
ByteArrayOutputStream bout = null;
try {
in = new ByteArrayInputStream(bytes);
bout = new ByteArrayOutputStream(1024);
Thumbnails.of(in).size(width, height).rotate(rotate).toOutputStream(bout);
return bout.toByteArray();
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (bout != null) {
bout.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}

2858
Dependencies/pom.xml vendored Normal file

File diff suppressed because it is too large Load Diff

111
Feign/pom.xml Normal file
View File

@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.crtech.cloud.feign</groupId>
<artifactId>Feign</artifactId>
<version>1.0.1</version>
<!-- 父工程 -->
<parent>
<groupId>cn.crtech.cloud.dependencies</groupId>
<artifactId>Dependencies</artifactId>
<version>1.0.1</version>
<relativePath/>
</parent>
<!-- 依赖的版本锁定 -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<common.version>1.0.1</common.version>
<feign.version>10.7.4</feign.version>
<httpclient.version>4.5.10</httpclient.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- nacos 客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--提供健康信息接口供监控使用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>${feign.version}</version>
</dependency>
<dependency>
<groupId>cn.crtech.cloud.common</groupId>
<artifactId>Common</artifactId>
<version>${common.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,18 @@
package cn.crtech.feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "cn.crtech.feign")
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class})
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}

View File

@ -0,0 +1,14 @@
package cn.crtech.feign.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignConfiguration {
// 日志级别
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}

View File

@ -0,0 +1,78 @@
package cn.crtech.feign.config;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@Configuration
public class GetHeaderConfiguration {
private static final String X_REQUEST_ID = "Bearer ";
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
HttpServletRequest httpServletRequest = getHttpServletRequest();
if (httpServletRequest != null) {
//获取头信息
Map<String, String> headers = getHeaders(httpServletRequest);
// 传递所有请求头,防止部分丢失
//将请求的头信息放入到RequestTemplate 的头信息中当使用RequestTemplate发起请求时会自动添加头信息
for (Map.Entry<String, String> entry : headers.entrySet()) {
template.header(entry.getKey(), entry.getValue());
}
// 微服务之间传递的唯一标识,区分大小写所以通过httpServletRequest获取
if (httpServletRequest.getHeader(X_REQUEST_ID) == null) {
template.header(X_REQUEST_ID);
}
}
}
/**
* RequestContextHolder 中获取 HttpServletRequest对象
*
* @return
*/
private HttpServletRequest getHttpServletRequest() {
try {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
} catch (Exception e) {
return null;
}
}
/**
* 获取头信息
*
* @param request
* @return
*/
private Map<String, String> getHeaders(HttpServletRequest request) {
Map<String, String> map = new LinkedHashMap<>();
Enumeration<String> enumeration = request.getHeaderNames();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
}
return map;
}
};
}
}

View File

@ -0,0 +1,32 @@
package cn.crtech.feign.controller;
import cn.crtech.cloud.common.dto.Result;
import cn.crtech.feign.pojo.AccountFlow;
import cn.crtech.feign.pojo.CompanyAccount;
import cn.crtech.feign.service.AcctClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AcctApi {
@Autowired
private AcctClient acctClient;
@PostMapping("/acct/account/flow")
public Result accountFlowCreate(@RequestBody AccountFlow accountFlow) {
return acctClient.accountFlowCreate(accountFlow);
}
@RequestMapping(value = "/acct/companyAccount/insertCompanyAccount")
public Result insertCompanyAccount(@RequestBody CompanyAccount companyAccount) {
return acctClient.insertCompanyAccount(companyAccount);
}
@RequestMapping(value = "/acct/companyAccount/updateCompanyAccount")
public Result updateCompanyAccount(@RequestBody CompanyAccount companyAccount) {
return acctClient.updateCompanyAccount(companyAccount);
}
}

View File

@ -0,0 +1,34 @@
package cn.crtech.feign.controller;
import cn.crtech.cloud.common.dto.Result;
import cn.crtech.feign.service.AuthClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* auth模块服务间调用Api
*
* @author TYP
* @since 2023-08-11 14:44
*/
@RestController
public class AuthApi {
private AuthClient authClient;
@Autowired
public AuthApi(AuthClient authClient) {
this.authClient = authClient;
}
/**
* 权限数据刷新
*
* @return 返回处理结果
*/
@PostMapping("/oauth/initRedis")
public Result initRedis() {
return authClient.initRedis();
}
}

View File

@ -0,0 +1,44 @@
package cn.crtech.feign.controller;
import cn.crtech.cloud.common.dto.Result;
import cn.crtech.feign.dto.ChainUserDto;
import cn.crtech.feign.service.LsErpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LsErpApi {
@Autowired
private LsErpClient lsErpClient;
/**
* 获取连锁公司选项
*
* @return 返回查询结果
*/
@PostMapping("/ls/erp/loadChainOption")
public Result loadChainOption() {
try {
return lsErpClient.loadChainOption();
} catch (Exception e) {
return Result.error("查询失败");
}
}
/**
* 连锁用户注册
*
* @param userDto 注册信息对象
* @return 返回注册结果
*/
@PostMapping("/ls/erp/registerUser")
public Result registerUser(@RequestBody ChainUserDto userDto) {
try {
return lsErpClient.registerUser(userDto);
} catch (Exception e) {
return Result.error("查询失败");
}
}
}

View File

@ -0,0 +1,87 @@
package cn.crtech.feign.controller;
import cn.crtech.cloud.common.dto.Result;
import cn.crtech.feign.dto.MessageDto;
import cn.crtech.feign.pojo.WxMpMessage;
import cn.crtech.feign.service.MnsClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
public class MnsApi {
@Autowired
private MnsClient mnsClient;
@PostMapping("/mns/sendSystemMessage")
public Result sendSystemMessage(@RequestBody MessageDto message) {
return mnsClient.sendSystemMessage(message);
}
@PostMapping("/mns/sendWxMpTempMessage")
public Result sendWxMpTempMessage(@RequestBody WxMpMessage wxMpMessage) {
return mnsClient.sendWxMpTempMessage(wxMpMessage);
}
@GetMapping("/mns/sendSMS/{type}/{mobile}")
public Result sendSystemMessage(@PathVariable("type") String type, @PathVariable("mobile") String mobile) {
return mnsClient.sendSMS(type, mobile);
}
/**
* 分页条件查询系统消息内容
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/mns/systemMessage/listQueryByPage")
Result listQueryByPage(Map<String, Object> params) {
return mnsClient.listQueryByPage(params);
}
/**
* 更新系统消息状态
*
* @param params 参数对象
* @return 返回执行结果
*/
@PostMapping("/mns/systemMessage/updateSystemMessageStatus")
Result updateSystemMessageStatus(Map<String, Object> params) {
return mnsClient.updateSystemMessageStatus(params);
}
/**
* 查询系统消息
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/mns/systemMessage/listByParams")
Result listByParams(Map<String, Object> params) {
return mnsClient.listByParams(params);
}
/**
* 发送(新增)系统消息
*
* @param params 参数对象
* @return 返回执行结果
*/
@PostMapping("/mns/systemMessage/sendPlatForm")
Result sendPlatForm(Map<String, Object> params) {
return mnsClient.sendPlatForm(params);
}
/**
* 更新系统消息
*
* @param params 参数对象
* @return 返回执行结果
*/
@PostMapping("/mns/systemMessage/update")
Result update(Map<String, Object> params) {
return mnsClient.update(params);
}
}

View File

@ -0,0 +1,72 @@
package cn.crtech.feign.controller;
import cn.crtech.cloud.common.api.CommonResult;
import cn.crtech.cloud.common.dto.Result;
import cn.crtech.feign.service.ResourceClient;
import feign.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
@Controller
public class ResourceApi {
@Autowired
private ResourceClient resourceClient;
@RequestMapping(value = "/rm/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ResponseBody
public CommonResult addFile(@RequestPart(name = "file") MultipartFile file,
@RequestParam(name = "path") String path,
@RequestParam(name = "name") String name,
@RequestParam(name = "description") String description) {
return resourceClient.addFile(file, path, name, description);
}
@GetMapping("/rm/s/{fileId}")
@ResponseBody
public CommonResult showUrl(@PathVariable(value = "fileId") String fileId) {
return resourceClient.showUrl(fileId);
}
@PostMapping("/rm/s/much")
@ResponseBody
public CommonResult showMuchUrl(@RequestBody Map<String, Object> params) {
return resourceClient.showMuchUrl(params);
}
@GetMapping("/rm/d/{fileId}")
@ResponseBody
public CommonResult downloadUrl(@PathVariable(value = "fileId") String fileId) {
return resourceClient.downloadUrl(fileId);
}
@PostMapping("/rm/remove")
@ResponseBody
public CommonResult remove(@RequestBody List<String> fileIds) {
return resourceClient.remove(fileIds);
}
@GetMapping("/getFileStream")
public Response getFileStream(@RequestParam("fileId")String fileId) {
return resourceClient.getFileStream(fileId);
}
@GetMapping(value = {"/rm/byte/res/stream/{fileId}", "/rm/byte/down/stream/{fileId}"}, produces = "application/json;charset=utf-8")
@ResponseBody
public CommonResult getFileStreamByFileId(@PathVariable("fileId") String fileId) {
return resourceClient.getFileStreamByFileId(fileId);
}
@GetMapping("/rm/copyOfMinIoFile")
@ResponseBody
public Result copyOfMinIoFile(@RequestParam(value = "fileId") String fileId) {
return resourceClient.copyOfMinIoFile(fileId);
}
}

View File

@ -0,0 +1,562 @@
package cn.crtech.feign.controller;
import cn.crtech.cloud.common.dto.Result;
import cn.crtech.feign.dto.RegisterDto;
import cn.crtech.feign.service.CloudMisClient;
import cn.crtech.feign.service.TmClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class UserApi {
private TmClient tmClient;
private CloudMisClient cloudMisClient;
@Autowired
public UserApi(TmClient tmClient, CloudMisClient cloudMisClient) {
this.tmClient = tmClient;
this.cloudMisClient = cloudMisClient;
}
/************************************* 公司角色 start *******************************************/
/**
* 条件查询公司角色数据
*
* @param obj 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/comRole/loadComRoleByPage")
public Result loadComRoleByPage(@RequestBody Map<String, Object> obj) {
return cloudMisClient.loadComRoleByPage(obj);
}
/**
* 创建公司角色数据信息
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/createComRole")
public Result createComRole(@RequestBody Map<String, Object> obj) {
return cloudMisClient.createComRole(obj);
}
/**
* 修改公司角色数据信息
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/updateComRole")
public Result updateComRole(@RequestBody Map<String, Object> obj) {
return cloudMisClient.updateComRole(obj);
}
/**
* 删除公司角色
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/delComRole")
public Result delComRole(@RequestBody Map<String, Object> obj) {
return cloudMisClient.delComRole(obj);
}
/**
* 公司角色设定管理员
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/setComAdminRole")
public Result setComAdminRole(@RequestBody Map<String, Object> obj) {
return cloudMisClient.setComAdminRole(obj);
}
/**
* 查询获取公司已授权绑定的产品数据
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/loadComBindSeriesApp")
public Result loadComBindSeriesApp(@RequestBody Map<String, Object> obj) {
return cloudMisClient.loadComBindSeriesApp(obj);
}
/**
* 查询获取公司已授权绑定产品的菜单数据
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/loadComAppPopedom")
public Result loadComAppPopedom(@RequestBody Map<String, Object> obj) {
return cloudMisClient.loadComAppPopedom(obj);
}
/**
* 查询获取公司已授权绑定产品菜单特殊权限数据
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/loadComAppPopAuthority")
public Result loadComAppPopAuthority(@RequestBody Map<String, Object> obj) {
return cloudMisClient.loadComAppPopAuthority(obj);
}
/**
* 角色绑定授权
*
* @param params 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/roleBindAuth")
public Result roleBindAuth(@RequestBody Map<String, Object> params) {
return cloudMisClient.roleBindAuth(params);
}
/**
* 查询角色绑定用户信息
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/comRole/loadComRoleUser")
public Result loadComRoleUser(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadComRoleUser(params);
}
/**
* 角色绑定用户信息
*
* @param params 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comRole/comRoleBindUser")
public Result comRoleBindUser(@RequestBody Map<String, Object> params) {
return cloudMisClient.comRoleBindUser(params);
}
/************************************* 公司角色 end *******************************************/
/************************************* 授权默认角色 start *******************************************/
/**
* 条件分页查询企业授权产品默认角色
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/defaultRole/loadComAuthDefaultRoleByPage")
Result loadComAuthDefaultRoleByPage(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadComAuthDefaultRoleByPage(params);
}
/**
* 查询默认角色授权数据
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/defaultRole/loadDefaultRoleAuth")
Result loadDefaultRoleAuth(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadDefaultRoleAuth(params);
}
/**
* 获取当前角色未绑定用户数据
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/defaultRole/loadUnbindDefaultRoleUser")
Result loadUnbindDefaultRoleUser(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadUnbindDefaultRoleUser(params);
}
/**
* 角色批量绑定用户
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/defaultRole/defaultRoleBindUser")
Result defaultRoleBindUser(@RequestBody Map<String, Object> params) {
return cloudMisClient.defaultRoleBindUser(params);
}
/************************************* 授权默认角色 end *******************************************/
/************************************* 企业用户 start *******************************************/
/**
* 条件分页查询企业用户信息
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/comUser/loadComUserByPage")
public Result loadComUserByPage(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadComUserByPage(params);
}
/**
* 用户注册新增
*
* @param registerDto 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comUser/createComUser")
public Result createComUser(@RequestBody Map<String, Object> registerDto) {
return cloudMisClient.createComUser(registerDto);
}
/**
* 更新企业用户信息
*
* @param params 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comUser/updateComUser")
public Result updateComUser(@RequestBody Map<String, Object> params) {
return cloudMisClient.updateComUser(params);
}
/**
* 企业用户状态修改
*
* @param params 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comUser/changeComUserState")
public Result changeComUserState(@RequestBody Map<String, Object> params) {
return cloudMisClient.changeComUserState(params);
}
/**
* 获取企业角色内容
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/comUser/loadComUserRoleData")
public Result loadComUserRoleData(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadComUserRoleData(params);
}
/**
* 用户绑定角色
*
* @param params 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comUser/comUserBindRole")
public Result comUserBindRole(@RequestBody Map<String, Object> params) {
return cloudMisClient.comUserBindRole(params);
}
/************************************* 企业用户 end *******************************************/
/************************************* 企业信息 start *******************************************/
/**
* 获取企业信息
*
* @param params 参数对象
* <ul>
* <li>companyCode: 公司标识</li>
* </ul>
* @return 返回查询结果
*/
@PostMapping("/misFeign/com/loadComInfo")
public Result loadComInfo(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadComInfo(params);
}
/************************************* 企业信息 end *******************************************/
/************************************* 个人企业 start *******************************************/
/**
* 修改用户默认企业
*
* @param params 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/comUser/setDefaultCompany")
public Result setDefaultCompany(@RequestBody Map<String, Object> params) {
return cloudMisClient.setDefaultCompany(params);
}
/************************************* 个人企业 end *******************************************/
/************************************* 个人技能证书 start *******************************************/
/**
* 用户技能证书提交
*
* @param params 参数对象
* @return 返回结果
*/
@PostMapping("/misFeign/userSkill/uploadCertificate")
public Result uploadCertificate(@RequestBody Map<String, Object> params) {
return cloudMisClient.uploadCertificate(params);
}
/**
* 查询个人技能证书
*
* @param params 参数对象
* <ul>
* <li>userId: 用户ID</li>
* </ul>
* @return 返回查询结果
*/
@PostMapping("/misFeign/userSkill/loadSelfCertificate")
public Result loadSelfCertificate(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadSelfCertificate(params);
}
/**
* 删除个人技能证书
*
* @param params 参数对象
* <ul>
* <li>id: 技能内容ID</li>
* </ul>
* @return 返回操作结果
*/
@PostMapping("/misFeign/userSkill/delSelfCertificate")
public Result delSelfCertificate(@RequestBody Map<String, Object> params) {
return cloudMisClient.delSelfCertificate(params);
}
/**
* 条件获取字典数据信息
*
* @param params 参数对象
* <ul>
* <li>typeCode: 字典类型标识</li>
* <li>code: 字典数据标识</li>
* <li>name: 字典数据名称</li>
* </ul>
* @return 返回查询结果
*/
@PostMapping("/misFeign/dict/loadDictDataByCondition")
public Result loadDictDataByCondition(@RequestBody Map<String, Object> params) {
return cloudMisClient.loadDictDataByCondition(params);
}
/************************************* 个人技能证书 end *******************************************/
/************************************* 安全中心 start *******************************************/
/**
* 用户密码修改
*
* @param obj 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/user/updateUserPwd")
public Result updateUserPwd(@RequestBody Map<String, Object> obj) {
return cloudMisClient.updateUserPwd(obj);
}
/************************************* 安全中心 end *******************************************/
/************************************* 个人中心 start *******************************************/
/**
* 更新用户信息
*
* @param params 参数对象
* <ul>
* <li>id: 用户ID</li>
* <li>email: 邮箱地址</li>
* <li>name: 名称</li>
* <li>state: 企业用户状态</li>
* </ul>
* @return 返回操作结果
*/
@PostMapping("/misFeign/user/renewUserInfo")
public Result renewUserInfo(@RequestBody Map<String, Object> params) {
try {
cloudMisClient.renewUserInfo(params);
;
tmClient.updateStaffInfo(params);
} catch (Exception e) {
return Result.error("修改人员失败");
}
return Result.success();
}
/**
* 获取团队管理用户信息
*
* @param params 参数对象
* @return 返回查询结果
*/
@PostMapping("/user/staff/queryById")
public Result queryStaffById(@RequestBody Map<String, Object> params) {
return tmClient.queryStaffById(params);
}
/************************************* 个人中心 end *******************************************/
/************************************* 其他调用内容 start **************************************/
/**
* 条件查询所有满足条件的所有用户数据(产品标识及公司标识)
*
* @param params 参数对象
* <ul>
* <li>applicationCode: 产品标识</li>
* <li>companyCode: 公司标识</li>
* </ul>
* @return 返回查询结果
*/
@PostMapping("/misFeign/user/queryAll")
public Result queryAllUser(@RequestParam Map<String, Object> params) {
return cloudMisClient.queryAllUser(params);
}
/**
* 条件查询所有满足条件的所有用户数据(公司标识)
*
* @param params 参数对象
* <ul>
* <li>companyCode: 公司标识</li>
* </ul>
* @return 返回查询结果
*/
@PostMapping("/misFeign/user/queryUserByCompanyCode")
public Result queryUserByCompanyCode(@RequestParam Map<String, Object> params) {
return cloudMisClient.queryUserByCompanyCode(params);
}
/**
* 根据手机号码查询用户信息
*
* @param mobile 用户手机号码
* @return 返回查询结果
*/
@PostMapping("/misFeign/user/queryUserByMobile")
public Result queryUserByMobile(@RequestParam String mobile) {
Map<String, Object> params = new HashMap<>();
params.put("mobile", mobile);
return cloudMisClient.queryUserByMobile(params);
}
/**
* 客户端用户注册
*
* @param user 参数对象
* @return 返回操作结果
*/
@PostMapping("/misFeign/user/clientRegister")
Result clientRegister(@RequestBody Map<String, Object> user) {
return cloudMisClient.clientRegister(user);
}
/**
* 条件查询所有满足条件的所有用户数据(角色标识)
*
* @param params 参数对象
* <ul>
* <li>pageNum: 分页页码</li>
* <li>pageSize: 数据个数</li>
* <li>roleCode: 对应角色标识集合</li>
* <li>nickName: 用户别名</li>
* <li>companyName: 企业名称</li>
* <li>mobile: 手机号码</li>
* </ul>
* @return 返回查询结果
*/
@PostMapping("/misFeign/user/queryUserByRoleCode")
Result queryUserByRoleCode(@RequestBody Map<String, Object> params) {
return cloudMisClient.queryUserByRoleCode(params);
}
/**
* 查询非公司用户
*
* @return 返回查询结果
*/
@PostMapping("/misFeign/user/queryNotCompanyUser")
Result queryNotCompanyUser() {
return cloudMisClient.queryNotCompanyUser();
}
/**
* 查询已创建的公司医院信息
*
* @return 返回查询结果
*/
@PostMapping("/misFeign/company/queryHospitalInfo")
Result queryHospitalInfo() {
return cloudMisClient.queryHospitalInfo();
}
/**
* 授权数据信息查询
*
* @param obj 参数对象
* @return 返回查询结果
*/
@PostMapping("/misFeign/comApp/loadRoleApp")
Result loadComAuthApp(@RequestBody Map<String, Object> obj) {
return cloudMisClient.loadComAuthApp(obj);
}
/**
* 更新用户信息(团队管理)
*
* @param params 参数对象
* <ul>
* <li>userName: 用户名称</li>
* <li>serialNo: 员工编号</li>
* <li>userId: 用户ID</li>
* <li>mobile: 手机号码</li>
* </ul>
* @return 返回操作结果
*/
@PostMapping("/misFeign/user/updateUserInfoByTm")
Result updateUserInfoByTm(@RequestBody Map<String, Object> params) {
return cloudMisClient.updateUserInfoByTm(params);
}
/**
* 获取连锁数据集合
*
* @param params 参数对象
* <ul>
* <li>chainName: 连锁名称</li>
* <li>chainCode: 连锁标识</li>
* <li>mechanismName: 机构名称</li>
* <li>mechanismCode: 机构标识</li>
* <li>isHeadOffice: 是否连锁总店</li>
* <li>isSystemCompany: 是否存在关联公司</li>
* </ul>
* @return 返回查询结果
*/
@PostMapping("/misFeign/chain/getChainList")
Result getChainList(@RequestBody Map<String, Object> params) {
return cloudMisClient.getChainList(params);
}
/**
* 批量新增用户
*
* @param registerDtos 用户数据集合
* @return 返回操作结果
*/
@PostMapping("/misFeign/comUser/batchRegister")
public Result batchRegister(@RequestBody List<RegisterDto> registerDtos) {
return cloudMisClient.batchRegister(registerDtos);
}
/**
* 初始化企业用户团队管理用户信息
*
* @param objectList 用户信息数据集合
* @return 返回操作结果
*/
@PostMapping("/user/staff/initTMStaff")
public Result initTMStaff(@RequestBody List<Map<String, Object>> objectList) {
return tmClient.initTMStaff(objectList);
}
}

View File

@ -0,0 +1,27 @@
package cn.crtech.feign.dto;
import lombok.*;
import java.io.Serializable;
/**
* 连锁主界面用户注册DTO对象
*
* @author TYP
* @since 2023-02-23 16:18
*/
@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ChainUserDto implements Serializable {
private Integer companyId;
private String name;
private String mobile;
private String email;
private String zhiyid;
private String lgnname;
private String password;
}

View File

@ -0,0 +1,15 @@
package cn.crtech.feign.dto;
import cn.crtech.feign.pojo.Message;
import cn.crtech.feign.pojo.MessageList;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class MessageDto implements Serializable {
private Message message;
private List<MessageList> messageLists;
}

Some files were not shown because too many files have changed in this diff Show More