提交 1c9fcbf4 编写于 作者: zlt2000's avatar zlt2000

增加全功能spring-cloud-gateway,优化部分公共模块适配webFlux

上级 eecaf5bb
...@@ -109,6 +109,7 @@ central-platform -- 父项目,公共依赖 ...@@ -109,6 +109,7 @@ central-platform -- 父项目,公共依赖
├─search-server -- 搜索中心服务端[7100] ├─search-server -- 搜索中心服务端[7100]
│─zlt-commons -- 通用工具一级工程 │─zlt-commons -- 通用工具一级工程
├─zlt-auth-client-spring-boot-starter -- 封装spring security client端的通用操作逻辑 ├─zlt-auth-client-spring-boot-starter -- 封装spring security client端的通用操作逻辑
├─zlt-common-core -- 封装通用操作逻辑
├─zlt-common-spring-boot-starter -- 封装通用操作逻辑 ├─zlt-common-spring-boot-starter -- 封装通用操作逻辑
├─zlt-db-spring-boot-starter -- 封装数据库通用操作逻辑 ├─zlt-db-spring-boot-starter -- 封装数据库通用操作逻辑
├─zlt-log-spring-boot-starter -- 封装log通用操作逻辑 ├─zlt-log-spring-boot-starter -- 封装log通用操作逻辑
...@@ -119,6 +120,7 @@ central-platform -- 父项目,公共依赖 ...@@ -119,6 +120,7 @@ central-platform -- 父项目,公共依赖
├─zlt-config -- 配置中心 ├─zlt-config -- 配置中心
├─zlt-doc -- 项目文档 ├─zlt-doc -- 项目文档
├─zlt-gateway -- api网关一级工程 ├─zlt-gateway -- api网关一级工程
├─sc-gateway -- spring-cloud-gateway[9900]
├─zuul-gateway -- netflix-zuul[9900] ├─zuul-gateway -- netflix-zuul[9900]
├─zlt-job -- 分布式任务调度一级工程 ├─zlt-job -- 分布式任务调度一级工程
├─job-admin -- 任务管理器[8081] ├─job-admin -- 任务管理器[8081]
......
...@@ -46,7 +46,6 @@ ...@@ -46,7 +46,6 @@
<spring-social-security.version>1.1.6.RELEASE</spring-social-security.version> <spring-social-security.version>1.1.6.RELEASE</spring-social-security.version>
<commons-io.version>2.6</commons-io.version> <commons-io.version>2.6</commons-io.version>
<servlet-api.version>4.0.1</servlet-api.version> <servlet-api.version>4.0.1</servlet-api.version>
<!--<platform-bom>Cairo-SR3</platform-bom>-->
<spring-cloud-alibaba-dependencies.version>2.1.0.RELEASE</spring-cloud-alibaba-dependencies.version> <spring-cloud-alibaba-dependencies.version>2.1.0.RELEASE</spring-cloud-alibaba-dependencies.version>
<spring-boot-dependencies.version>2.1.8.RELEASE</spring-boot-dependencies.version> <spring-boot-dependencies.version>2.1.8.RELEASE</spring-boot-dependencies.version>
<spring-cloud-dependencies.version>Greenwich.SR3</spring-cloud-dependencies.version> <spring-cloud-dependencies.version>Greenwich.SR3</spring-cloud-dependencies.version>
...@@ -140,7 +139,11 @@ ...@@ -140,7 +139,11 @@
<artifactId>zlt-config</artifactId> <artifactId>zlt-config</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-common-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>io.jsonwebtoken</groupId> <groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId> <artifactId>jjwt</artifactId>
...@@ -343,13 +346,6 @@ ...@@ -343,13 +346,6 @@
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<!--<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>${platform-bom}</version>
<type>pom</type>
<scope>import</scope>
</dependency>-->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId> <artifactId>spring-cloud-dependencies</artifactId>
...@@ -441,6 +437,8 @@ ...@@ -441,6 +437,8 @@
<developer> <developer>
<name>LeTao Zhu</name> <name>LeTao Zhu</name>
<email>zltdiablo@163.com</email> <email>zltdiablo@163.com</email>
<organizationUrl>https://github.com/zlt2000</organizationUrl>
<url>https://blog.csdn.net/zlt2000</url>
</developer> </developer>
</developers> </developers>
</project> </project>
\ No newline at end of file
...@@ -35,6 +35,10 @@ ...@@ -35,6 +35,10 @@
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
......
...@@ -14,10 +14,18 @@ ...@@ -14,10 +14,18 @@
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>zlt-config</artifactId> <artifactId>zlt-config</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-common-spring-boot-starter</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>search-client</artifactId> <artifactId>search-client</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
......
...@@ -44,6 +44,10 @@ ...@@ -44,6 +44,10 @@
<artifactId>search-client</artifactId> <artifactId>search-client</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
...@@ -67,6 +71,11 @@ ...@@ -67,6 +71,11 @@
<groupId>io.micrometer</groupId> <groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId> <artifactId>micrometer-registry-prometheus</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -19,5 +19,6 @@ ...@@ -19,5 +19,6 @@
<module>zlt-ribbon-spring-boot-starter</module> <module>zlt-ribbon-spring-boot-starter</module>
<module>zlt-auth-client-spring-boot-starter</module> <module>zlt-auth-client-spring-boot-starter</module>
<module>zlt-sentinel-spring-boot-starter</module> <module>zlt-sentinel-spring-boot-starter</module>
<module>zlt-common-core</module>
</modules> </modules>
</project> </project>
\ No newline at end of file
...@@ -12,10 +12,6 @@ ...@@ -12,10 +12,6 @@
<artifactId>zlt-auth-client-spring-boot-starter</artifactId> <artifactId>zlt-auth-client-spring-boot-starter</artifactId>
<description>认证客户端通用组件</description> <description>认证客户端通用组件</description>
<dependencies> <dependencies>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-common-spring-boot-starter</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId> <artifactId>spring-cloud-starter-oauth2</artifactId>
...@@ -23,6 +19,7 @@ ...@@ -23,6 +19,7 @@
<dependency> <dependency>
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>zlt-redis-spring-boot-starter</artifactId> <artifactId>zlt-redis-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
......
...@@ -6,6 +6,7 @@ import org.springframework.context.annotation.Import; ...@@ -6,6 +6,7 @@ import org.springframework.context.annotation.Import;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler; import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
...@@ -52,12 +53,14 @@ public class DefaultResourceServerConf extends ResourceServerConfigurerAdapter { ...@@ -52,12 +53,14 @@ public class DefaultResourceServerConf extends ResourceServerConfigurerAdapter {
.antMatchers(HttpMethod.OPTIONS).permitAll() .antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest(); .anyRequest();
setAuthenticate(authorizedUrl); setAuthenticate(authorizedUrl);
//允许使用iframe 嵌套,避免swagger-ui 不被加载的问题
http.headers() http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.frameOptions() .and()
.disable() .httpBasic().disable()
.and() .headers()
.csrf().disable(); .frameOptions().disable()
.and()
.csrf().disable();
} }
/** /**
......
package com.central.oauth2.common.config;
import com.central.oauth2.common.properties.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityPropertiesConfig {
}
package com.central.oauth2.common.service;
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
/**
* 请求权限判断service
*
* @author zlt
* @date 2018/10/28
*/
public interface IPermissionService {
/**
* 判断请求是否有权限
*
* @param authentication 认证信息
* @return 是否有权限
*/
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
...@@ -6,7 +6,6 @@ import com.central.common.constant.CommonConstant; ...@@ -6,7 +6,6 @@ import com.central.common.constant.CommonConstant;
import com.central.common.context.TenantContextHolder; import com.central.common.context.TenantContextHolder;
import com.central.common.model.SysMenu; import com.central.common.model.SysMenu;
import com.central.oauth2.common.properties.SecurityProperties; import com.central.oauth2.common.properties.SecurityProperties;
import com.central.oauth2.common.service.IPermissionService;
import com.central.oauth2.common.util.AuthUtils; import com.central.oauth2.common.util.AuthUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
...@@ -18,7 +17,6 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; ...@@ -18,7 +17,6 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import javax.servlet.http.HttpServletRequest;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -29,7 +27,7 @@ import java.util.stream.Collectors; ...@@ -29,7 +27,7 @@ import java.util.stream.Collectors;
* @date 2018/10/28 * @date 2018/10/28
*/ */
@Slf4j @Slf4j
public abstract class DefaultPermissionServiceImpl implements IPermissionService { public abstract class DefaultPermissionServiceImpl {
@Autowired @Autowired
private SecurityProperties securityProperties; private SecurityProperties securityProperties;
...@@ -43,10 +41,9 @@ public abstract class DefaultPermissionServiceImpl implements IPermissionService ...@@ -43,10 +41,9 @@ public abstract class DefaultPermissionServiceImpl implements IPermissionService
*/ */
public abstract List<SysMenu> findMenuByRoleCodes(String roleCodes); public abstract List<SysMenu> findMenuByRoleCodes(String roleCodes);
@Override public boolean hasPermission(Authentication authentication, String requestMethod, String requestURI) {
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
// 前端跨域OPTIONS请求预检放行 也可通过前端配置代理实现 // 前端跨域OPTIONS请求预检放行 也可通过前端配置代理实现
if (HttpMethod.OPTIONS.name().equalsIgnoreCase(request.getMethod())) { if (HttpMethod.OPTIONS.name().equalsIgnoreCase(requestMethod)) {
return true; return true;
} }
if (!(authentication instanceof AnonymousAuthenticationToken)) { if (!(authentication instanceof AnonymousAuthenticationToken)) {
...@@ -68,7 +65,7 @@ public abstract class DefaultPermissionServiceImpl implements IPermissionService ...@@ -68,7 +65,7 @@ public abstract class DefaultPermissionServiceImpl implements IPermissionService
//判断不进行url权限认证的api,所有已登录用户都能访问的url //判断不进行url权限认证的api,所有已登录用户都能访问的url
for (String path : securityProperties.getAuth().getUrlPermission().getIgnoreUrls()) { for (String path : securityProperties.getAuth().getUrlPermission().getIgnoreUrls()) {
if (antPathMatcher.match(path, request.getRequestURI())) { if (antPathMatcher.match(path, requestURI)) {
return true; return true;
} }
} }
...@@ -86,9 +83,9 @@ public abstract class DefaultPermissionServiceImpl implements IPermissionService ...@@ -86,9 +83,9 @@ public abstract class DefaultPermissionServiceImpl implements IPermissionService
String roleCodes = grantedAuthorityList.stream().map(SimpleGrantedAuthority::getAuthority).collect(Collectors.joining(", ")); String roleCodes = grantedAuthorityList.stream().map(SimpleGrantedAuthority::getAuthority).collect(Collectors.joining(", "));
List<SysMenu> menuList = findMenuByRoleCodes(roleCodes); List<SysMenu> menuList = findMenuByRoleCodes(roleCodes);
for (SysMenu menu : menuList) { for (SysMenu menu : menuList) {
if (StringUtils.isNotEmpty(menu.getUrl()) && antPathMatcher.match(menu.getUrl(), request.getRequestURI())) { if (StringUtils.isNotEmpty(menu.getUrl()) && antPathMatcher.match(menu.getUrl(), requestURI)) {
if (StrUtil.isNotEmpty(menu.getPathMethod())) { if (StrUtil.isNotEmpty(menu.getPathMethod())) {
return request.getMethod().equalsIgnoreCase(menu.getPathMethod()); return requestMethod.equalsIgnoreCase(menu.getPathMethod());
} else { } else {
return true; return true;
} }
......
...@@ -52,7 +52,7 @@ public class AuthUtils { ...@@ -52,7 +52,7 @@ public class AuthUtils {
Enumeration<String> headers = request.getHeaders(CommonConstant.TOKEN_HEADER); Enumeration<String> headers = request.getHeaders(CommonConstant.TOKEN_HEADER);
while (headers.hasMoreElements()) { while (headers.hasMoreElements()) {
String value = headers.nextElement(); String value = headers.nextElement();
if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) { if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE))) {
String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim(); String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
int commaIndex = authHeaderValue.indexOf(','); int commaIndex = authHeaderValue.indexOf(',');
if (commaIndex > 0) { if (commaIndex > 0) {
......
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.central.oauth2.common.config.SecurityPropertiesConfig
\ No newline at end of file
<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>
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-commons</artifactId>
<version>2.7.2</version>
</parent>
<artifactId>zlt-common-core</artifactId>
<description>公共通用组件</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-log-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- easypoi -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>banner</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
\ No newline at end of file
package com.central.common.annotation; package com.central.common.annotation;
import java.lang.annotation.*; import java.lang.annotation.*;
/** /**
* 请求的方法参数SysUser上添加该注解,则注入当前登录人信息 * 请求的方法参数SysUser上添加该注解,则注入当前登录人信息
* 例1:public void test(@LoginUser SysUser user) //只有username 和 roles * 例1:public void test(@LoginUser SysUser user) //只有username 和 roles
* 例2:public void test(@LoginUser(isFull = true) SysUser user) //能获取SysUser对象的所有信息 * 例2:public void test(@LoginUser(isFull = true) SysUser user) //能获取SysUser对象的所有信息
* *
* @author zlt * @author zlt
* @date 2018/7/24 16:44 * @date 2018/7/24 16:44
*/ */
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
public @interface LoginUser { public @interface LoginUser {
/** /**
* 是否查询SysUser对象所有信息,true则通过rpc接口查询 * 是否查询SysUser对象所有信息,true则通过rpc接口查询
*/ */
boolean isFull() default false; boolean isFull() default false;
} }
package com.central.common.config; package com.central.common.config;
import com.central.common.utils.CustomThreadPoolTaskExecutor; import com.central.common.utils.CustomThreadPoolTaskExecutor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.task.TaskExecutor; import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
/** /**
* @author zlt * @author zlt
* @date 2018/12/13 * @date 2018/12/13
*/ */
@Setter @Setter
@Getter @Getter
@EnableAsync(proxyTargetClass = true) @EnableAsync(proxyTargetClass = true)
public class DefaultAsycTaskConfig { public class DefaultAsycTaskConfig {
/** /**
* 线程池维护线程的最小数量. * 线程池维护线程的最小数量.
*/ */
@Value("${asyc-task.corePoolSize:10}") @Value("${asyc-task.corePoolSize:10}")
private int corePoolSize; private int corePoolSize;
/** /**
* 线程池维护线程的最大数量 * 线程池维护线程的最大数量
*/ */
@Value("${asyc-task.maxPoolSize:200}") @Value("${asyc-task.maxPoolSize:200}")
private int maxPoolSize; private int maxPoolSize;
/** /**
* 队列最大长度 * 队列最大长度
*/ */
@Value("${asyc-task.queueCapacity:10}") @Value("${asyc-task.queueCapacity:10}")
private int queueCapacity; private int queueCapacity;
/** /**
* 线程池前缀 * 线程池前缀
*/ */
@Value("${asyc-task.threadNamePrefix:ZltExecutor-}") @Value("${asyc-task.threadNamePrefix:ZltExecutor-}")
private String threadNamePrefix; private String threadNamePrefix;
@Bean @Bean
public TaskExecutor taskExecutor() { public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new CustomThreadPoolTaskExecutor(); ThreadPoolTaskExecutor executor = new CustomThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize); executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize); executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity); executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix(threadNamePrefix); executor.setThreadNamePrefix(threadNamePrefix);
/* /*
rejection-policy:当pool已经达到max size的时候,如何处理新任务 rejection-policy:当pool已经达到max size的时候,如何处理新任务
CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行 CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
*/ */
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize(); executor.initialize();
return executor; return executor;
} }
} }
package com.central.common.config; package com.central.common.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
/** /**
* @author zlt * @author zlt
* 密码工具类 * 密码工具类
*/ */
public class DefaultPasswordConfig { public class DefaultPasswordConfig {
/** /**
* 装配BCryptPasswordEncoder用户密码的匹配 * 装配BCryptPasswordEncoder用户密码的匹配
* @return * @return
*/ */
@Bean @Bean
public PasswordEncoder passwordEncoder() { public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
} }
} }
package com.central.common.config; package com.central.common.config;
import com.central.common.feign.UserService; import com.central.common.feign.UserService;
import com.central.common.resolver.ClientArgumentResolver; import com.central.common.resolver.ClientArgumentResolver;
import com.central.common.resolver.TokenArgumentResolver; import com.central.common.resolver.TokenArgumentResolver;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List; import java.util.List;
/** /**
* 公共配置类, 一些公共工具配置 * 公共配置类, 一些公共工具配置
* *
* @author zlt * @author zlt
* @date 2018/8/25 * @date 2018/8/25
*/ */
public class LoginArgResolverConfig implements WebMvcConfigurer { public class LoginArgResolverConfig implements WebMvcConfigurer {
@Lazy @Lazy
@Autowired @Autowired
private UserService userService; private UserService userService;
/** /**
* Token参数解析 * Token参数解析
* *
* @param argumentResolvers 解析类 * @param argumentResolvers 解析类
*/ */
@Override @Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
//注入用户信息 //注入用户信息
argumentResolvers.add(new TokenArgumentResolver(userService)); argumentResolvers.add(new TokenArgumentResolver(userService));
//注入应用信息 //注入应用信息
argumentResolvers.add(new ClientArgumentResolver()); argumentResolvers.add(new ClientArgumentResolver());
} }
} }
package com.central.common.constant; package com.central.common.constant;
/** /**
* 全局公共常量 * 全局公共常量
* *
* @author zlt * @author zlt
* @date 2018/10/29 * @date 2018/10/29
*/ */
public interface CommonConstant { public interface CommonConstant {
/** /**
* 项目版本号(banner使用) * 项目版本号(banner使用)
*/ */
String PROJECT_VERSION = "2.7.2"; String PROJECT_VERSION = "2.7.2";
/** /**
* token请求头名称 * token请求头名称
*/ */
String TOKEN_HEADER = "Authorization"; String TOKEN_HEADER = "Authorization";
/** /**
* The access token issued by the authorization server. This value is REQUIRED. * The access token issued by the authorization server. This value is REQUIRED.
*/ */
String ACCESS_TOKEN = "access_token"; String ACCESS_TOKEN = "access_token";
String BEARER_TYPE = "Bearer"; String BEARER_TYPE = "Bearer";
/** /**
* 标签 header key * 标签 header key
*/ */
String HEADER_LABEL = "x-label"; String HEADER_LABEL = "x-label";
/** /**
* 标签 header 分隔符 * 标签 header 分隔符
*/ */
String HEADER_LABEL_SPLIT = ","; String HEADER_LABEL_SPLIT = ",";
/** /**
* 标签或 名称 * 标签或 名称
*/ */
String LABEL_OR = "labelOr"; String LABEL_OR = "labelOr";
/** /**
* 标签且 名称 * 标签且 名称
*/ */
String LABEL_AND = "labelAnd"; String LABEL_AND = "labelAnd";
/** /**
* 权重key * 权重key
*/ */
String WEIGHT_KEY = "weight"; String WEIGHT_KEY = "weight";
/** /**
* 删除 * 删除
*/ */
String STATUS_DEL = "1"; String STATUS_DEL = "1";
/** /**
* 正常 * 正常
*/ */
String STATUS_NORMAL = "0"; String STATUS_NORMAL = "0";
/** /**
* 锁定 * 锁定
*/ */
String STATUS_LOCK = "9"; String STATUS_LOCK = "9";
/** /**
* 目录 * 目录
*/ */
Integer CATALOG = -1; Integer CATALOG = -1;
/** /**
* 菜单 * 菜单
*/ */
Integer MENU = 1; Integer MENU = 1;
/** /**
* 权限 * 权限
*/ */
Integer PERMISSION = 2; Integer PERMISSION = 2;
/** /**
* 删除标记 * 删除标记
*/ */
String DEL_FLAG = "is_del"; String DEL_FLAG = "is_del";
/** /**
* 超级管理员用户名 * 超级管理员用户名
*/ */
String ADMIN_USER_NAME = "admin"; String ADMIN_USER_NAME = "admin";
/** /**
* 公共日期格式 * 公共日期格式
*/ */
String MONTH_FORMAT = "yyyy-MM"; String MONTH_FORMAT = "yyyy-MM";
String DATE_FORMAT = "yyyy-MM-dd"; String DATE_FORMAT = "yyyy-MM-dd";
String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
String SIMPLE_MONTH_FORMAT = "yyyyMM"; String SIMPLE_MONTH_FORMAT = "yyyyMM";
String SIMPLE_DATE_FORMAT = "yyyyMMdd"; String SIMPLE_DATE_FORMAT = "yyyyMMdd";
String SIMPLE_DATETIME_FORMAT = "yyyyMMddHHmmss"; String SIMPLE_DATETIME_FORMAT = "yyyyMMddHHmmss";
String DEF_USER_PASSWORD = "123456"; String DEF_USER_PASSWORD = "123456";
String LOCK_KEY_PREFIX = "LOCK_KEY:"; String LOCK_KEY_PREFIX = "LOCK_KEY:";
/** /**
* 租户id参数 * 租户id参数
*/ */
String TENANT_ID_PARAM = "tenantId"; String TENANT_ID_PARAM = "tenantId";
/** /**
* 日志链路追踪id信息头 * 日志链路追踪id信息头
*/ */
String TRACE_ID_HEADER = "x-traceId-header"; String TRACE_ID_HEADER = "x-traceId-header";
/** /**
* 日志链路追踪id日志标志 * 日志链路追踪id日志标志
*/ */
String LOG_TRACE_ID = "traceId"; String LOG_TRACE_ID = "traceId";
/** /**
* 负载均衡策略-版本号 信息头 * 负载均衡策略-版本号 信息头
*/ */
String Z_L_T_VERSION = "z-l-t-version"; String Z_L_T_VERSION = "z-l-t-version";
/** /**
* 注册中心元数据 版本号 * 注册中心元数据 版本号
*/ */
String METADATA_VERSION = "version"; String METADATA_VERSION = "version";
} }
package com.central.common.constant; package com.central.common.constant;
/** /**
* Security 权限常量 * Security 权限常量
* *
* @author zlt * @author zlt
*/ */
public interface SecurityConstants { public interface SecurityConstants {
/** /**
* 用户信息分隔符 * 用户信息分隔符
*/ */
String USER_SPLIT = ":"; String USER_SPLIT = ":";
/** /**
* 用户信息头 * 用户信息头
*/ */
String USER_HEADER = "x-user-header"; String USER_HEADER = "x-user-header";
/** /**
* 用户id信息头 * 用户id信息头
*/ */
String USER_ID_HEADER = "x-userid-header"; String USER_ID_HEADER = "x-userid-header";
/** /**
* 角色信息头 * 角色信息头
*/ */
String ROLE_HEADER = "x-role-header"; String ROLE_HEADER = "x-role-header";
/** /**
* 租户信息头(应用) * 租户信息头(应用)
*/ */
String TENANT_HEADER = "x-tenant-header"; String TENANT_HEADER = "x-tenant-header";
/** /**
* 基础角色 * 基础角色
*/ */
String BASE_ROLE = "ROLE_USER"; String BASE_ROLE = "ROLE_USER";
/** /**
* 授权码模式 * 授权码模式
*/ */
String AUTHORIZATION_CODE = "authorization_code"; String AUTHORIZATION_CODE = "authorization_code";
/** /**
* 密码模式 * 密码模式
*/ */
String PASSWORD = "password"; String PASSWORD = "password";
/** /**
* 刷新token * 刷新token
*/ */
String REFRESH_TOKEN = "refresh_token"; String REFRESH_TOKEN = "refresh_token";
/** /**
* oauth token * oauth token
*/ */
String OAUTH_TOKEN_URL = "/oauth/token"; String OAUTH_TOKEN_URL = "/oauth/token";
/** /**
* 默认的处理验证码的url前缀 * 默认的处理验证码的url前缀
*/ */
String DEFAULT_VALIDATE_CODE_URL_PREFIX = "/validata/code"; String DEFAULT_VALIDATE_CODE_URL_PREFIX = "/validata/code";
/** /**
* 手机号的处理验证码的url前缀 * 手机号的处理验证码的url前缀
*/ */
String MOBILE_VALIDATE_CODE_URL_PREFIX = "/validata/smsCode"; String MOBILE_VALIDATE_CODE_URL_PREFIX = "/validata/smsCode";
/** /**
* 默认生成图形验证码宽度 * 默认生成图形验证码宽度
*/ */
String DEFAULT_IMAGE_WIDTH = "100"; String DEFAULT_IMAGE_WIDTH = "100";
/** /**
* 默认生成图像验证码高度 * 默认生成图像验证码高度
*/ */
String DEFAULT_IMAGE_HEIGHT = "35"; String DEFAULT_IMAGE_HEIGHT = "35";
/** /**
* 默认生成图形验证码长度 * 默认生成图形验证码长度
*/ */
String DEFAULT_IMAGE_LENGTH = "4"; String DEFAULT_IMAGE_LENGTH = "4";
/** /**
* 默认生成图形验证码过期时间 * 默认生成图形验证码过期时间
*/ */
int DEFAULT_IMAGE_EXPIRE = 60; int DEFAULT_IMAGE_EXPIRE = 60;
/** /**
* 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. * 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue.
*/ */
String DEFAULT_COLOR_FONT = "blue"; String DEFAULT_COLOR_FONT = "blue";
/** /**
* 图片边框 * 图片边框
*/ */
String DEFAULT_IMAGE_BORDER = "no"; String DEFAULT_IMAGE_BORDER = "no";
/** /**
* 默认图片间隔 * 默认图片间隔
*/ */
String DEFAULT_CHAR_SPACE = "5"; String DEFAULT_CHAR_SPACE = "5";
/** /**
* 默认保存code的前缀 * 默认保存code的前缀
*/ */
String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY"; String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY";
/** /**
* 验证码文字大小 * 验证码文字大小
*/ */
String DEFAULT_IMAGE_FONT_SIZE = "30"; String DEFAULT_IMAGE_FONT_SIZE = "30";
/** /**
* zlt公共前缀 * zlt公共前缀
*/ */
String ZLT_PREFIX = "zlt:"; String ZLT_PREFIX = "zlt:";
/** /**
* 缓存client的redis key,这里是hash结构存储 * 缓存client的redis key,这里是hash结构存储
*/ */
String CACHE_CLIENT_KEY = "oauth_client_details"; String CACHE_CLIENT_KEY = "oauth_client_details";
/** /**
* OAUTH模式登录处理地址 * OAUTH模式登录处理地址
*/ */
String OAUTH_LOGIN_PRO_URL = "/user/login"; String OAUTH_LOGIN_PRO_URL = "/user/login";
/** /**
* PASSWORD模式登录处理地址 * PASSWORD模式登录处理地址
*/ */
String PASSWORD_LOGIN_PRO_URL = "/oauth/user/token"; String PASSWORD_LOGIN_PRO_URL = "/oauth/user/token";
/** /**
* 获取授权码地址 * 获取授权码地址
*/ */
String AUTH_CODE_URL = "/oauth/authorize"; String AUTH_CODE_URL = "/oauth/authorize";
/** /**
* 登录页面 * 登录页面
*/ */
String LOGIN_PAGE = "/login.html"; String LOGIN_PAGE = "/login.html";
/** /**
* 默认的OPENID登录请求处理url * 默认的OPENID登录请求处理url
*/ */
String OPENID_TOKEN_URL = "/oauth/openId/token"; String OPENID_TOKEN_URL = "/oauth/openId/token";
/** /**
* 手机登录URL * 手机登录URL
*/ */
String MOBILE_TOKEN_URL = "/oauth/mobile/token"; String MOBILE_TOKEN_URL = "/oauth/mobile/token";
/** /**
* 登出URL * 登出URL
*/ */
String LOGOUT_URL = "/oauth/remove/token"; String LOGOUT_URL = "/oauth/remove/token";
/** /**
* 默认token过期时间(1小时) * 默认token过期时间(1小时)
*/ */
Integer ACCESS_TOKEN_VALIDITY_SECONDS = 60 * 60; Integer ACCESS_TOKEN_VALIDITY_SECONDS = 60 * 60;
/** /**
* redis中授权token对应的key * redis中授权token对应的key
*/ */
String REDIS_TOKEN_AUTH = "auth:"; String REDIS_TOKEN_AUTH = "auth:";
/** /**
* redis中应用对应的token集合的key * redis中应用对应的token集合的key
*/ */
String REDIS_CLIENT_ID_TO_ACCESS = "client_id_to_access:"; String REDIS_CLIENT_ID_TO_ACCESS = "client_id_to_access:";
/** /**
* redis中用户名对应的token集合的key * redis中用户名对应的token集合的key
*/ */
String REDIS_UNAME_TO_ACCESS = "uname_to_access:"; String REDIS_UNAME_TO_ACCESS = "uname_to_access:";
/** /**
* rsa公钥 * rsa公钥
*/ */
String RSA_PUBLIC_KEY = "pubkey.txt"; String RSA_PUBLIC_KEY = "pubkey.txt";
} }
package com.central.common.constant; package com.central.common.constant;
/** /**
* 服务名称常量 * 服务名称常量
* *
* @author zlt * @author zlt
* @date 2018/7/27 13:50 * @date 2018/7/27 13:50
*/ */
public interface ServiceNameConstants { public interface ServiceNameConstants {
/** /**
* 用户权限服务 * 用户权限服务
*/ */
String USER_SERVICE = "user-center"; String USER_SERVICE = "user-center";
/** /**
* 搜索中心服务 * 搜索中心服务
*/ */
String SEARCH_SERVICE = "search-center"; String SEARCH_SERVICE = "search-center";
} }
package com.central.common.exception; package com.central.common.exception;
/** /**
* 业务异常 * 业务异常
* *
* @author zlt * @author zlt
*/ */
public class BusinessException extends RuntimeException { public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 6610083281801529147L; private static final long serialVersionUID = 6610083281801529147L;
public BusinessException(String message) { public BusinessException(String message) {
super(message); super(message);
} }
} }
package com.central.common.exception; package com.central.common.exception;
import com.central.common.model.Result; import com.central.common.model.Result;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.sql.SQLException; import java.sql.SQLException;
/** /**
* 异常通用处理 * 异常通用处理
* *
* @author zlt * @author zlt
*/ */
@ResponseBody @ResponseBody
@Slf4j @Slf4j
public class DefaultExceptionAdvice { public class DefaultExceptionAdvice {
/** /**
* IllegalArgumentException异常处理返回json * IllegalArgumentException异常处理返回json
* 返回状态码:400 * 返回状态码:400
*/ */
@ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({IllegalArgumentException.class}) @ExceptionHandler({IllegalArgumentException.class})
public Result badRequestException(IllegalArgumentException e) { public Result badRequestException(IllegalArgumentException e) {
return defHandler("参数解析失败", e); return defHandler("参数解析失败", e);
} }
/** /**
* AccessDeniedException异常处理返回json * AccessDeniedException异常处理返回json
* 返回状态码:403 * 返回状态码:403
*/ */
@ResponseStatus(HttpStatus.FORBIDDEN) @ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler({AccessDeniedException.class}) @ExceptionHandler({AccessDeniedException.class})
public Result badMethodExpressException(AccessDeniedException e) { public Result badMethodExpressException(AccessDeniedException e) {
return defHandler("没有权限请求当前方法", e); return defHandler("没有权限请求当前方法", e);
} }
/** /**
* 返回状态码:405 * 返回状态码:405
*/ */
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler({HttpRequestMethodNotSupportedException.class}) @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
return defHandler("不支持当前请求方法", e); return defHandler("不支持当前请求方法", e);
} }
/** /**
* 返回状态码:415 * 返回状态码:415
*/ */
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler({HttpMediaTypeNotSupportedException.class}) @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) { public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
return defHandler("不支持当前媒体类型", e); return defHandler("不支持当前媒体类型", e);
} }
/** /**
* SQLException sql异常处理 * SQLException sql异常处理
* 返回状态码:500 * 返回状态码:500
*/ */
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler({SQLException.class}) @ExceptionHandler({SQLException.class})
public Result handleSQLException(SQLException e) { public Result handleSQLException(SQLException e) {
return defHandler("服务运行SQLException异常", e); return defHandler("服务运行SQLException异常", e);
} }
/** /**
* BusinessException 业务异常处理 * BusinessException 业务异常处理
* 返回状态码:500 * 返回状态码:500
*/ */
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(BusinessException.class) @ExceptionHandler(BusinessException.class)
public Result handleException(BusinessException e) { public Result handleException(BusinessException e) {
return defHandler("业务异常", e); return defHandler("业务异常", e);
} }
/** /**
* IdempotencyException 幂等性异常 * IdempotencyException 幂等性异常
* 返回状态码:200 * 返回状态码:200
*/ */
@ResponseStatus(HttpStatus.OK) @ResponseStatus(HttpStatus.OK)
@ExceptionHandler(IdempotencyException.class) @ExceptionHandler(IdempotencyException.class)
public Result handleException(IdempotencyException e) { public Result handleException(IdempotencyException e) {
return Result.failed(e.getMessage()); return Result.failed(e.getMessage());
} }
/** /**
* 所有异常统一处理 * 所有异常统一处理
* 返回状态码:500 * 返回状态码:500
*/ */
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
public Result handleException(Exception e) { public Result handleException(Exception e) {
return defHandler("未知异常", e); return defHandler("未知异常", e);
} }
private Result defHandler(String msg, Exception e) { private Result defHandler(String msg, Exception e) {
log.error(msg, e); log.error(msg, e);
return Result.failed(msg); return Result.failed(msg);
} }
} }
package com.central.common.exception; package com.central.common.exception;
/** /**
* 幂等性异常 * 幂等性异常
* *
* @author zlt * @author zlt
*/ */
public class IdempotencyException extends RuntimeException { public class IdempotencyException extends RuntimeException {
private static final long serialVersionUID = 6610083281801529147L; private static final long serialVersionUID = 6610083281801529147L;
public IdempotencyException(String message) { public IdempotencyException(String message) {
super(message); super(message);
} }
} }
package com.central.common.exception; package com.central.common.exception;
/** /**
* 分布式锁异常 * 分布式锁异常
* *
* @author zlt * @author zlt
*/ */
public class LockException extends RuntimeException { public class LockException extends RuntimeException {
private static final long serialVersionUID = 6610083281801529147L; private static final long serialVersionUID = 6610083281801529147L;
public LockException(String message) { public LockException(String message) {
super(message); super(message);
} }
} }
package com.central.common.feign; package com.central.common.feign;
import com.central.common.constant.ServiceNameConstants; import com.central.common.constant.ServiceNameConstants;
import com.central.common.feign.fallback.UserServiceFallbackFactory; import com.central.common.feign.fallback.UserServiceFallbackFactory;
import com.central.common.model.LoginAppUser; import com.central.common.model.LoginAppUser;
import com.central.common.model.SysUser; import com.central.common.model.SysUser;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
/** /**
* @author zlt * @author zlt
*/ */
@FeignClient(name = ServiceNameConstants.USER_SERVICE, fallbackFactory = UserServiceFallbackFactory.class, decode404 = true) @FeignClient(name = ServiceNameConstants.USER_SERVICE, fallbackFactory = UserServiceFallbackFactory.class, decode404 = true)
public interface UserService { public interface UserService {
/** /**
* feign rpc访问远程/users/{username}接口 * feign rpc访问远程/users/{username}接口
* 查询用户实体对象SysUser * 查询用户实体对象SysUser
* *
* @param username * @param username
* @return * @return
*/ */
@GetMapping(value = "/users/name/{username}") @GetMapping(value = "/users/name/{username}")
SysUser selectByUsername(@PathVariable("username") String username); SysUser selectByUsername(@PathVariable("username") String username);
/** /**
* feign rpc访问远程/users-anon/login接口 * feign rpc访问远程/users-anon/login接口
* *
* @param username * @param username
* @return * @return
*/ */
@GetMapping(value = "/users-anon/login", params = "username") @GetMapping(value = "/users-anon/login", params = "username")
LoginAppUser findByUsername(@RequestParam("username") String username); LoginAppUser findByUsername(@RequestParam("username") String username);
/** /**
* 通过手机号查询用户、角色信息 * 通过手机号查询用户、角色信息
* *
* @param mobile 手机号 * @param mobile 手机号
*/ */
@GetMapping(value = "/users-anon/mobile", params = "mobile") @GetMapping(value = "/users-anon/mobile", params = "mobile")
LoginAppUser findByMobile(@RequestParam("mobile") String mobile); LoginAppUser findByMobile(@RequestParam("mobile") String mobile);
/** /**
* 根据OpenId查询用户信息 * 根据OpenId查询用户信息
* *
* @param openId openId * @param openId openId
*/ */
@GetMapping(value = "/users-anon/openId", params = "openId") @GetMapping(value = "/users-anon/openId", params = "openId")
LoginAppUser findByOpenId(@RequestParam("openId") String openId); LoginAppUser findByOpenId(@RequestParam("openId") String openId);
} }
package com.central.common.feign.fallback; package com.central.common.feign.fallback;
import com.central.common.feign.UserService; import com.central.common.feign.UserService;
import com.central.common.model.LoginAppUser; import com.central.common.model.LoginAppUser;
import com.central.common.model.SysUser; import com.central.common.model.SysUser;
import feign.hystrix.FallbackFactory; import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* userService降级工场 * userService降级工场
* *
* @author zlt * @author zlt
* @date 2019/1/18 * @date 2019/1/18
*/ */
@Slf4j @Slf4j
@Component @Component
public class UserServiceFallbackFactory implements FallbackFactory<UserService> { public class UserServiceFallbackFactory implements FallbackFactory<UserService> {
@Override @Override
public UserService create(Throwable throwable) { public UserService create(Throwable throwable) {
return new UserService() { return new UserService() {
@Override @Override
public SysUser selectByUsername(String username) { public SysUser selectByUsername(String username) {
log.error("通过用户名查询用户异常:{}", username, throwable); log.error("通过用户名查询用户异常:{}", username, throwable);
return new SysUser(); return new SysUser();
} }
@Override @Override
public LoginAppUser findByUsername(String username) { public LoginAppUser findByUsername(String username) {
log.error("通过用户名查询用户异常:{}", username, throwable); log.error("通过用户名查询用户异常:{}", username, throwable);
return new LoginAppUser(); return new LoginAppUser();
} }
@Override @Override
public LoginAppUser findByMobile(String mobile) { public LoginAppUser findByMobile(String mobile) {
log.error("通过手机号查询用户异常:{}", mobile, throwable); log.error("通过手机号查询用户异常:{}", mobile, throwable);
return new LoginAppUser(); return new LoginAppUser();
} }
@Override @Override
public LoginAppUser findByOpenId(String openId) { public LoginAppUser findByOpenId(String openId) {
log.error("通过openId查询用户异常:{}", openId, throwable); log.error("通过openId查询用户异常:{}", openId, throwable);
return new LoginAppUser(); return new LoginAppUser();
} }
}; };
} }
} }
package com.central.common.lock; package com.central.common.lock;
/** /**
* 分布式锁抽象类 * 分布式锁抽象类
* *
* @author zlt * @author zlt
* @date 2018/5/29 14:14 * @date 2018/5/29 14:14
*/ */
public abstract class AbstractDistributedLock implements DistributedLock{ public abstract class AbstractDistributedLock implements DistributedLock{
@Override @Override
public boolean lock(String key) { public boolean lock(String key) {
return lock(key, TIMEOUT_MILLIS, RETRY_TIMES, SLEEP_MILLIS); return lock(key, TIMEOUT_MILLIS, RETRY_TIMES, SLEEP_MILLIS);
} }
@Override @Override
public boolean lock(String key, int retryTimes) { public boolean lock(String key, int retryTimes) {
return lock(key, TIMEOUT_MILLIS, retryTimes, SLEEP_MILLIS); return lock(key, TIMEOUT_MILLIS, retryTimes, SLEEP_MILLIS);
} }
@Override @Override
public boolean lock(String key, int retryTimes, long sleepMillis) { public boolean lock(String key, int retryTimes, long sleepMillis) {
return lock(key, TIMEOUT_MILLIS, retryTimes, sleepMillis); return lock(key, TIMEOUT_MILLIS, retryTimes, sleepMillis);
} }
@Override @Override
public boolean lock(String key, long expire) { public boolean lock(String key, long expire) {
return lock(key, expire, RETRY_TIMES, SLEEP_MILLIS); return lock(key, expire, RETRY_TIMES, SLEEP_MILLIS);
} }
@Override @Override
public boolean lock(String key, long expire, int retryTimes) { public boolean lock(String key, long expire, int retryTimes) {
return lock(key, expire, retryTimes, SLEEP_MILLIS); return lock(key, expire, retryTimes, SLEEP_MILLIS);
} }
} }
package com.central.common.lock; package com.central.common.lock;
/** /**
* 分布式锁顶级接口 * 分布式锁顶级接口
* 例如: * 例如:
* RETRY_TIMES=100,SLEEP_MILLIS=100 * RETRY_TIMES=100,SLEEP_MILLIS=100
* RETRY_TIMES * SLEEP_MILLIS = 10000 意味着如果一直获取不了锁,最长会等待10秒后抛超时异常 * RETRY_TIMES * SLEEP_MILLIS = 10000 意味着如果一直获取不了锁,最长会等待10秒后抛超时异常
* *
* @author zlt * @author zlt
* @date 2018/5/29 14:12 * @date 2018/5/29 14:12
*/ */
public interface DistributedLock { public interface DistributedLock {
/** /**
* 默认超时时间 * 默认超时时间
*/ */
long TIMEOUT_MILLIS = 5000; long TIMEOUT_MILLIS = 5000;
/** /**
* 重试次数 * 重试次数
*/ */
int RETRY_TIMES = 100; int RETRY_TIMES = 100;
/** /**
* 每次重试后等待的时间 * 每次重试后等待的时间
*/ */
long SLEEP_MILLIS = 100; long SLEEP_MILLIS = 100;
/** /**
* 获取锁 * 获取锁
* *
* @param key key * @param key key
* @return 成功/失败 * @return 成功/失败
*/ */
boolean lock(String key); boolean lock(String key);
/** /**
* 获取锁 * 获取锁
* *
* @param key key * @param key key
* @param retryTimes 重试次数 * @param retryTimes 重试次数
* @return 成功/失败 * @return 成功/失败
*/ */
boolean lock(String key, int retryTimes); boolean lock(String key, int retryTimes);
/** /**
* 获取锁 * 获取锁
* *
* @param key key * @param key key
* @param retryTimes 重试次数 * @param retryTimes 重试次数
* @param sleepMillis 获取锁失败的重试间隔 * @param sleepMillis 获取锁失败的重试间隔
* @return 成功/失败 * @return 成功/失败
*/ */
boolean lock(String key, int retryTimes, long sleepMillis); boolean lock(String key, int retryTimes, long sleepMillis);
/** /**
* 获取锁 * 获取锁
* *
* @param key key * @param key key
* @param expire 获取锁超时时间 * @param expire 获取锁超时时间
* @return 成功/失败 * @return 成功/失败
*/ */
boolean lock(String key, long expire); boolean lock(String key, long expire);
/** /**
* 获取锁 * 获取锁
* *
* @param key key * @param key key
* @param expire 获取锁超时时间 * @param expire 获取锁超时时间
* @param retryTimes 重试次数 * @param retryTimes 重试次数
* @return 成功/失败 * @return 成功/失败
*/ */
boolean lock(String key, long expire, int retryTimes); boolean lock(String key, long expire, int retryTimes);
/** /**
* 获取锁 * 获取锁
* *
* @param key key * @param key key
* @param expire 获取锁超时时间 * @param expire 获取锁超时时间
* @param retryTimes 重试次数 * @param retryTimes 重试次数
* @param sleepMillis 获取锁失败的重试间隔 * @param sleepMillis 获取锁失败的重试间隔
* @return 成功/失败 * @return 成功/失败
*/ */
boolean lock(String key, long expire, int retryTimes, long sleepMillis); boolean lock(String key, long expire, int retryTimes, long sleepMillis);
/** /**
* 释放锁 * 释放锁
* *
* @param key key值 * @param key key值
* @return 释放结果 * @return 释放结果
*/ */
boolean releaseLock(String key); boolean releaseLock(String key);
} }
package com.central.common.model; package com.central.common.model;
/** /**
* @Author: zlt * @Author: zlt
*/ */
public enum CodeEnum { public enum CodeEnum {
SUCCESS(0), SUCCESS(0),
ERROR(1); ERROR(1);
private Integer code; private Integer code;
CodeEnum(Integer code){ CodeEnum(Integer code){
this.code = code; this.code = code;
} }
public Integer getCode() { public Integer getCode() {
return code; return code;
} }
} }
package com.central.common.model; package com.central.common.model;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.social.security.SocialUserDetails; import org.springframework.social.security.SocialUserDetails;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
/** /**
* @author zlt * @author zlt
* 用户实体绑定spring security * 用户实体绑定spring security
*/ */
@Getter @Getter
@Setter @Setter
public class LoginAppUser extends SysUser implements SocialUserDetails { public class LoginAppUser extends SysUser implements SocialUserDetails {
private static final long serialVersionUID = -3685249101751401211L; private static final long serialVersionUID = -3685249101751401211L;
private Set<String> permissions; private Set<String> permissions;
/*** /***
* 权限重写 * 权限重写
*/ */
@JsonIgnore @JsonIgnore
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collection = new HashSet<>(); Collection<GrantedAuthority> collection = new HashSet<>();
if (!CollectionUtils.isEmpty(super.getRoles())) { if (!CollectionUtils.isEmpty(super.getRoles())) {
super.getRoles().parallelStream().forEach(role -> collection.add(new SimpleGrantedAuthority(role.getCode()))); super.getRoles().parallelStream().forEach(role -> collection.add(new SimpleGrantedAuthority(role.getCode())));
} }
return collection; return collection;
} }
@Override @Override
public boolean isAccountNonExpired() { public boolean isAccountNonExpired() {
return true; return true;
} }
@Override @Override
public boolean isAccountNonLocked() { public boolean isAccountNonLocked() {
return true; return true;
} }
@Override @Override
public boolean isCredentialsNonExpired() { public boolean isCredentialsNonExpired() {
return true; return true;
} }
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return getEnabled(); return getEnabled();
} }
@Override @Override
public String getUserId() { public String getUserId() {
return getOpenId(); return getOpenId();
} }
} }
package com.central.common.model; package com.central.common.model;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
/** /**
* 分页实体类 * 分页实体类
* *
* @author zlt * @author zlt
*/ */
@Data @Data
@Builder @Builder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class PageResult<T> implements Serializable { public class PageResult<T> implements Serializable {
private static final long serialVersionUID = -275582248840137389L; private static final long serialVersionUID = -275582248840137389L;
/** /**
* 总数 * 总数
*/ */
private Long count; private Long count;
/** /**
* 是否成功:0 成功、1 失败 * 是否成功:0 成功、1 失败
*/ */
private int code; private int code;
/** /**
* 当前页结果集 * 当前页结果集
*/ */
private List<T> data; private List<T> data;
} }
package com.central.common.model; package com.central.common.model;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serializable; import java.io.Serializable;
/** /**
* @Author: zlt * @Author: zlt
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class Result<T> implements Serializable { public class Result<T> implements Serializable {
private T datas; private T datas;
private Integer resp_code; private Integer resp_code;
private String resp_msg; private String resp_msg;
public static <T> Result<T> succeed(String msg) { public static <T> Result<T> succeed(String msg) {
return succeedWith(null, CodeEnum.SUCCESS.getCode(), msg); return succeedWith(null, CodeEnum.SUCCESS.getCode(), msg);
} }
public static <T> Result<T> succeed(T model, String msg) { public static <T> Result<T> succeed(T model, String msg) {
return succeedWith(model, CodeEnum.SUCCESS.getCode(), msg); return succeedWith(model, CodeEnum.SUCCESS.getCode(), msg);
} }
public static <T> Result<T> succeed(T model) { public static <T> Result<T> succeed(T model) {
return succeedWith(model, CodeEnum.SUCCESS.getCode(), ""); return succeedWith(model, CodeEnum.SUCCESS.getCode(), "");
} }
public static <T> Result<T> succeedWith(T datas, Integer code, String msg) { public static <T> Result<T> succeedWith(T datas, Integer code, String msg) {
return new Result<>(datas, code, msg); return new Result<>(datas, code, msg);
} }
public static <T> Result<T> failed(String msg) { public static <T> Result<T> failed(String msg) {
return failedWith(null, CodeEnum.ERROR.getCode(), msg); return failedWith(null, CodeEnum.ERROR.getCode(), msg);
} }
public static <T> Result<T> failed(T model, String msg) { public static <T> Result<T> failed(T model, String msg) {
return failedWith(model, CodeEnum.ERROR.getCode(), msg); return failedWith(model, CodeEnum.ERROR.getCode(), msg);
} }
public static <T> Result<T> failedWith(T datas, Integer code, String msg) { public static <T> Result<T> failedWith(T datas, Integer code, String msg) {
return new Result<>(datas, code, msg); return new Result<>(datas, code, msg);
} }
} }
package com.central.common.model; package com.central.common.model;
import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.activerecord.Model; import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** /**
* 实体父类 * 实体父类
* *
* @author zlt * @author zlt
*/ */
@Setter @Setter
@Getter @Getter
public class SuperEntity<T extends Model<?>> extends Model<T> { public class SuperEntity<T extends Model<?>> extends Model<T> {
/** /**
* 主键ID * 主键ID
*/ */
@TableId @TableId
private Long id; private Long id;
@TableField(fill = FieldFill.INSERT) @TableField(fill = FieldFill.INSERT)
private Date createTime; private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) @TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime; private Date updateTime;
@Override @Override
protected Serializable pkVal() { protected Serializable pkVal() {
return this.id; return this.id;
} }
} }
package com.central.common.model; package com.central.common.model;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* @author zlt * @author zlt
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@TableName("sys_menu") @TableName("sys_menu")
public class SysMenu extends SuperEntity { public class SysMenu extends SuperEntity {
private static final long serialVersionUID = 749360940290141180L; private static final long serialVersionUID = 749360940290141180L;
private Long parentId; private Long parentId;
private String name; private String name;
private String css; private String css;
private String url; private String url;
private String path; private String path;
private Integer sort; private Integer sort;
private Integer type; private Integer type;
private Boolean hidden; private Boolean hidden;
/** /**
* 请求的类型 * 请求的类型
*/ */
private String pathMethod; private String pathMethod;
@TableField(exist = false) @TableField(exist = false)
private List<SysMenu> subMenus; private List<SysMenu> subMenus;
@TableField(exist = false) @TableField(exist = false)
private Long roleId; private Long roleId;
@TableField(exist = false) @TableField(exist = false)
private Set<Long> menuIds; private Set<Long> menuIds;
} }
package com.central.common.model; package com.central.common.model;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* @author zlt * @author zlt
* 角色 * 角色
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@TableName("sys_role") @TableName("sys_role")
public class SysRole extends SuperEntity { public class SysRole extends SuperEntity {
private static final long serialVersionUID = 4497149010220586111L; private static final long serialVersionUID = 4497149010220586111L;
private String code; private String code;
private String name; private String name;
private Long userId; private Long userId;
} }
package com.central.common.model; package com.central.common.model;
import java.util.List; import java.util.List;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* @author zlt * @author zlt
* 用户实体 * 用户实体
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
@TableName("sys_user") @TableName("sys_user")
public class SysUser extends SuperEntity { public class SysUser extends SuperEntity {
private static final long serialVersionUID = -5886012896705137070L; private static final long serialVersionUID = -5886012896705137070L;
private String username; private String username;
private String password; private String password;
private String nickname; private String nickname;
private String headImgUrl; private String headImgUrl;
private String mobile; private String mobile;
private Integer sex; private Integer sex;
private Boolean enabled; private Boolean enabled;
private String type; private String type;
private String openId; private String openId;
@TableLogic @TableLogic
private boolean isDel; private boolean isDel;
@TableField(exist = false) @TableField(exist = false)
private List<SysRole> roles; private List<SysRole> roles;
@TableField(exist = false) @TableField(exist = false)
private String roleId; private String roleId;
@TableField(exist = false) @TableField(exist = false)
private String oldPassword; private String oldPassword;
@TableField(exist = false) @TableField(exist = false)
private String newPassword; private String newPassword;
} }
package com.central.common.model; package com.central.common.model;
/** /**
* @author zlt * @author zlt
* 用户类型 * 用户类型
*/ */
public enum UserType { public enum UserType {
/** /**
* 前端app用户 * 前端app用户
*/ */
APP, APP,
/** /**
* 后端管理用户 * 后端管理用户
*/ */
BACKEND BACKEND
} }
package com.central.common.resolver; package com.central.common.resolver;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.central.common.annotation.LoginUser; import com.central.common.annotation.LoginUser;
import com.central.common.constant.SecurityConstants; import com.central.common.constant.SecurityConstants;
import com.central.common.feign.UserService; import com.central.common.feign.UserService;
import com.central.common.model.SysRole; import com.central.common.model.SysRole;
import com.central.common.model.SysUser; import com.central.common.model.SysUser;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
* Token转化SysUser * Token转化SysUser
* *
* @author zlt * @author zlt
* @date 2018/12/21 * @date 2018/12/21
*/ */
@Slf4j @Slf4j
public class TokenArgumentResolver implements HandlerMethodArgumentResolver { public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
private UserService userService; private UserService userService;
public TokenArgumentResolver(UserService userService) { public TokenArgumentResolver(UserService userService) {
this.userService = userService; this.userService = userService;
} }
/** /**
* 入参筛选 * 入参筛选
* *
* @param methodParameter 参数集合 * @param methodParameter 参数集合
* @return 格式化后的参数 * @return 格式化后的参数
*/ */
@Override @Override
public boolean supportsParameter(MethodParameter methodParameter) { public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(LoginUser.class) && methodParameter.getParameterType().equals(SysUser.class); return methodParameter.hasParameterAnnotation(LoginUser.class) && methodParameter.getParameterType().equals(SysUser.class);
} }
/** /**
* @param methodParameter 入参集合 * @param methodParameter 入参集合
* @param modelAndViewContainer model 和 view * @param modelAndViewContainer model 和 view
* @param nativeWebRequest web相关 * @param nativeWebRequest web相关
* @param webDataBinderFactory 入参解析 * @param webDataBinderFactory 入参解析
* @return 包装对象 * @return 包装对象
*/ */
@Override @Override
public Object resolveArgument(MethodParameter methodParameter, public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) { WebDataBinderFactory webDataBinderFactory) {
LoginUser loginUser = methodParameter.getParameterAnnotation(LoginUser.class); LoginUser loginUser = methodParameter.getParameterAnnotation(LoginUser.class);
boolean isFull = loginUser.isFull(); boolean isFull = loginUser.isFull();
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class); HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
String userId = request.getHeader(SecurityConstants.USER_ID_HEADER); String userId = request.getHeader(SecurityConstants.USER_ID_HEADER);
String username = request.getHeader(SecurityConstants.USER_HEADER); String username = request.getHeader(SecurityConstants.USER_HEADER);
String roles = request.getHeader(SecurityConstants.ROLE_HEADER); String roles = request.getHeader(SecurityConstants.ROLE_HEADER);
if (StrUtil.isBlank(username)) { if (StrUtil.isBlank(username)) {
log.warn("resolveArgument error username is empty"); log.warn("resolveArgument error username is empty");
return null; return null;
} }
SysUser user; SysUser user;
if (isFull) { if (isFull) {
user = userService.selectByUsername(username); user = userService.selectByUsername(username);
} else { } else {
user = new SysUser(); user = new SysUser();
user.setId(Long.valueOf(userId)); user.setId(Long.valueOf(userId));
user.setUsername(username); user.setUsername(username);
} }
List<SysRole> sysRoleList = new ArrayList<>(); List<SysRole> sysRoleList = new ArrayList<>();
Arrays.stream(roles.split(",")).forEach(role -> { Arrays.stream(roles.split(",")).forEach(role -> {
SysRole sysRole = new SysRole(); SysRole sysRole = new SysRole();
sysRole.setCode(role); sysRole.setCode(role);
sysRoleList.add(sysRole); sysRoleList.add(sysRole);
}); });
user.setRoles(sysRoleList); user.setRoles(sysRoleList);
return user; return user;
} }
} }
package com.central.common.service; package com.central.common.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.central.common.lock.DistributedLock; import com.central.common.lock.DistributedLock;
/** /**
* service接口父类 * service接口父类
* *
* @author zlt * @author zlt
* @date 2019/1/10 * @date 2019/1/10
*/ */
public interface ISuperService<T> extends IService<T> { public interface ISuperService<T> extends IService<T> {
/** /**
* 幂等性新增记录 * 幂等性新增记录
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @param msg 对象已存在提示信息 * @param msg 对象已存在提示信息
* @return * @return
*/ */
boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg); boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg);
/** /**
* 幂等性新增记录 * 幂等性新增记录
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @return * @return
*/ */
boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper); boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper);
/** /**
* 幂等性新增或更新记录 * 幂等性新增或更新记录
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @param msg 对象已存在提示信息 * @param msg 对象已存在提示信息
* @return * @return
*/ */
boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg); boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg);
/** /**
* 幂等性新增或更新记录 * 幂等性新增或更新记录
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @return * @return
*/ */
boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper); boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper);
} }
package com.central.common.service.impl; package com.central.common.service.impl;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils; import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.central.common.exception.IdempotencyException; import com.central.common.exception.IdempotencyException;
import com.central.common.exception.LockException; import com.central.common.exception.LockException;
import com.central.common.lock.DistributedLock; import com.central.common.lock.DistributedLock;
import com.central.common.service.ISuperService; import com.central.common.service.ISuperService;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
/** /**
* service实现父类 * service实现父类
* *
* @author zlt * @author zlt
* @date 2019/1/10 * @date 2019/1/10
*/ */
public class SuperServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements ISuperService<T> { public class SuperServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements ISuperService<T> {
/** /**
* 幂等性新增记录 * 幂等性新增记录
* 例子如下: * 例子如下:
* String username = sysUser.getUsername(); * String username = sysUser.getUsername();
* boolean result = super.saveIdempotency(sysUser, lock * boolean result = super.saveIdempotency(sysUser, lock
* , LOCK_KEY_USERNAME+username * , LOCK_KEY_USERNAME+username
* , new QueryWrapper<SysUser>().eq("username", username)); * , new QueryWrapper<SysUser>().eq("username", username));
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @param msg 对象已存在提示信息 * @param msg 对象已存在提示信息
* @return * @return
*/ */
@Override @Override
public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg) { public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg) {
if (lock == null) { if (lock == null) {
throw new LockException("DistributedLock is null"); throw new LockException("DistributedLock is null");
} }
if (StrUtil.isEmpty(lockKey)) { if (StrUtil.isEmpty(lockKey)) {
throw new LockException("lockKey is null"); throw new LockException("lockKey is null");
} }
try { try {
//加锁 //加锁
boolean isLock = lock.lock(lockKey); boolean isLock = lock.lock(lockKey);
if (isLock) { if (isLock) {
//判断记录是否已存在 //判断记录是否已存在
int count = super.count(countWrapper); int count = super.count(countWrapper);
if (count == 0) { if (count == 0) {
return super.save(entity); return super.save(entity);
} else { } else {
if (StrUtil.isEmpty(msg)) { if (StrUtil.isEmpty(msg)) {
msg = "已存在"; msg = "已存在";
} }
throw new IdempotencyException(msg); throw new IdempotencyException(msg);
} }
} else { } else {
throw new LockException("锁等待超时"); throw new LockException("锁等待超时");
} }
} finally { } finally {
lock.releaseLock(lockKey); lock.releaseLock(lockKey);
} }
} }
/** /**
* 幂等性新增记录 * 幂等性新增记录
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @return * @return
*/ */
@Override @Override
public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper) { public boolean saveIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper) {
return saveIdempotency(entity, lock, lockKey, countWrapper, null); return saveIdempotency(entity, lock, lockKey, countWrapper, null);
} }
/** /**
* 幂等性新增或更新记录 * 幂等性新增或更新记录
* 例子如下: * 例子如下:
* String username = sysUser.getUsername(); * String username = sysUser.getUsername();
* boolean result = super.saveOrUpdateIdempotency(sysUser, lock * boolean result = super.saveOrUpdateIdempotency(sysUser, lock
* , LOCK_KEY_USERNAME+username * , LOCK_KEY_USERNAME+username
* , new QueryWrapper<SysUser>().eq("username", username)); * , new QueryWrapper<SysUser>().eq("username", username));
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @param msg 对象已存在提示信息 * @param msg 对象已存在提示信息
* @return * @return
*/ */
@Override @Override
public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg) { public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper, String msg) {
if (null != entity) { if (null != entity) {
Class<?> cls = entity.getClass(); Class<?> cls = entity.getClass();
TableInfo tableInfo = TableInfoHelper.getTableInfo(cls); TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) { if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty()); Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) { if (StringUtils.checkValNull(idVal) || Objects.isNull(getById((Serializable) idVal))) {
if (StrUtil.isEmpty(msg)) { if (StrUtil.isEmpty(msg)) {
msg = "已存在"; msg = "已存在";
} }
return this.saveIdempotency(entity, lock, lockKey, countWrapper, msg); return this.saveIdempotency(entity, lock, lockKey, countWrapper, msg);
} else { } else {
return updateById(entity); return updateById(entity);
} }
} else { } else {
throw ExceptionUtils.mpe("Error: Can not execute. Could not find @TableId."); throw ExceptionUtils.mpe("Error: Can not execute. Could not find @TableId.");
} }
} }
return false; return false;
} }
/** /**
* 幂等性新增或更新记录 * 幂等性新增或更新记录
* 例子如下: * 例子如下:
* String username = sysUser.getUsername(); * String username = sysUser.getUsername();
* boolean result = super.saveOrUpdateIdempotency(sysUser, lock * boolean result = super.saveOrUpdateIdempotency(sysUser, lock
* , LOCK_KEY_USERNAME+username * , LOCK_KEY_USERNAME+username
* , new QueryWrapper<SysUser>().eq("username", username)); * , new QueryWrapper<SysUser>().eq("username", username));
* *
* @param entity 实体对象 * @param entity 实体对象
* @param lock 锁实例 * @param lock 锁实例
* @param lockKey 锁的key * @param lockKey 锁的key
* @param countWrapper 判断是否存在的条件 * @param countWrapper 判断是否存在的条件
* @return * @return
*/ */
@Override @Override
public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper) { public boolean saveOrUpdateIdempotency(T entity, DistributedLock lock, String lockKey, Wrapper<T> countWrapper) {
return this.saveOrUpdateIdempotency(entity, lock, lockKey, countWrapper, null); return this.saveOrUpdateIdempotency(entity, lock, lockKey, countWrapper, null);
} }
} }
package com.central.common.utils; package com.central.common.utils;
import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil; import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams; import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Excel工具类 * Excel工具类
* *
* @author zlt * @author zlt
* @date 2019/1/6 * @date 2019/1/6
*/ */
public class ExcelUtil { public class ExcelUtil {
private ExcelUtil() { private ExcelUtil() {
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }
/** /**
* 导出 * 导出
* *
* @param list 数据列表 * @param list 数据列表
* @param title 标题 * @param title 标题
* @param sheetName sheet名称 * @param sheetName sheet名称
* @param pojoClass 元素类型 * @param pojoClass 元素类型
* @param fileName 文件名 * @param fileName 文件名
* @param isCreateHeader 是否创建列头 * @param isCreateHeader 是否创建列头
* @param response * @param response
* @throws IOException * @throws IOException
*/ */
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName
, boolean isCreateHeader, HttpServletResponse response) throws IOException { , boolean isCreateHeader, HttpServletResponse response) throws IOException {
ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF); ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
exportParams.setCreateHeadRows(isCreateHeader); exportParams.setCreateHeadRows(isCreateHeader);
defaultExport(list, pojoClass, fileName, response, exportParams); defaultExport(list, pojoClass, fileName, response, exportParams);
} }
/** /**
* 导出 * 导出
* *
* @param list 数据列表 * @param list 数据列表
* @param title 标题 * @param title 标题
* @param sheetName sheet名称 * @param sheetName sheet名称
* @param pojoClass 元素类型 * @param pojoClass 元素类型
* @param fileName 文件名 * @param fileName 文件名
* @param response * @param response
* @throws IOException * @throws IOException
*/ */
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName
, HttpServletResponse response) throws IOException { , HttpServletResponse response) throws IOException {
defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF)); defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));
} }
/** /**
* 导出 * 导出
* *
* @param list 数据列表(元素是Map) * @param list 数据列表(元素是Map)
* @param fileName 文件名 * @param fileName 文件名
* @param response * @param response
* @throws IOException * @throws IOException
*/ */
public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException { public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
defaultExport(list, fileName, response); defaultExport(list, fileName, response);
} }
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName
, HttpServletResponse response, ExportParams exportParams) throws IOException { , HttpServletResponse response, ExportParams exportParams) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list); Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
if (workbook != null) { if (workbook != null) {
downLoadExcel(fileName, response, workbook); downLoadExcel(fileName, response, workbook);
} }
} }
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException { private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.XSSF); Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.XSSF);
if (workbook != null) { if (workbook != null) {
downLoadExcel(fileName, response, workbook); downLoadExcel(fileName, response, workbook);
} }
} }
private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException { private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
response.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
workbook.write(response.getOutputStream()); workbook.write(response.getOutputStream());
} }
public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) { public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) {
if (StringUtils.isBlank(filePath)) { if (StringUtils.isBlank(filePath)) {
return Collections.emptyList(); return Collections.emptyList();
} }
ImportParams params = new ImportParams(); ImportParams params = new ImportParams();
params.setTitleRows(titleRows); params.setTitleRows(titleRows);
params.setHeadRows(headerRows); params.setHeadRows(headerRows);
return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params); return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
} }
public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws Exception { public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws Exception {
if (file == null) { if (file == null) {
return Collections.emptyList(); return Collections.emptyList();
} }
ImportParams params = new ImportParams(); ImportParams params = new ImportParams();
params.setTitleRows(titleRows); params.setTitleRows(titleRows);
params.setHeadRows(headerRows); params.setHeadRows(headerRows);
return ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params); return ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params);
} }
} }
package com.central.common.utils; package com.central.common.utils;
import com.central.common.model.Result; import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper; import com.central.common.model.Result;
import org.springframework.http.MediaType; import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.io.buffer.DataBuffer;
import javax.servlet.http.HttpServletResponse; import org.springframework.core.io.buffer.DataBufferFactory;
import java.io.IOException; import org.springframework.core.io.buffer.DataBufferUtils;
import java.io.Writer; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
/** import org.springframework.http.server.reactive.ServerHttpResponse;
* @author zlt import org.springframework.web.server.ServerWebExchange;
* @date 2018/12/20 import reactor.core.publisher.Mono;
*/
public class ResponseUtil { import javax.servlet.http.HttpServletResponse;
private ResponseUtil() { import java.io.IOException;
throw new IllegalStateException("Utility class"); import java.io.Writer;
} import java.nio.charset.Charset;
/**
* 通过流写到前端 /**
* * @author zlt
* @param objectMapper 对象序列化 * @date 2018/12/20
* @param response */
* @param msg 返回信息 public class ResponseUtil {
* @param httpStatus 返回状态码 private ResponseUtil() {
* @throws IOException throw new IllegalStateException("Utility class");
*/ }
public static void responseWriter(ObjectMapper objectMapper, HttpServletResponse response, String msg, int httpStatus) throws IOException { /**
Result result = Result.succeedWith(null, httpStatus, msg); * 通过流写到前端
responseWrite(objectMapper, response, result); *
} * @param objectMapper 对象序列化
* @param response
/** * @param msg 返回信息
* 通过流写到前端 * @param httpStatus 返回状态码
* @param objectMapper 对象序列化 * @throws IOException
* @param response */
* @param obj public static void responseWriter(ObjectMapper objectMapper, HttpServletResponse response, String msg, int httpStatus) throws IOException {
*/ Result result = Result.succeedWith(null, httpStatus, msg);
public static void responseSucceed(ObjectMapper objectMapper, HttpServletResponse response, Object obj) throws IOException { responseWrite(objectMapper, response, result);
Result result = Result.succeed(obj); }
responseWrite(objectMapper, response, result);
} /**
* 通过流写到前端
/** * @param objectMapper 对象序列化
* 通过流写到前端 * @param response
* @param objectMapper * @param obj
* @param response */
* @param msg public static void responseSucceed(ObjectMapper objectMapper, HttpServletResponse response, Object obj) throws IOException {
* @throws IOException Result result = Result.succeed(obj);
*/ responseWrite(objectMapper, response, result);
public static void responseFailed(ObjectMapper objectMapper, HttpServletResponse response, String msg) throws IOException { }
Result result = Result.failed(msg);
responseWrite(objectMapper, response, result); /**
} * 通过流写到前端
* @param objectMapper
private static void responseWrite(ObjectMapper objectMapper, HttpServletResponse response, Result result) throws IOException { * @param response
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); * @param msg
try ( * @throws IOException
Writer writer = response.getWriter() */
) { public static void responseFailed(ObjectMapper objectMapper, HttpServletResponse response, String msg) throws IOException {
writer.write(objectMapper.writeValueAsString(result)); Result result = Result.failed(msg);
writer.flush(); responseWrite(objectMapper, response, result);
} }
}
} private static void responseWrite(ObjectMapper objectMapper, HttpServletResponse response, Result result) throws IOException {
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
try (
Writer writer = response.getWriter()
) {
writer.write(objectMapper.writeValueAsString(result));
writer.flush();
}
}
/**
* webflux的response返回json对象
*/
public static Mono<Void> responseWriter(ServerWebExchange exchange, int httpStatus, String msg) {
Result result = Result.succeedWith(null, httpStatus, msg);
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.valueOf(result.getResp_code()));
response.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);
DataBufferFactory dataBufferFactory = response.bufferFactory();
DataBuffer buffer = dataBufferFactory.wrap(JSONObject.toJSONString(result).getBytes(Charset.defaultCharset()));
return response.writeWith(Mono.just(buffer)).doOnError((error) -> {
DataBufferUtils.release(buffer);
});
}
}
package com.central.common.utils; package com.central.common.utils;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* spring获取bean工具类 * spring获取bean工具类
* *
* @author 作者 owen E-mail: 624191343@qq.com * @author 作者 owen E-mail: 624191343@qq.com
*/ */
@Component @Component
public class SpringUtil implements ApplicationContextAware { public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null; private static ApplicationContext applicationContext = null;
public static <T> T getBean(Class<T> cla) { public static <T> T getBean(Class<T> cla) {
return applicationContext.getBean(cla); return applicationContext.getBean(cla);
} }
public static <T> T getBean(String name, Class<T> cal) { public static <T> T getBean(String name, Class<T> cal) {
return applicationContext.getBean(name, cal); return applicationContext.getBean(name, cal);
} }
public static String getProperty(String key) { public static String getProperty(String key) {
return applicationContext.getBean(Environment.class).getProperty(key); return applicationContext.getBean(Environment.class).getProperty(key);
} }
@Override @Override
public void setApplicationContext(ApplicationContext applicationContext) { public void setApplicationContext(ApplicationContext applicationContext) {
SpringUtil.applicationContext = applicationContext; SpringUtil.applicationContext = applicationContext;
} }
} }
org.springframework.context.ApplicationContextInitializer=\
com.central.common.config.BannerInitializer
\ No newline at end of file
...@@ -12,81 +12,13 @@ ...@@ -12,81 +12,13 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>zlt-log-spring-boot-starter</artifactId> <artifactId>zlt-common-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<!-- easypoi -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>javax.servlet</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>javax.servlet-api</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>com.nepxion</groupId>
<artifactId>banner</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
...@@ -4,8 +4,10 @@ import cn.hutool.core.util.StrUtil; ...@@ -4,8 +4,10 @@ import cn.hutool.core.util.StrUtil;
import com.central.common.constant.CommonConstant; import com.central.common.constant.CommonConstant;
import com.central.common.constant.SecurityConstants; import com.central.common.constant.SecurityConstants;
import com.central.common.context.TenantContextHolder; import com.central.common.context.TenantContextHolder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -18,6 +20,7 @@ import java.io.IOException; ...@@ -18,6 +20,7 @@ import java.io.IOException;
* @author zlt * @author zlt
* @date 2019/9/15 * @date 2019/9/15
*/ */
@ConditionalOnClass(Filter.class)
public class TenantFilter extends OncePerRequestFilter { public class TenantFilter extends OncePerRequestFilter {
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
......
...@@ -4,9 +4,11 @@ import cn.hutool.core.util.StrUtil; ...@@ -4,9 +4,11 @@ import cn.hutool.core.util.StrUtil;
import com.central.common.constant.CommonConstant; import com.central.common.constant.CommonConstant;
import com.central.log.properties.TraceProperties; import com.central.log.properties.TraceProperties;
import org.slf4j.MDC; import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -19,6 +21,7 @@ import java.io.IOException; ...@@ -19,6 +21,7 @@ import java.io.IOException;
* @author zlt * @author zlt
* @date 2019/9/15 * @date 2019/9/15
*/ */
@ConditionalOnClass(Filter.class)
public class TraceFilter extends OncePerRequestFilter { public class TraceFilter extends OncePerRequestFilter {
@Resource @Resource
private TraceProperties traceProperties; private TraceProperties traceProperties;
......
org.springframework.context.ApplicationContextInitializer=\
com.central.common.config.BannerInitializer
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.central.common.filter.TenantFilter,\ com.central.common.filter.TenantFilter,\
com.central.common.filter.TraceFilter com.central.common.filter.TraceFilter
\ No newline at end of file
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>zlt-common-spring-boot-starter</artifactId> <artifactId>zlt-common-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>zlt-common-spring-boot-starter</artifactId> <artifactId>zlt-common-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>zlt-common-spring-boot-starter</artifactId> <artifactId>zlt-common-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
......
package com.central.common.ribbon;
import com.central.common.constant.ConfigConstants;
import com.central.common.ribbon.config.RuleConfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
/**
* Ribbon扩展配置类
*
* @author zlt
* @date 2018/11/17 9:24
*/
@ConditionalOnProperty(value = ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED, havingValue = "true")
@RibbonClients(defaultConfiguration = {RuleConfigure.class})
public class LbIsolationAutoConfigure {
}
package com.central.common.ribbon; package com.central.common.ribbon;
import com.central.common.constant.ConfigConstants;
import com.central.common.ribbon.config.RestTemplateProperties; import com.central.common.ribbon.config.RestTemplateProperties;
import com.central.common.ribbon.config.RuleConfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.ribbon.DefaultPropertiesFactory; import org.springframework.cloud.netflix.ribbon.DefaultPropertiesFactory;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** /**
* Ribbon扩展配置类 * Ribbon扩展配置类
...@@ -22,10 +17,4 @@ public class RibbonAutoConfigure { ...@@ -22,10 +17,4 @@ public class RibbonAutoConfigure {
public DefaultPropertiesFactory defaultPropertiesFactory() { public DefaultPropertiesFactory defaultPropertiesFactory() {
return new DefaultPropertiesFactory(); return new DefaultPropertiesFactory();
} }
@Configuration
@ConditionalOnProperty(value = ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED, havingValue = "true")
@RibbonClients(defaultConfiguration = {RuleConfigure.class})
public class LbIsolationConfig {
}
} }
...@@ -87,7 +87,7 @@ public class FeignInterceptorConfig { ...@@ -87,7 +87,7 @@ public class FeignInterceptorConfig {
Enumeration<String> headers = request.getHeaders(CommonConstant.TOKEN_HEADER); Enumeration<String> headers = request.getHeaders(CommonConstant.TOKEN_HEADER);
while (headers.hasMoreElements()) { while (headers.hasMoreElements()) {
String value = headers.nextElement(); String value = headers.nextElement();
if ((value.toLowerCase().startsWith(CommonConstant.BEARER_TYPE.toLowerCase()))) { if ((value.toLowerCase().startsWith(CommonConstant.BEARER_TYPE))) {
String authHeaderValue = value.substring(CommonConstant.BEARER_TYPE.length()).trim(); String authHeaderValue = value.substring(CommonConstant.BEARER_TYPE.length()).trim();
int commaIndex = authHeaderValue.indexOf(','); int commaIndex = authHeaderValue.indexOf(',');
if (commaIndex > 0) { if (commaIndex > 0) {
......
...@@ -5,8 +5,12 @@ import com.central.common.constant.CommonConstant; ...@@ -5,8 +5,12 @@ import com.central.common.constant.CommonConstant;
import com.central.common.constant.ConfigConstants; import com.central.common.constant.ConfigConstants;
import com.central.common.context.LbIsolationContextHolder; import com.central.common.context.LbIsolationContextHolder;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -19,6 +23,7 @@ import java.io.IOException; ...@@ -19,6 +23,7 @@ import java.io.IOException;
* @author zlt * @author zlt
* @date 2019/9/15 * @date 2019/9/15
*/ */
@ConditionalOnClass(Filter.class)
public class LbIsolationFilter extends OncePerRequestFilter { public class LbIsolationFilter extends OncePerRequestFilter {
@Value("${" + ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED + ":false}") @Value("${" + ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED + ":false}")
private boolean enableIsolation; private boolean enableIsolation;
......
...@@ -18,6 +18,7 @@ import java.util.stream.Collectors; ...@@ -18,6 +18,7 @@ import java.util.stream.Collectors;
* @date 2019/9/3 * @date 2019/9/3
*/ */
public class CustomIsolationRule extends RoundRobinRule { public class CustomIsolationRule extends RoundRobinRule {
private final static String KEY_DEFAULT = "default";
/** /**
* 优先根据版本号取实例 * 优先根据版本号取实例
*/ */
...@@ -26,7 +27,13 @@ public class CustomIsolationRule extends RoundRobinRule { ...@@ -26,7 +27,13 @@ public class CustomIsolationRule extends RoundRobinRule {
if (lb == null) { if (lb == null) {
return null; return null;
} }
String version = LbIsolationContextHolder.getVersion(); String version;
if (key != null && !KEY_DEFAULT.equals(key)) {
version = key.toString();
} else {
version = LbIsolationContextHolder.getVersion();
}
List<Server> targetList = null; List<Server> targetList = null;
List<Server> upList = lb.getReachableServers(); List<Server> upList = lb.getReachableServers();
if (StrUtil.isNotEmpty(version)) { if (StrUtil.isNotEmpty(version)) {
......
...@@ -2,4 +2,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ...@@ -2,4 +2,5 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.central.common.ribbon.RibbonAutoConfigure,\ com.central.common.ribbon.RibbonAutoConfigure,\
com.central.common.ribbon.FeignAutoConfigure,\ com.central.common.ribbon.FeignAutoConfigure,\
com.central.common.ribbon.RestTemplateAutoConfigure,\ com.central.common.ribbon.RestTemplateAutoConfigure,\
com.central.common.ribbon.filter.LbIsolationFilter com.central.common.ribbon.filter.LbIsolationFilter,\
com.central.common.ribbon.LbIsolationAutoConfigure
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.zlt</groupId> <groupId>com.zlt</groupId>
<artifactId>zlt-common-spring-boot-starter</artifactId> <artifactId>zlt-common-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>com.alibaba.cloud</groupId>
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>io.springfox</groupId>
......
...@@ -11,7 +11,7 @@ spring.redis.timeout=5000 ...@@ -11,7 +11,7 @@ spring.redis.timeout=5000
##### elasticsearch配置 ##### elasticsearch配置
zlt.elasticsearch.cluster-name=my-es zlt.elasticsearch.cluster-name=my-es
zlt.elasticsearch.cluster-nodes=119.3.105.180 zlt.elasticsearch.cluster-nodes=192.168.28.130
##### sentinel配置 ##### sentinel配置
zlt.sentinel.dashboard=192.168.28.130:6999 zlt.sentinel.dashboard=192.168.28.130:6999
......
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
<artifactId>zlt-gateway</artifactId> <artifactId>zlt-gateway</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<modules> <modules>
<!-- zuul -->
<module>zuul-gateway</module> <module>zuul-gateway</module>
<!-- spring cloud gateway -->
<module>sc-gateway</module>
</modules> </modules>
</project> </project>
\ No newline at end of file
<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>
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-gateway</artifactId>
<version>2.7.2</version>
</parent>
<artifactId>sc-gateway</artifactId>
<description>spring cloud gateway网关</description>
<dependencies>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-config</artifactId>
</dependency>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-ribbon-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-sentinel-spring-boot-starter</artifactId>
</dependency>
<!--<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>-->
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-auth-client-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<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-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-redis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<imageTags>
<imageTag>${project.version}</imageTag>
<imageTag>latest</imageTag>
</imageTags>
<forceTags>true</forceTags>
<baseImage>${docker.baseImage}</baseImage>
<volumes>${docker.volumes}</volumes>
<env>
<JAVA_OPTS>${docker.java.opts}</JAVA_OPTS>
</env>
<entryPoint>["sh","-c","java $JAVA_OPTS ${docker.java.security.egd} -jar /${project.build.finalName}.jar"]</entryPoint>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>
\ No newline at end of file
package com.central;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author zlt
* @date 2019/10/5
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class SCGatewayApp {
public static void main(String[] args) {
SpringApplication.run(SCGatewayApp.class, args);
}
}
\ No newline at end of file
package com.central.gateway.auth;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import reactor.core.publisher.Mono;
/**
* @author zlt
* @date 2019/10/6
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
public class CustomAuthenticationManager implements ReactiveAuthenticationManager {
private TokenStore tokenStore;
public CustomAuthenticationManager(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
return Mono.justOrEmpty(authentication)
.filter(a -> a instanceof BearerTokenAuthenticationToken)
.cast(BearerTokenAuthenticationToken.class)
.map(BearerTokenAuthenticationToken::getToken)
.flatMap((accessTokenValue -> {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
if (accessToken == null) {
return Mono.error(new InvalidTokenException("Invalid access token: " + accessTokenValue));
} else if (accessToken.isExpired()) {
tokenStore.removeAccessToken(accessToken);
return Mono.error(new InvalidTokenException("Access token expired: " + accessTokenValue));
}
OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
if (result == null) {
return Mono.error(new InvalidTokenException("Invalid access token: " + accessTokenValue));
}
return Mono.just(result);
}))
.cast(Authentication.class);
}
}
package com.central.gateway.auth;
import com.central.common.utils.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 403拒绝访问异常处理,转换为JSON
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Slf4j
public class JsonAccessDeniedHandler implements ServerAccessDeniedHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException e) {
return ResponseUtil.responseWriter(exchange, HttpStatus.FORBIDDEN.value(), e.getMessage());
}
}
package com.central.gateway.auth;
import com.central.common.utils.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 401未授权异常处理,转换为JSON
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Slf4j
public class JsonAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
@Override
public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) {
return ResponseUtil.responseWriter(exchange, HttpStatus.UNAUTHORIZED.value(), e.getMessage());
}
}
package com.central.gateway.auth;
import cn.hutool.core.collection.CollectionUtil;
import com.central.common.constant.SecurityConstants;
import com.central.common.model.SysUser;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 认证成功处理类
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
public class Oauth2AuthSuccessHandler implements ServerAuthenticationSuccessHandler {
@Override
public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
SysUser user = (SysUser)authentication.getPrincipal();
Long userId = user.getId();
String username = user.getUsername();
OAuth2Authentication oauth2Authentication = (OAuth2Authentication)authentication;
String clientId = oauth2Authentication.getOAuth2Request().getClientId();
ServerWebExchange exchange = webFilterExchange.getExchange();
ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate()
.headers(h -> {
h.add(SecurityConstants.USER_ID_HEADER, String.valueOf(userId));
h.add(SecurityConstants.USER_HEADER, username);
h.add(SecurityConstants.TENANT_HEADER, clientId);
h.add(SecurityConstants.ROLE_HEADER, CollectionUtil.join(authentication.getAuthorities(), ","));
})
.build();
ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build();
return webFilterExchange.getChain().filter(build);
}
}
package com.central.gateway.auth;
import com.central.common.model.SysMenu;
import com.central.gateway.feign.MenuService;
import com.central.oauth2.common.service.impl.DefaultPermissionServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.List;
/**
* url权限认证
*
* @author zlt
* @date 2019/10/6
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Slf4j
@Component
public class PermissionAuthManager extends DefaultPermissionServiceImpl implements ReactiveAuthorizationManager<AuthorizationContext> {
@Resource
private MenuService menuService;
@Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
return authentication.map(auth -> {
ServerWebExchange exchange = authorizationContext.getExchange();
ServerHttpRequest request = exchange.getRequest();
boolean isPermission = super.hasPermission(auth, request.getMethodValue(), request.getURI().getPath());
return new AuthorizationDecision(isPermission);
}).defaultIfEmpty(new AuthorizationDecision(false));
}
@Override
public List<SysMenu> findMenuByRoleCodes(String roleCodes) {
return menuService.findByRoleCodes(roleCodes);
}
}
package com.central.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* 跨域配置
*
* @author zlt
* @date 2019/10/5
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Configuration
public class CorsConfig {
private static final String ALL = "*";
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// cookie跨域
config.setAllowCredentials(Boolean.TRUE);
config.addAllowedMethod(ALL);
config.addAllowedOrigin(ALL);
config.addAllowedHeader(ALL);
// 配置前端js允许访问的自定义响应头
config.addExposedHeader("setToken");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
package com.central.gateway.config;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.central.gateway.route.NacosRouteDefinitionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 动态路由配置
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Configuration
@ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "enabled", havingValue = "true")
public class DynamicRouteConfig {
@Autowired
private ApplicationEventPublisher publisher;
/**
* Nacos实现方式
*/
@Configuration
@ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true)
public class NacosDynRoute {
@Autowired
private NacosConfigProperties nacosConfigProperties;
@Bean
public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() {
return new NacosRouteDefinitionRepository(publisher, nacosConfigProperties);
}
}
}
package com.central.gateway.config;
import com.central.gateway.auth.*;
import com.central.oauth2.common.properties.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.ServerAuthenticationEntryPointFailureHandler;
/**
* 资源服务器配置
*
* @author zlt
* @date 2019/10/5
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Configuration
public class ResourceServerConfiguration {
@Autowired
private SecurityProperties securityProperties;
@Autowired
private TokenStore tokenStore;
@Autowired
private PermissionAuthManager permissionAuthManager;
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
//认证处理器
ReactiveAuthenticationManager customAuthenticationManager = new CustomAuthenticationManager(tokenStore);
JsonAuthenticationEntryPoint entryPoint = new JsonAuthenticationEntryPoint();
//token转换器
ServerBearerTokenAuthenticationConverter tokenAuthenticationConverter = new ServerBearerTokenAuthenticationConverter();
tokenAuthenticationConverter.setAllowUriQueryParameter(true);
//oauth2认证过滤器
AuthenticationWebFilter oauth2Filter = new AuthenticationWebFilter(customAuthenticationManager);
oauth2Filter.setServerAuthenticationConverter(tokenAuthenticationConverter);
oauth2Filter.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(entryPoint));
oauth2Filter.setAuthenticationSuccessHandler(new Oauth2AuthSuccessHandler());
http.addFilterAt(oauth2Filter, SecurityWebFiltersOrder.AUTHENTICATION);
ServerHttpSecurity.AuthorizeExchangeSpec authorizeExchange = http.authorizeExchange();
if (securityProperties.getAuth().getHttpUrls().length > 0) {
authorizeExchange.pathMatchers(securityProperties.getAuth().getHttpUrls()).authenticated();
}
if (securityProperties.getIgnore().getUrls().length > 0) {
authorizeExchange.pathMatchers(securityProperties.getIgnore().getUrls()).permitAll();
}
authorizeExchange
.pathMatchers(HttpMethod.OPTIONS).permitAll()
.anyExchange()
.access(permissionAuthManager)
.and()
.exceptionHandling()
.accessDeniedHandler(new JsonAccessDeniedHandler())
.authenticationEntryPoint(entryPoint)
.and()
.headers()
.frameOptions()
.disable()
.and()
.httpBasic().disable()
.csrf().disable();
return http.build();
}
}
package com.central.gateway.feign;
import com.central.common.constant.ServiceNameConstants;
import com.central.common.model.SysMenu;
import com.central.gateway.feign.fallback.MenuServiceFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
/**
* @author zlt
*/
@FeignClient(name = ServiceNameConstants.USER_SERVICE, fallbackFactory = MenuServiceFallbackFactory.class, decode404 = true)
public interface MenuService {
/**
* 角色菜单列表
* @param roleCodes
*/
@GetMapping(value = "/menus/{roleCodes}")
List<SysMenu> findByRoleCodes(@PathVariable("roleCodes") String roleCodes);
}
package com.central.gateway.feign.fallback;
import cn.hutool.core.collection.CollectionUtil;
import com.central.gateway.feign.MenuService;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* menuService降级工场
*
* @author zlt
* @date 2019/1/18
*/
@Slf4j
@Component
public class MenuServiceFallbackFactory implements FallbackFactory<MenuService> {
@Override
public MenuService create(Throwable throwable) {
return roleIds -> {
log.error("调用findByRoleCodes异常:{}", roleIds, throwable);
return CollectionUtil.newArrayList();
};
}
}
package com.central.gateway.filter;
import cn.hutool.core.util.StrUtil;
import com.central.common.constant.CommonConstant;
import com.central.common.constant.ConfigConstants;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.net.URI;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
/**
* 传递负载均衡隔离值
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Component
@ConditionalOnProperty(name = ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED, havingValue = "true")
public class LbIsolationFilter extends LoadBalancerClientFilter {
public LbIsolationFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
super(loadBalancer, properties);
}
@Override
protected ServiceInstance choose(ServerWebExchange exchange) {
String vresion = exchange.getRequest().getHeaders().getFirst(CommonConstant.Z_L_T_VERSION);
if (StrUtil.isNotEmpty(vresion)) {
if (this.loadBalancer instanceof RibbonLoadBalancerClient) {
RibbonLoadBalancerClient client = (RibbonLoadBalancerClient) this.loadBalancer;
String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
return client.choose(serviceId, vresion);
}
}
return super.choose(exchange);
}
}
package com.central.gateway.filter;
import eu.bitwalker.useragentutils.UserAgent;
import com.central.gateway.utils.ReactiveAddrUtil;
import com.central.log.monitor.PointUtil;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Map;
/**
* 请求统计分析埋点过滤器
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Component
public class RequestStatisticsFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
Map<String, String> headers = request.getHeaders().toSingleValueMap();
UserAgent userAgent = UserAgent.parseUserAgentString(headers.get("User-Agent"));
//埋点
PointUtil.debug("1", "request-statistics",
"ip=" + ReactiveAddrUtil.getRemoteAddr(request)
+ "&browser=" + userAgent.getBrowser()
+ "&operatingSystem=" + userAgent.getOperatingSystem());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
package com.central.gateway.filter;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.central.common.constant.CommonConstant;
import com.central.common.constant.SecurityConstants;
import com.central.log.properties.TraceProperties;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 生成日志链路追踪id,并传入header中
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Component
public class TraceFilter implements GlobalFilter, Ordered {
@Autowired
private TraceProperties traceProperties;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (traceProperties.getEnable()) {
//链路追踪id
String traceId = IdUtil.fastSimpleUUID();
MDC.put(CommonConstant.LOG_TRACE_ID, traceId);
ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate()
.headers(h -> h.add(CommonConstant.TRACE_ID_HEADER, traceId))
.build();
ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build();
return chain.filter(build);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
package com.central.gateway.route;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.context.ApplicationEventPublisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* nacos路由数据源
*
* @author zlt
* @date 2019/10/7
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Slf4j
public class NacosRouteDefinitionRepository implements RouteDefinitionRepository {
private static final String SCG_DATA_ID = "scg-routes";
private static final String SCG_GROUP_ID = "SCG_GATEWAY";
private ApplicationEventPublisher publisher;
private NacosConfigProperties nacosConfigProperties;
public NacosRouteDefinitionRepository(ApplicationEventPublisher publisher, NacosConfigProperties nacosConfigProperties) {
this.publisher = publisher;
this.nacosConfigProperties = nacosConfigProperties;
addListener();
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
try {
String content = nacosConfigProperties.configServiceInstance().getConfig(SCG_DATA_ID, SCG_GROUP_ID,5000);
List<RouteDefinition> routeDefinitions = getListByStr(content);
return Flux.fromIterable(routeDefinitions);
} catch (NacosException e) {
log.error("getRouteDefinitions by nacos error", e);
}
return Flux.fromIterable(CollUtil.newArrayList());
}
/**
* 添加Nacos监听
*/
private void addListener() {
try {
nacosConfigProperties.configServiceInstance().addListener(SCG_DATA_ID, SCG_GROUP_ID, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
publisher.publishEvent(new RefreshRoutesEvent(this));
}
});
} catch (NacosException e) {
log.error("nacos-addListener-error", e);
}
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return null;
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return null;
}
private List<RouteDefinition> getListByStr(String content) {
if (StrUtil.isNotEmpty(content)) {
return JSONObject.parseArray(content, RouteDefinition.class);
}
return new ArrayList<>(0);
}
}
package com.central.gateway.swagger;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.HashSet;
import java.util.Set;
/**
* swagger聚合配置
*
* @author zlt
* @date 2019/10/5
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Setter
@Getter
@ConfigurationProperties("zlt.swagger-agg")
@RefreshScope
public class SwaggerAggProperties {
/**
* Swagger返回JSON文档的接口路径(全局配置)
*/
private String apiDocsPath = "/v2/api-docs";
/**
* Swagger文档版本(全局配置)
*/
private String swaggerVersion = "2.0";
/**
* 自动生成文档的路由名称,设置了generateRoutes之后,ignoreRoutes不生效
*/
private Set<String> generateRoutes = new HashSet<>();
/**
* 不自动生成文档的路由名称,设置了generateRoutes之后,本配置不生效
*/
private Set<String> ignoreRoutes = new HashSet<>();
/**
* 是否显示该路由
*/
public boolean isShow(String route) {
int generateRoutesSize = generateRoutes.size();
int ignoreRoutesSize = ignoreRoutes.size();
if(generateRoutesSize > 0 && !generateRoutes.contains(route)) {
return false;
}
if(ignoreRoutesSize > 0 && ignoreRoutes.contains(route)) {
return false;
}
return true;
}
}
package com.central.gateway.swagger;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Optional;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
/**
* @author zlt
* @date 2019/10/5
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@RestController
@RequestMapping("/swagger-resources" )
public class SwaggerHandler {
private final SwaggerResourcesProvider swaggerResources;
@Autowired(required = false)
private SecurityConfiguration securityConfiguration;
@Autowired(required = false)
private UiConfiguration uiConfiguration;
@Autowired
public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
this.swaggerResources = swaggerResources;
}
@GetMapping("/configuration/security" )
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping("/configuration/ui" )
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
}
@GetMapping
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
}
\ No newline at end of file
package com.central.gateway.swagger;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 跨域配置
*
* @author zlt
* @date 2019/10/5
* <p>
* Blog: https://blog.csdn.net/zlt2000
* Github: https://github.com/zlt2000
*/
@Component
@EnableConfigurationProperties(SwaggerAggProperties.class)
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Resource
private SwaggerAggProperties swaggerAggProperties;
public SwaggerProvider(RouteLocator routeLocator, GatewayProperties gatewayProperties) {
this.routeLocator = routeLocator;
this.gatewayProperties = gatewayProperties;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
Set<String> routes = new HashSet<>();
//取出Spring Cloud Gateway中的route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
//结合application.yml中的路由配置,只获取有效的route节点
gatewayProperties.getRoutes().stream().filter(
routeDefinition -> (
routes.contains(routeDefinition.getId()) && swaggerAggProperties.isShow(routeDefinition.getId())
)
).forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
.forEach(predicateDefinition -> resources.add(
swaggerResource(
routeDefinition.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", swaggerAggProperties.getApiDocsPath())
)
)
)
);
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion(swaggerAggProperties.getSwaggerVersion());
return swaggerResource;
}
}
\ No newline at end of file
server:
port: 9900
zlt:
nacos:
server-addr: 192.168.28.130:8848
spring:
application:
name: sc-gateway
cloud:
nacos:
config:
server-addr: ${zlt.nacos.server-addr}
file-extension: yml
shared-dataids: common.yml
refreshable-dataids: common.yml
discovery:
server-addr: ${zlt.nacos.server-addr}
\ No newline at end of file
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjmgfejBXsPYynAIPczHA
eEvTDfAVaNKQudyI7VszdezbHDM1CStCIgwiMmLd7QYf1SrrmQoqxhcSRbhjE3ej
RF5qzhtx3kmepdpMrQptcsLjRkixaxCc4E2k6Us5707gGwbhoaTrRit5F2MnAdLY
C1TS3WwnO/hQfqUcAglbK8yrJ4AwAv0DAoIUSWnWqzuniV1SYbdV57uswxUssoWy
sEfPz+nv1ZLRs6Wz4eQ5Myqx2+CjWc9F8iXa2PV8Rmjms3dVbWcLUpCP18Dfzp8l
n8vF9LfYB7UaLSpfJe6FFF6+vCg4JHfo12djTUgwGjauMF3e9mmjU83KIoQS66lp
AQIDAQAB
-----END PUBLIC KEY-----
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册