Ling-cloud

来自ling
跳转至: 导航搜索

基于Ling3实现并升级

目录

常用连接

cadvisor http://192.168.74.129:18080

grafana http://192.168.74.129:3000 admin/admin

grafana初始化登录密码

mysqld-exporter http://192.168.74.129:9104

create user 'exporter'@'%' identified by 'exporter' with MAX_USER_CONNECTIONS 3 ;
grant process,replication client,select on *.* to 'exporter'@'%';

node-exporter http://192.168.74.129:9190

prometheus http://192.168.74.129:9090

seata http://192.168.74.129:8091

sentinel http://192.168.74.129:8858 sentinel/sentinel

kafkamanager http://192.168.74.129:9000/

docker stack

kafka.yml

常用代码

-Xms8192m -Xmx16384m --spring.profiles.active=prd ---spring.cloud.nacos.config.server-addr=res.powerlong.com:80 --spring.cloud.nacos.config.namespace=dc8e9e10-542d-456a-a9c7-97890e1f55ab --spring.cloud.nacos.discovery.namespace=dc8e9e10-542d-456a-a9c7-97890e1f55ab --server.port=4000 --log.path=/data/logs/pd-patch


ServiceNameConstants

-Xms128m -Xmx256m 
-e "spring.profiles.active=dev"
-e "server.ip=101.132.191.22"
-e "server.port=3000"
-e "eureka.client.service-url.defaultZone=http://pig:pig@101.132.191.22:8761/eureka/"
-e "eureka.instance.prefer-ip-address=true"
-e "eureka.instance.ip-address=101.132.191.22"

本地启动全部服务测试时需要

c:\windows\system32\drivers\etc
127.0.0.1 config.ling2.cn
127.0.0.1 eureka.ling2.cn
127.0.0.1 ling-cloud-config

docker stack

sudo tee /etc/docker/daemon.json <<-'EOF'
{
"insecure-registries": ["192.168.74.129","192.168.74.129:84"],"graph":"/data/tools/docker","registry-mirrors": ["https://3ue1wki2.mirror.aliyuncs.com"]
}
EOF
docker swarm init --advertise-addr 192.168.74.129

Swarm initialized: current node (sudtei5xkdon7pg933l9ioc18) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-3kzillfbm78apkh02xzi38fqrtn6j11z6keqt1glhmdmdou2k1-7dgedcwz5jd6gpg9letp3b5nc 192.168.74.129:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

docker network create --driver overlay --subnet 172.30.0.0/16 blade_net

docker node ls
docker node update --label-add role=cluster0001 ejxxvy9qx71uh4cs7twgczaui
docker node update --label-rm role ejxxvy9qx71uh4cs7twgczaui
docker node inspect

删除和更新

docker service update --replicas 0  blade_blade-swagger
docker service update --replicas 0  blade_blade-gateway
docker service update --replicas 0  blade_blade-auth
docker service update --replicas 0  blade_blade-report
docker service update --replicas 0  blade_blade-log
docker service update --replicas 0  blade_blade-desk
docker service update --replicas 0  blade_blade-user
docker service update --replicas 0  blade_blade-system
docker service update --replicas 0  blade_blade-flow
docker service update --replicas 0  blade_blade-design
docker service update --replicas 0  blade_blade-resource
docker service update --replicas 0  blade_blade-flow-design

docker service update --replicas 1  blade_blade-swagger
docker service update --replicas 1  blade_blade-gateway
docker service update --replicas 1  blade_blade-auth
docker service update --replicas 1  blade_blade-report
docker service update --replicas 1  blade_blade-log
docker service update --replicas 1  blade_blade-desk
docker service update --replicas 1  blade_blade-user
docker service update --replicas 1  blade_blade-system
docker service update --replicas 1  blade_blade-flow
docker service update --replicas 1  blade_blade-design
docker service update --replicas 1  blade_blade-resource

docker service rm    blade_blade-swagger
docker service rm    blade_blade-gateway
docker service rm    blade_blade-auth
docker service rm    blade_blade-report
docker service rm    blade_blade-log
docker service rm    blade_blade-desk
docker service rm    blade_blade-user
docker service rm    blade_blade-system
docker service rm    blade_blade-flow
docker service rm    blade_blade-design
docker service rm    blade_blade-resource
docker service rm    blade_blade-flow-design


docker service rm    blade_blade-redis
docker service rm    blade_nacos
docker service rm    blade_seata-server
docker service rm    blade_sentinel
docker service rm    blade_web-nginx



docker service rm    blade_prometheus
docker service rm    blade_node-exporter
docker service rm    blade_mysqld-exporter
docker service rm    blade_cadvisor
docker service rm    blade_grafana
docker service rm    blade_grafana
docker service rm    blade_grafana

docker service update --replicas 0  blade_prometheus
docker service update --replicas 0  blade_node-exporter
docker service update --replicas 0  blade_mysqld-exporter
docker service update --replicas 0  blade_cadvisor
docker service update --replicas 0  blade_grafana
docker service update --replicas 0  blade_cadvisor
docker service update --replicas 0  blade_blade-turbine
docker service update --replicas 0  blade_blade-admin
docker service update --replicas 0  blade_seata-server
docker service update --replicas 0  blade_sentinel


docker service update --replicas 1  blade_prometheus
docker service update --replicas 1  blade_node-exporter
docker service update --replicas 1  blade_mysqld-exporter
docker service update --replicas 1  blade_cadvisor
docker service update --replicas 1  blade_grafana
docker service update --replicas 1  blade_cadvisor
docker service update --replicas 1  blade_blade-turbine
docker service update --replicas 1  blade_blade-admin
docker service update --replicas 1  blade_seata-server
docker service update --replicas 1  blade_sentinel

#docker service update --image redis:3.0.6 myredis
指定service的某个节点排满状态,清空node1节点的任务

docker node update --availability drain worker1
docker node update --availability active node1
docker node inspect --pretty node1
此后,将不会再在worker1 上面分派任务。

初始化

注意先更新/docker/script 及其相关目录下脚本

cd /docker/script


如果docker stack后通过docker service logs -f看不到日志,需要使用docker stack ps blade 查看stack日志

初始化nacos

此步骤会初始化blade_blade_net nacos数据库配置在script/docker/app/nacos/init.d/custom.properties中,注意保证/docker/nacos/init.d/custom.properties中的内容正确,/docker/nacos/standalone-logs和/docker/nacos/data文件夹的存在,否则会包mount错误

docker stack deploy -c nacos-01.yml blade

初始化配置文件blade.yaml,blade-dev.yaml

blade.yaml blade-dev.yaml blade-gateway-dev.json blade-swagger-dev.yaml blade-pd-job-executor-biz-dev.yaml

初始化bladex数据库

常见问题

Pool overlaps with other one on this address space

存在相同网段配置 删除 script_blade_net

invalid mount config for type

script deploy.sh mount执行不对

配置了swagger缺没有接口出来

blade.yaml下配置base-packages

swagger:
  base-packages:
    - org.springblade
    - com.powerlong
ErrCode:503, ErrMsg:server is DOWN now, please try again later

换最新版本的nacos版本

启动时卡主不动

确保blade_net blade_blade_net只有一个 先pull images

nacos启动日志正常无法访问

使用1.4.0以上版本后需要配置mode

01nacos.yml

Adjusted frame length exceeds discarded

常用代码

mvn -o clean package dockerfile:build dockerfile:push
docker stack deploy -c auth.yml blade
export REGISTER=192.168.74.129:88/blade
export TAG=3.2.RELEASE
export COMMAND='--spring.profiles.active=dev --spring.cloud.nacos.discovery.server-addr=192.168.74.129:8848 --spring.cloud.nacos.config.server-addr=192.168.74.129:8848 --spring.cloud.inetutils.preferredNetworks[0]=^172\.30'
cd /docker/script
docker stack deploy -c pdjobexecutor.yml blade


export REGISTER=192.168.74.129:88/blade
export TAG=2.8.0.RELEASE
export COMMAND='--spring.cloud.nacos.discovery.server-addr=192.168.74.129:8848 --spring.cloud.nacos.config.server-addr=192.168.74.129:8848 --spring.cloud.inetutils.preferredNetworks[0]=^172\.30'

非docker运行

/alidata/server/maven_repository
/alidata/server/maven3.5.0
/alidata/server/java

在最后行修改

vi /etc/profile
export MAVEN_HOME=/alidata/server/apache-maven-3.6.3
export PATH=$PATH:$MAVEN_HOME/bin


source /etc/profile
. /etc/profile
mvn -version


tee /alidata/server/apache-maven-3.6.3/conf/settings.xml <<-'EOF'
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
	<localRepository>/alidata/server/maven_repository/repository</localRepository>
	<interactiveMode>true</interactiveMode>
	<usePluginRegistry />
	<offline>false</offline>
	<pluginGroups />
	
    <servers>
        <server>
            <id>ling-releases</id>
            <username>admin</username>
            <password>admin123</password>
        </server>
        <server>
            <id>ling-snapshots</id>
            <username>admin</username>
            <password>admin123</password>
        </server>
    </servers>
	
	<proxies />
	<profiles>
		<profile>
			<id>jdk-1.8</id>
			<activation>
				<activeByDefault>true</activeByDefault>
				<jdk>1.8</jdk>
			</activation>
			<properties>
				<maven.compiler.source>1.8</maven.compiler.source>
				<maven.compiler.target>1.8</maven.compiler.target>
				<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
			</properties>
			<repositories>
				<!-- oschina maven -->
				<repository>
					<id>oschinaRepository</id>
					<name>local private nexus</name>
					<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
					<releases>
						<enabled>true</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</repository>
			</repositories>
			<!-- 指定maven plugin仓库 -->
			<pluginRepositories>
				<!-- oschina的maven plugin仓库 -->
				<pluginRepository>
					<id>oschinaPluginRepository</id>
					<name>local private nexus</name>
					<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
					<releases>
						<enabled>true</enabled>
					</releases>
					<snapshots>
						<enabled>false</enabled>
					</snapshots>
				</pluginRepository>
			</pluginRepositories>
		</profile>  
	</profiles>
</settings>
EOF

java -jar

dxl

cd /ling-cloud/ling-cloud/ling-cloud-common
mvn -o -Dmaven.test.skip=true   install

nacos

cd /ling-cloud/ling-cloud/ling-cloud-nacos
mvn install
cd /ling-cloud/ling-cloud/ling-cloud-nacos/target
nohup java -jar /ling-cloud/ling-cloud/ling-cloud-nacos/target/ling-cloud-nacos.jar  --server.port=8848 --db.user=root --db.password=Wb19831010! --db.url.0=jdbc:mysql://172.26.161.78:3306/base_nacos
tail -f /ling-cloud/ling-cloud/ling-cloud-nacos/target/nohup.out

admin

cd /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-admin
mvn -o install
cd /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-admin/ling-cloud-admin-biz/target
nohup java -jar /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-admin/ling-cloud-admin-biz/target/ling-cloud-admin-biz.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=4000 &
tail -f /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-admin/ling-cloud-admin-biz/target/nohup.out
sh /ling-cloud/ling-cloud/ling-cloud-admin/ling-cloud-admin-biz/stop.sh
sh /ling-cloud/ling-cloud/ling-cloud-admin/ling-cloud-admin-biz/start.sh

auth

cd /ling-cloud/ling-cloud/ling-cloud-auth
mvn install
cd /ling-cloud/ling-cloud/ling-cloud-auth/target
nohup java -jar /ling-cloud/ling-cloud/ling-cloud-auth/target/ling-cloud-auth.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=3000 &
 tail -f /ling-cloud/ling-cloud/ling-cloud-auth/target/nohup.out

gateway

cd /ling-cloud/ling-cloud/ling-cloud-gateway
mvn install
cd /ling-cloud/ling-cloud/ling-cloud-gateway
nohup java -jar /ling-cloud/ling-cloud/ling-cloud-gateway/target/ling-cloud-gateway.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=9999 &
 tail -f /ling-cloud/ling-cloud/ling-cloud-gateway/target/nohup.out

file

cd /ling-cloud/ling-cloud/ling-cloud-file
sudo mvn -o install
cd /ling-cloud/ling-cloud/ling-cloud-file/ling-cloud-file-biz/target
sudo nohup java -jar /ling-cloud/ling-cloud/ling-cloud-file/ling-cloud-file-biz/target/ling-cloud-file-biz.jar --spring.profiles.active=prd --spring.cloud.nacos.discovery.ip=www.ling2.win --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --file.rootpath=/alidata/dockerdata/fileroot --cor.segmenterConfFolder=/alidata/dockerdata/segementer_conf --server.port=7000 &
sudo nohup java -jar /ling-cloud/ling-cloud/ling-cloud-file/ling-cloud-file-biz/target/ling-cloud-file-biz.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --file.rootpath=/alidata/dockerdata/fileroot --cor.segmenterConfFolder=/alidata/dockerdata/segementer_conf --server.port=7000 &
sudo tail -f /ling-cloud/ling-cloud/ling-cloud-file/ling-cloud-file-biz/target/nohup.out

sh /ling-cloud/ling-cloud/ling-cloud-file/ling-cloud-file-biz/stop.sh
sh /ling-cloud/ling-cloud/ling-cloud-file/ling-cloud-file-biz/start.sh

zy

cd /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-zy
mvn -o install
cd /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-zy/ling-cloud-zy-biz/target
nohup java -jar /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-zy/ling-cloud-zy-biz/target/ling-cloud-zy-biz.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=6300 &
tail -f /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-zy/ling-cloud-zy-biz/target/nohup.out
sh /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-zy/ling-cloud-zy-biz/stop.sh
sh /ling-cloud/ling-cloud-3.3.RELEASE/ling-cloud-zy/ling-cloud-zy-biz/start.sh

dxl

rm -rf /alidata/server/maven_repository/repository/com/ling/ling-cloud-dxl-api
cd /ling-cloud/ling-cloud/ling-cloud-dxl
git pull
mvn -o install
cd /ling-cloud/ling-cloud/ling-cloud-dxl/ling-cloud-dxl-biz/target
nohup java -jar /ling-cloud/ling-cloud/ling-cloud-dxl/ling-cloud-dxl-biz/target/ling-cloud-dxl-biz.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=5009 &
tail -f /ling-cloud/ling-cloud/ling-cloud-dxl/ling-cloud-dxl-biz/target/nohup.out
sh /ling-cloud/ling-cloud/ling-cloud-dxl/ling-cloud-dxl-biz/stop.sh
ps -ef | grep ling-cloud-dxl-biz
sh /ling-cloud/ling-cloud/ling-cloud-dxl/ling-cloud-dxl-biz/start.sh

pay

cd /ling-cloud/ling-cloud/ling-cloud-pay
git pull
mvn -o install
cd /ling-cloud/ling-cloud/ling-cloud-pay/target
nohup java -jar /ling-cloud/ling-cloud/ling-cloud-pay/target/ling-cloud-pay.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=5010 &
tail -f /ling-cloud/ling-cloud/ling-cloud-pay/target/nohup.out
sh /ling-cloud/ling-cloud/ling-cloud-pay/stop.sh
ps -ef | grep ling-cloud-pay
sh /ling-cloud/ling-cloud/ling-cloud-pay/start.sh


rm -rf /alidata/server/maven_repository/repository/com/ling/ling-cloud-dxl-api

mp

cd /ling-cloud/ling-cloud/ling-cloud-mp
git pull
mvn -o install
cd /ling-cloud/ling-cloud/ling-cloud-mp/target
nohup java -jar /ling-cloud/ling-cloud/ling-cloud-mp/target/ling-cloud-mp.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=6000 &
tail -f /ling-cloud/ling-cloud/ling-cloud-mp/target/nohup.out
sh /ling-cloud/ling-cloud/ling-cloud-mp/stop.sh
ps -ef | grep ling-cloud-mp
sh /ling-cloud/ling-cloud/ling-cloud-mp/start.sh

elasticsearch

cd /ling-cloud/ling-cloud/ling-cloud-elasticsearch
sudo mvn -o install
cd /ling-cloud/ling-cloud/ling-cloud-elasticsearch/ling-cloud-elasticsearch-biz/target
sudo nohup java -jar /ling-cloud/ling-cloud/ling-cloud-elasticsearch/ling-cloud-elasticsearch-biz/target/ling-cloud-elasticsearch-biz.jar --spring.profiles.active=prd --spring.cloud.nacos.config.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --spring.cloud.nacos.discovery.namespace=71af3a62-0bc0-4f1e-8e50-a997f811101c --server.port=6200 &
tail -f /ling-cloud/ling-cloud/ling-cloud-elasticsearch/ling-cloud-elasticsearch-biz/target/nohup.out

总计设计

Tax-cloud-network.png

ling-cloud-dependencies

做包和版本管理

ling-cloud-eureka

ling-cloud-eureka开发和调试

  • hosts中设置
101.132.191.22 ling-cloud-eureka
  • 如果需要调试hosts中设置
127.0.01 ling-cloud-eureka
  • docker
docker run --name ling-cloud-eureka -d -p 8761:8761 ling-cloud-eureka

ling-cloud-config

spring.cloud.config.uri=http://config.ling2.cn:8888,http://config.ling2.cn:8889 如果第一个失败.整个应用都会失败

配置中心,基于spring cloud config

ling-cloud-config开发和调试

  • hosts中设置
101.132.191.22 ling-cloud-config
  • 如果需要调试hosts中设置
127.0.01 ling-cloud-config
  • docker
docker run --name ling-cloud-config --link ling-cloud-eureka  -d -p 8888:8888 ling-cloud-config
spring:
  application:
    name: @artifactId@
  # dev环境
  profiles:
    active: dev
  # 配置中心
  cloud:
    config:
      fail-fast: true
      name: ${spring.application.name}
      profile: ${spring.profiles.active}
      uri: @cloud.uri@
#      discovery:
#        enabled: true
#        service-id: ling-cloud-config
#      discovery:
#        enabled: true
#        service-id: ling-cloud-config

不能和 uri: @cloud.uri@同时出现

启动直到config中心启动

开启客户端重试功能需要两个新依赖,spring-retry 和 spring-boot-starter-aop,把如下代码添加到 web 项目的 pom.xml 文件中:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
spring:
  application:
    name: web-client
  cloud:
    config:
      uri: http://localhost:8888
      fail-fast: true
      retry:
        initial-interval: 1000
        max-attempts: 6
        max-interval: 2000
        multiplier: 1.1

首先把 spring.cloud.config.fail-fast 为true,即在获取不到远程配置时,立即失败,但是用下边的配置进行重试。

spring.cloud.config.retry 所有子项均为默认值:

  • initial-interval: 最初重试间隔为 1000 毫秒
  • max-attempts: 最多重试 6 次
  • max-interval: 最长重试间隔为 2000 毫秒
  • multiplier: 每次重试失败后,重试间隔所增加的倍数

ling-cloud-nacos

  • 替代ling-cloud-config和ling-cloud-eureka

ling-cloud-common

ling-cloud-common-core

ling-cloud-common-data

  • src\main\resources\META-INF\spring.factories

redis

@Configuration
@AutoConfigureAfter({RedisAutoConfiguration.class})
@ConditionalOnBean({RedisConnectionFactory.class})
@ConditionalOnMissingBean({CacheManager.class})
@EnableConfigurationProperties(CacheProperties.class)
@EnableCaching
@Configuration
@ConditionalOnBean(RedisConnectionFactory.class)
@AllArgsConstructor

redis 异步

	private final RedisTemplate redisTemplate;
	redisTemplate.convertAndSend(CacheConstants.MP_REDIS_RELOAD_TOPIC, "重新加载公众号配置");


@Slf4j
@Configuration
@RequiredArgsConstructor
public class RedisEventConfig {
	/**
	 * redis 监听配置,监听 mp_redis_reload_topic,重新加载配置
	 *
	 * @param redisConnectionFactory redis 配置
	 * @return
	 */
	@Bean
	public RedisMessageListenerContainer redisContainer(RedisConnectionFactory redisConnectionFactory) {
		RedisMessageListenerContainer container = new RedisMessageListenerContainer();
		container.setConnectionFactory(redisConnectionFactory);
		container.addMessageListener((message, bytes) -> {
			log.warn("接收到重新加载公众号配置事件");
			initServices();
		}, new ChannelTopic(CacheConstants.MP_REDIS_RELOAD_TOPIC));
		return container;
	}
}

mybatis

  • DataScope
  • DataScopeInterceptor mybatis 数据权限拦截器
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
  • DataScopeTypeEnum
  • MybatisPlusConfig 创建租户维护处理器对象,分页插件,数据权限插件,逻辑删除插件
@MapperScan("com.ling.cloud.*.mapper")

@TableLogic 删除标记,1:已删除,0:正常

  • 租户列和值的设置
  • TenantSqlParser
  • public class LingTenantHandler implements TenantHandler

Tenant

requestTemplate.header(CommonConstants.TENANT_ID, TenantContextHolder.getTenantId().toString());
private final ThreadLocal<Integer> THREAD_LOCAL_TENANT = new TransmittableThreadLocal<>();
@Order(Ordered.HIGHEST_PRECEDENCE)

ling-cloud-common-job(ElasticJob)

使用zookeeper版本的任务调度,更多参考ElasticJob

  • EnableElasticJob 使用此注解的微服务能使用ElasticJob
ElasticJobAutoConfiguration-->ElasticJobProperties-->@ConfigurationProperties(prefix = "spring.elasticjob")
  • AbstractJobInitialization 任务初始化基类
  • DataflowJobInitialization 流式任务初始
  • ScriptJobInitialization 脚本任务初始
  • SimpleJobInitialization 简单任务初始
  • ElasticJobProperties 任务配置类
@ConfigurationProperties(prefix = "spring.elasticjob")
  • ElasticJobAutoConfiguration 任务初配置入口类
public static final String DEFAULT_REGISTRY_CENTER_NAME = "elasticJobRegistryCenter";

ling-cloud-common-job

更多参考ElasticJob

ling-cloud-common-job(xxx-Job)

  • 依赖
        <dependency>
            <groupId>com.deloitte.atrapa</groupId>
            <artifactId>tax-common-job</artifactId>
        </dependency>
  • 配置
tax:
  job:
    executor:
      appname: tax-basic
      ip:
      port: 9991
  • 实现
package com.deloitte.atrapa.tax.ptc.job;

import org.springframework.stereotype.Component;

import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import com.xxl.job.core.log.XxlJobLogger;

/**
 * 任务Handler示例(Bean模式)
 *
 * 开发步骤: 1、继承"IJobHandler":“com.xxl.job.core.handler.IJobHandler”;
 * 2、注册到Spring容器:添加“@Component”注解,被Spring容器扫描为Bean实例;
 * 3、注册到执行器工厂:添加“@JobHandler(value="自定义jobhandler名称")”注解,注解value值对应的是调度中心新建任务的JobHandler属性的值。
 * 4、执行日志:需要通过 "XxlJobLogger.log" 打印执行日志;
 *
 * 
 */
@JobHandler(value = "TestJobHandler")
@Component
public class DemoJobHandler extends IJobHandler {


	@Override
	public ReturnT<String> execute(String param) throws Exception {
		XxlJobLogger.log("XXL-JOB, Hello World.");
		System.out.print("------------> + Hello World.\n");
		return SUCCESS;
	}

}

ling-cloud-common-jpa

ling-cloud-common-neo4j

图数据库介绍

ling-cloud-common-log

  • src\main\resources\META-INF\spring.factories
  • RemoteLogService
  • SysLog 注解,在方法上使用,记录谁,在什么时候调用了什么微服务的这个方法,参数是什么,耗时多少
@SysLog("添加部门")
publisher.publishEvent(new SysLogEvent(logVo));
@Slf4j
@AllArgsConstructor
public class SysLogListener {
	private final RemoteLogService remoteLogService;

	@Async
	@Order
	@EventListener(SysLogEvent.class)
	public void saveSysLog(SysLogEvent event) {
		SysLogVo sysLog = event.getSysLog();
		remoteLogService.saveLog(sysLog, SecurityConstants.FROM_IN);
	}
}
@EnableAsync
@Configuration
@AllArgsConstructor
@ConditionalOnWebApplication
public class LogAutoConfiguration {
	private final RemoteLogService remoteLogService;

	@Bean
	public SysLogListener sysLogListener() {
		return new SysLogListener(remoteLogService);
	}

	@Bean
	public SysLogAspect sysLogAspect(ApplicationEventPublisher publisher) {
		return new SysLogAspect(publisher);
	}
}

ling-cloud-common-minio

更多参考minio

  • src\main\resources\META-INF\spring.factories
  • MinioEndpoint minio 对外提供服务端点
  • MinioTemplate java minio sdk 对接
  • MinioAutoConfiguration minio 自动配置类
  • MinioProperties 读取minio配置对应的配置

minio配置

# 文件系统
minio:
  url: http://miniocloud.ling2.cn
  access-key: ling
  secret-key: 123456789

开启minio除了上面2个属性外,还需要以下2个属性

minio.endpoint.enable
minio.endpoint.name
@ConditionalOnProperty(name = "minio.endpoint.enable", havingValue = "true")
@RestController
@AllArgsConstructor
@RequestMapping("${minio.endpoint.name:/minio}")
public class MinioEndpoint {
...
}

ling-cloud-common-security

......
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Import({LingResourceServerAutoConfiguration.class, LingSecurityBeanDefinitionRegistrar.class})
public @interface EnableLingResourceServer {

	/**
	 * false:上下文获取用户名
	 * true: 上下文获取全部用户信息
	 *
	 * @return
	 */
	boolean details() default false;
}

oauth2

@Configuration
@AutoConfigureAfter(OAuth2AutoConfiguration.class)
@ConditionalOnWebApplication
@ConditionalOnProperty("security.oauth2.client.client-id")
接口权限
@PreAuthorize("@pms.hasPermission('sys_menu_add')")
  • @RequestMapping(value = "/user/getUserInfoByUserName", method = RequestMethod.GET) 中保证角色列表正确传递
  • PermissionService 中实现@pms.hasPermission
  • DefaultUserDetailsServiceImpl#getUserDetails 完成业务对象和spring security对象的转换
  • 如果上述不能满足要求,参考PermissionService 实现相关逻辑即可
@Data
@Configuration
@RefreshScope
@ConditionalOnExpression("!'${security.oauth2.client.ignore-urls}'.isEmpty()")
@ConfigurationProperties(prefix = "security.oauth2.client")
public class PermitAllUrlProperties {
	private List<String> ignoreUrls = new ArrayList<>();
}

exception

feign security

对应sys_oauth_client_details表 client_id和client_secret

## spring security 配置
security:
  oauth2:
    client:
      client-id: ENC(ltJPpR50wT0oIY9kfOe1Iw==)
      client-secret: ENC(ltJPpR50wT0oIY9kfOe1Iw==)
      scope: server
      # 默认放行url,子模块重写时application-dev.yml中的公共配置会被覆盖,所以要把公共配置中的放行url再写一次
      ignore-urls:
        - /actuator/**
        - /v2/api-docs
        - /user/info/*
        - /social/info/*
        - /mobile/*
        - /log/save

全局FeignFallback

hystrix.command.default.execution.isolation.strategy=SEMAPHORE

##阿里sentinel熔断器
feign:
  hystrix:
    enabled: true #全局fallback生效前提
#使用Feign调用接口分两层,ribbon的调用和hystrix的调用,所以ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间 https://blog.csdn.net/east123321/article/details/82385816
#hystrix配置
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE

登录

  • 在index.html中设置加密需要的js
  •     /**  bo.wang token  begin */
        async tryGetToken(username, password) {
          const user = encryption({
            data: {
              username: username,
              password: password
            },
            key: ENCRYPTION_KEY,
            param: ['password']
          })
          // console.log(user)
          let result = await loginByUsername(user.username, user.password, user.code, user.randomStr);
          if(result.success) {
            const data = result.result;
            saveToken(data)
          }
        }
        /**  bo.wang token  end */
import axios from 'axios';
import request from "../../utils/request";
import {getStore, removeStore, setStore} from "../../utils/store";

export const ENCRYPTION_KEY = 'pigxpigxpigxpigx';
export const ACCESS_TOKEN = 'access_token';
export const EXPIRES_IN = 'expires_in';
export const REFRESH_TOKEN = 'refresh_token';

const scope = 'server'

/**
 *加密处理
 */
export function encryption(params) {
  let {
    data,
    type,
    param,
    key
  } = params
  const result = JSON.parse(JSON.stringify(data))
  if (type === 'Base64') {
    param.forEach(ele => {
      result[ele] = btoa(result[ele])
    })
  } else {
    param.forEach(ele => {
      var data = result[ele]
      key = CryptoJS.enc.Latin1.parse(key)
      var iv = key
      // 加密
      var encrypted = CryptoJS.AES.encrypt(
        data,
        key, {
          iv: iv,
          mode: CryptoJS.mode.CBC,
          padding: CryptoJS.pad.ZeroPadding
        })
      result[ele] = encrypted.toString()
    })
  }
  return result
}

export async function loginByUsername(username, password, code, randomStr) {
  const grant_type = 'password';
  const scope = 'server'
  let data = {username, password, randomStr, code, grant_type, scope};
  let headers = {
    isToken: false,
    'TENANT_ID': '1',
    'Authorization': 'Basic cGlnOnBpZw=='
  }
  return request({
    url: '/auth/oauth/token',
    method: 'get',
    data: data,
    headers: headers
  })
}

export const refreshToken = (refresh_token) => {
  const grant_type = 'refresh_token'
  return request({
    url: '/auth/oauth/token',
    headers: {
      'isToken': false,
      'TENANT_ID': '1',
      'Authorization': 'Basic cGlnOnBpZw==',
    },
    method: 'post',
    params: {refresh_token, grant_type, scope}
  })
}

export const logout = () => {
  return request({
    url: '/auth/token/logout',
    method: 'delete'
  })
}


export const saveToken = (token) => {
  setStore({
    name: ACCESS_TOKEN,
    content: token.access_token,
    type: 'session'
  })
  setStore({
    name: EXPIRES_IN,
    content: token.expires_in,
    type: 'session'
  })
  setStore({
    name: REFRESH_TOKEN,
    content: token.refresh_token,
    type: 'session'
  })
}
export const clearToken = () => {
  removeStore({
    name: ACCESS_TOKEN,
  });
  removeStore({
    name: EXPIRES_IN,
  });
  removeStore({
    name: REFRESH_TOKEN,
  });
}

export const getToken = (name) => {
  return getStore({
    name: name
  })
}

刷新token

src\page\home\Home.vue

  created() {
    // console.log("this.$store.getters.token ="+this.$store.getters.token);
    this.getPrivate();
    /*********bo.wang token begin**************/
    //实时检测刷新token
    try {
      this.refreshToken()
    }catch (e) {

    }
    /*********bo.wang token end**************/
  },

    /*********bo.wang token begin**************/
    // 实时检测刷新token
    refreshToken() {
      this.refreshTime = setInterval(() => {
        const token = getStore({
          name: 'access_token',
          debug: true,
        });

        if (validatenull(token)) {
          return;
        }

        if (this.$store.state.user.expiresIn <= 1000 && !this.refreshToken) {
          this.refreshToken = true
          try {
            this.tryRefreshToken();
          } catch (e) {
            clearInterval(this.refreshTime)
          }
          this.refreshLock = false
        }
        this.$store.state.user.expiresIn = this.expiresIn - 10;
      }, 10000);
    },
    async tryRefreshToken() {
      let result = await refreshToken(this.$store.state.user.refreshToken);
      // console.log(result);
      const data = result.result;
      saveToken(data)
    }
    /*********bo.wang token end**************/

登出

LingTokenEndpoint#logout src/store/modules/user.js

      /***BO.WANG roken 处理 begin****/
      try{
        logout().then(response=>{
          clearToken();
        })
      }catch (e) {

      }
      /***BO.WANG roken 处理 end****/

短信验证

菜单,角色,权限数据链

SysUserService:findUserInfo-->DefaultUserDetailsServiceImpl:getUserDetails-->SecurityUtils:getRoles

HandlerMethodArgumentResolver @LoginUser

https://www.cnblogs.com/pythoncd/articles/12546575.html

ling-cloud-common-swagger

文件:Swagger配置文档.pdf

#swagger公共信息
swagger:
  authorization:
    name: pigX OAuth
    auth-regex: ^.*$
    authorization-scope-list:
      - scope: server
        description: server all
    token-url-list:
      - http://cloud.ling2.cn:9999/auth/oauth/token
# swagger相关配置,覆盖全局配置
swagger:
  authorization:
    authorization-scope-list:
      - scope: 'server'
        description: 'server all'
      - scope: 'read'
        description: 'read all'
      - scope: 'write'
        description: 'access all'

Pigx2.0-swagger2.PNG Ling-cloud-gteway-swagger-ui.png

403 Forbidden

发现form中的内容并未在network的监控出现,所以发现是跨域问题

Request URL:http://127.0.0.1:9999/auth/oauth/token
Request Method:OPTIONS
Status Code:403 Forbidden
Remote Address:127.0.0.1:9999

ling-cloud-common-transaction

mobile

service

ling-cloud-common-transaction

ling-cloud-common-datasource

  • admin SysDsConf中维护多数据源配置
  • ling-cloud-common-datasource模块 通过默认jpa配置连接admin 模块数据库读取SysDsConf表数据初始化多数据源
  • 通过DynamicDataSourceContextHolder.setDataSourceType(dbkey);切换当前数据源
  • 通过下面的代码获取数据源连接并执行操作
try (Connection connection = dataSource.getConnection()) {
				int result = connection.prepareStatement("select count(1) from base_user").executeUpdate();
//				int result =dataSource.getConnection().getCurrentSession().createNativeQuery("select count(1) from base_user").executeUpdate();
//				int result = entityManager.createNativeQuery("select count(1) from base_user").executeUpdate();
				log.info(result + "");
			} catch (Exception e) {
				e.printStackTrace();
			}

ling-cloud-common-job

  • 主要负责以下职责
    • 和第三方系统做数据同步
    • 连接系统内的多个数据库完成跨数据库数据操作 比如项目和人员数据分发
    • 调用微服务内通过remotexxxx提供的复杂的逻辑,而不是冗余一份代码到job
  • 通过ling-cloud-common-datasource拥有多数据源功能

ling-cloud-auth

ling-cloud-auth总体设计

  • 提供基于的sso认证体系
  • 通过依赖ling-cloud-common-security调用ling-cloud-base-biz服务提供的具体实现
  • 需要独立的数据库

ling-cloud-auth详细设计

登录流程

  • 登录请求路径:auth/oauth/token?username=admin&password=rKu1%2F348LvKp0rsVC06eCA%3D%3D&randomStr=92391549130573849&code=n33p&grant_type=password&scope=server
  • 登录请求流程在gateway的PasswordDecoderFilter中首先被处理
  • PasswordDecoderFilter会解密密码并转发请求http://127.0.0.1:9999/oauth/token?password=123456&randomStr=49011549130055496&code=e3nf&grant_type=password&username=admin&scope=server
  • gateway把服务交给oauth,oauth中会调用UserDetailsService的实现查找用户并组装成实现了User接口的对象并交给spring security进行后续的登录判断


XMLHttpRequest cannot load http://192.168.31.128:3000/token/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.31.128:8080' is therefore not allowed access. The response had HTTP status code 404.

PasswordDecoderFilter

Connection has been closed] for HTTP POST "/auth/oauth/token?username=admin&password=rKu1%2F348LvKp0rsVC06eCA%3D%3D&randomStr=63861551504419904&code=264m&grant_type=password&scope=server", but ServerHttpResponse already committed (302 FOUND)
03-02 13:27:18.305 DEBUG [reactor.netty.channel.ChannelOperations] - [id: 0xd80b757d, L:/127.0.0.1:9999 ! R:/127.0.0.1:57745] An outbound error could not be processed

gateway 路由失败-->gateway从redis加载路由机制没启动



LingTokenEndpoint不能命名为TokenEndpoint

前端登录后跳http://192.168.31.128:3001/token/login 用户:pig 登录失败,异常:Bad credentials

SecurityConstants中的sql语句配置异常 或sys_oauth_client_details数据为空

能启动,整个项目不能访问

突然就好了,我在老parent中启动没问题后,把所有代码替换过来,启动就可以了,代码比较没有啥改动.................Maven clean?

Handling error: InvalidRequestException, Missing grant type
org.springframework.security.oauth2.provider.endpoint.TokenEndpoint

我们的框架中需要用get请求,post出错

http://127.0.0.1:8888/oauth/token?password=123456&timeSpan=2019-03-09T13%3A36%3A06.527Z&code=&grant_type=password&username=admin&scope=server
428 Precondition Required 请求头中client信息为空
RemoteTokenServices.loadAuthentication NullPointerException
java.lang.NullPointerException: null
at org.springframework.security.oauth2.provider.token.RemoteTokenServices.loadAuthentication(RemoteTokenServices.java:108)
常用断点
RemoteTokenServices.postForMap

OAuth2的四种模式

https://cloud.tencent.com/developer/news/229775 http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html 跨域免登陆 开发时用localhost和127.0.0.1完成测试,切换的下拉内容地址用字典来配置

例如:

•	当前地址为:
localhost:8000/adaf?aaa=bbb
•	切换系统地址:
127.0.0.1:8000/tokenlogin?redirect='adaf?aaa=bbb'&token=:token
•	模拟登陆
1.	写入token,tenant_id和调用admin/user/info
2.	完成相同页面跳转
•	tokenlogin页面跳转到127.0.0.1:8000/adaf?aaa=bbb或首页

OAuth2 client 验证

OAuth2的四种模式

OAuth2的四种模式 https://blog.csdn.net/qq_34997906/article/details/89600076?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

  • 授权码模式(authorization code)(最正统的方式,也是目前绝大多数系统所采用的)(支持refresh token) (用在服务端应用之间)
  • 密码模式(resource owner password credentials)(为遗留系统设计) (支持refresh token)
  • 简化模式(implicit)(为web浏览器应用设计)(不支持refresh token) (用在移动app或者web app,这些app是在用户的设备上的,如在手机上调起微信来进行认证授权)
  • 客户端模式(client credentials)(为后台api服务消费者设计) (不支持refresh token) (为后台api服务消费者设计)
OAuth2 client设计

当前权限检测机制有2层,第一层是header的token机制,第二层是客户端模式(client credentials)需要的appId和appKey

默认所有对服务后端的请求,如果没有在配置中心配置ignore-urls为匿名访问,都需要经过token和client 的验证.

添加@Inner(value = false)使请求放弃token验证和client 验证

添加@Client使请求使用client 验证

@Inner 如何保证有此注解的方法能匿名访问?

  • 由Client,LingSecurityClientAspect,RemoteClientDetailsService,LingTokenEndpoint完成这些功能
  • 接口暴露,使用@Inner(value = false)将此接口排除在token机制之外 使用@Client将接口设置为必须校验客户端模式(client credentials)的值
	@Inner(value = false)
	@Client
	@SysLog("testClent")
	@GetMapping("/testClent")
	public R<String> testClent() {
		return R.ok();
	}
  • 接口调用示例

Client security.PNG

  • 错误返回
{
    "code": 40000002,
    "message": "appId和appKey请求授权错误",
    "data": null,
    "msg": "appId和appKey请求授权错误",
    "success": false
}
  • 正确返回示例
{
    "code": 0,
    "message": null,
    "data": null,
    "msg": null,
    "success": true
}
OAuth2 client 账号添加

平台basic中的表xxx_oauth_client_details 3.2版本admin中的表为sys_oauth_client_details 在表中添加client_id和client_secret 用于授权.

Remote接口编写示例
@FeignClient(contextId = "remoteDemoService", value = ServiceNameConstants.BASE_SERVICE)
public interface RemoteDemoService {

	@GetMapping("/demo/testClent")
	public R<String> testClent(@RequestHeader(value = SecurityConstants.APP_ID, required = true) String appId,
							   @RequestHeader(value = SecurityConstants.APP_KEY, required = true) String appKey);
}
Remote接口调用示例
package com.ling.cloud.admin.controller;

import com.ling.cloud.common.core.util.R;
import com.ling.cloud.common.log.annotation.SysLog;
import com.ling.cloud.common.security.annotation.Client;
import com.ling.cloud.common.security.annotation.Inner;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@Slf4j
@Transactional
@RestController
@AllArgsConstructor
@RequestMapping("/demo")
@Api(value = "demo", tags = "demo")
public class DemoController {
	@Resource
	RemoteDemoService remoteDemoService;

	/**
	 * 使用 apiId和appKey暴露的接口
	 *
	 * @return
	 */
	@Inner(value = false)
	@Client
	@SysLog("testClent")
	@GetMapping("/testClent")
	public R<String> testClent() {
		return R.ok();
	}

	/**
	 * 通过remote接口测试apiId和appKey暴露的接口
	 *
	 * @return
	 */
	@Inner(value = false)
	@SysLog("testRemoteClent")
	@GetMapping("/testRemoteClent")
	public R<String> testRemoteClent() {
		/**
		 * 这里请使用@value 配置模式
		 */
		remoteDemoService.testClent("test", "pig");
		return R.ok();
	}
//	@RequestHeader(SecurityConstants.FROM) String from
}

ling-cloud-admin

ling-cloud-admin-api

ling-cloud-admin-biz

Ling-cloud-admin-config.jpg

  • 权限相关表

Ling-cloud-admin-security.jpg

租户和管理员

  • 超级租户1拥有ROLE_ADMIN角色和ROLE_admin角色
  • ROLE_ADMIN比ROLE_admin多拥有菜单管理和租户管理权限
  • ROLE_ADMIN可以维护各个租户的ROLE_admin的权限
  • ROLE_admin不可以维护自己的权限
  • ROLE_admin可以创建所属租户其他角色,所有租户能分配的权限只能是以ROLE_admin的权限为最大范围

字典参数

系统参数

数据权限

租户数据权限

接口权限

ling-cloud-activiti

ling-cloud-activiti

流程驱动

  • 创建业务数据
  • 启动流程(业务id)
	/**
	 * 启动流程、更新请假单状态
	 *
	 * @param leaveId
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean saveStartProcess(String leaveId) {
		LeaveBill leaveBill = leaveBillMapper.selectById(leaveId);
		leaveBill.setState(TaskStatusEnum.CHECK.getStatus());

		String key = leaveBill.getClass().getSimpleName();
		String businessKey = key + "_" + leaveBill.getLeaveId();
		runtimeService.startProcessInstanceByKeyAndTenantId(key, businessKey, String.valueOf(ContextHolder.getTenantId()));
		leaveBillMapper.updateById(leaveBill);
		return Boolean.TRUE;
	}

触发TaskListener

package com.ling.cloud.act.listener;

import cn.hutool.core.collection.CollUtil;
import com.ling.cloud.common.core.constant.SecurityConstants;
import com.ling.cloud.common.security.service.RemoteUserService;
import com.ling.pl.common.vo.SysUserVO;
import com.ling.pl.common.context.ContextHolder;
import com.ling.cloud.common.core.util.R;
import com.ling.cloud.common.core.util.SpringContextHolder;
import com.ling.cloud.common.security.util.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.springframework.messaging.simp.SimpMessagingTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author lengleng
 * @date 2018/9/27
 * 请假流程监听器
 */
@Slf4j
public class LeaveProcessTaskListener implements TaskListener {

	/**
	 * 查询提交人的上级
	 *
	 * @param delegateTask
	 */
	@Override
	public void notify(DelegateTask delegateTask) {
		SimpMessagingTemplate simpMessagingTemplate = SpringContextHolder.getBean(SimpMessagingTemplate.class);
		RemoteUserService userService = SpringContextHolder.getBean(RemoteUserService.class);

		R<List<SysUserVO>> result = userService.ancestorUsers(SecurityUtils.getUser().getUsername(), SecurityConstants.FROM_IN);
		List<String> remindUserList = new ArrayList<>();

		if (CollUtil.isEmpty(result.getData())) {
			log.info("用户 {} 不存在上级,任务单由当前用户审批", SecurityUtils.getUser().getUsername());
			delegateTask.addCandidateUser(SecurityUtils.getUser().getUsername());
			remindUserList.add(SecurityUtils.getUser().getUsername());
		} else {
			List<String> userList = result.getData().stream().map(SysUserVO::getUsername).collect(Collectors.toList());
			log.info("当前任务 {},由 {}处理", delegateTask.getId(), userList);
			delegateTask.addCandidateUsers(userList);
			remindUserList.addAll(userList);
		}

		remindUserList.forEach(user -> {
			String target = String.format("%s-%s", SecurityUtils.getUser().getUsername(), ContextHolder.getTenantId());
			simpMessagingTemplate.convertAndSendToUser(target, "/remind", delegateTask.getName());
		});
	}
}

业务驱动流程

/**
	 * 提交任务
	 *
	 * @param leaveBillDto
	 * @return
	 */
	@Override
	public Boolean submitTask(LeaveBillDto leaveBillDto) {
		String taskId = leaveBillDto.getTaskId();
		String message = leaveBillDto.getComment();
		String id = leaveBillDto.getLeaveId();

		Task task = taskService.createTaskQuery()
			.taskId(taskId)
			.singleResult();

		String processInstanceId = task.getProcessInstanceId();
		Authentication.setAuthenticatedUserId(SecurityUtils.getUser().getUsername());
		taskService.addComment(taskId, processInstanceId, message);

		Map<String, Object> variables = new HashMap<>(1);
		variables.put("flag", leaveBillDto.getTaskFlag());
		variables.put("days", leaveBillDto.getDays());

		taskService.complete(taskId, variables);
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()
			.processInstanceId(processInstanceId)
			.singleResult();

		if (pi == null) {
			LeaveBill bill = new LeaveBill();
			bill.setLeaveId(id);
			bill.setState(StrUtil.equals(TaskStatusEnum.OVERRULE.getDescription()
				, leaveBillDto.getTaskFlag()) ? TaskStatusEnum.OVERRULE.getStatus()
				: TaskStatusEnum.COMPLETED.getStatus());
			leaveBillMapper.updateById(bill);
		}
		return null;
	}

ling-cloud-builder

ling-cloud-builder

ling-cloud-gateway

路由规则参考 https://blog.csdn.net/zzzgd_666/article/details/86095716 证书配置参考 https://segmentfault.com/a/1190000017155814

ling-cloud-gateway概要设计

  • 处理请求过滤,限流,swagger资源聚合,验证码,动态路由加载(非初始化,初始化有base模块完成),微服务请求全局拦截器(非权限验证),

ling-cloud-gateway详细设计

# 不校验验证码终端 config/ling-cloud-gateway-dev.yml
ignore:
  clients:
    - test
  swagger-providers:
    - ling-cloud-auth
    - ling-cloud-admin-tx-manager
  • HystrixFallbackHandlerHystrix 降级通知
  • 非关键请求错误降级不通知(HystrixFallbackHandler中修改HttpStatus和注释掉代码都不生效,去掉file请求也无效)

ling-cloud-common-gateway

使使用EnableDynamicRoute注解的application支持动态路由加载,否则需要在配置文件中设置路由规则

EnableDynamicRoute-->@Import(DynamicRouteAutoConfiguration.class)-->@ComponentScan("com.ling.cloud.common.gateway")-->RedisRouteDefinitionWriter-->getRouteDefinitions
public class DynamicRouteInitEvent extends ApplicationEvent {
	public DynamicRouteInitEvent(Object source) {
		super(source);
	}
}

	@Async
	@Order
	@EventListener({WebServerInitializedEvent.class, DynamicRouteInitEvent.class})
	public void initRoute() {
		Boolean result = redisTemplate.delete(CommonConstants.ROUTE_KEY);
		log.info("初始化网关路由 {} ", result);

		routeConfService.routes().forEach(route -> {
			RouteDefinitionVo vo = new RouteDefinitionVo();
			vo.setRouteName(route.getRouteName());
			vo.setId(route.getRouteId());
			vo.setUri(URI.create(route.getUri()));
			vo.setOrder(route.getOrder());

			JSONArray filterObj = JSONUtil.parseArray(route.getFilters());
			vo.setFilters(filterObj.toList(FilterDefinition.class));
			JSONArray predicateObj = JSONUtil.parseArray(route.getPredicates());
			vo.setPredicates(predicateObj.toList(PredicateDefinition.class));

			log.info("加载路由ID:{},{}", route.getRouteId(), vo);
			redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(RouteDefinitionVo.class));
			redisTemplate.opsForHash().put(CommonConstants.ROUTE_KEY, route.getRouteId(), vo);
		});
		log.debug("初始化网关路由结束 ");
	}
  • DynamicRouteInitRunner容器启动后保存配置文件里面的路由信息到Redis
  • RedisRouteDefinitionWriter redis 保存路由信息,优先级比配置文件高 save,delete,getRouteDefinitions. 与DynamicRouteInitRunner结合,一个从数据库读取路由配置然后存redis,RedisRouteDefinitionWriter操作redis信息

动态路由的开启和关闭

bootstrap.yml 中设置 enable-dynamic-route为false或不设置时,不走动态路由逻辑.只有配置并且为true时走redis中缓存的路由配置

routes:
  enable-dynamic-route: false

ling-cloud-pay

https://javen205.gitee.io/ijpay/guide/config/weixinpay_config.html#%E6%99%AE%E9%80%9A%E5%95%86%E6%88%B7%E6%A8%A1%E5%BC%8F%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%95%86%E6%A8%A1%E5%BC%8F%E5%8C%BA%E5%88%AB

https://blog.csdn.net/lzq199528/article/details/104728479/

<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wxaec0aa10e3f127b1]]></appid>
<mch_id><![CDATA[1601549052]]></mch_id>
<nonce_str><![CDATA[Nj3YR1XN5MlWUPMu]]></nonce_str>
<sign><![CDATA[EE20B893E054443A70BE9A81E0E7E484]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx032228539633968887260c101250106300]]></prepay_id>
<trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

 {"channel":"WEIXIN_MP","params":{"timeStamp":"1596467148","package":"prepay_id=wx03230548427456a646f2f82b1718649300","paySign":"DDBE91FC0D11ACA918A274F13811DCD1","appId":"wxaec0aa10e3f127b1","signType":"MD5","nonceStr":"1596467148492"},"goods":{"amount":"1","archiveBaseDate":1659539147975,"createDate":1596467147954,"createdBy":"-1","createdByEnName":"SYSTEM","createdByName":"SYSTEM","delFlag":"0","goodsId":"10001","goodsName":"测试产品","log":false,"modifiedDate":1596467148502,"payOrderId":"20200803234705","status":"0","userId":"opTqewHXdEcXNoOd8JdTvzuch790"}}
微信订单回调信息:<xml><appid><![CDATA[wxaec0aa10e3f127b1]]></appid>
<attach><![CDATA[dxl]]></attach>
<bank_type><![CDATA[OTHERS]]></bank_type>
<cash_fee><![CDATA[1]]></cash_fee>
<fee_type><![CDATA[CNY]]></fee_type>
<is_subscribe><![CDATA[Y]]></is_subscribe>
<mch_id><![CDATA[1601549052]]></mch_id>
<nonce_str><![CDATA[1597458498473]]></nonce_str>
<openid><![CDATA[opTqewHXdEcXNoOd8JdTvzuch790]]></openid>
<out_trade_no><![CDATA[20200815101828]]></out_trade_no>
<result_code><![CDATA[SUCCESS]]></result_code>
<return_code><![CDATA[SUCCESS]]></return_code>
<sign><![CDATA[F74FD7F293CCD1061C29781CBBF1848F]]></sign>
<time_end><![CDATA[20200815102824]]></time_end>
<total_fee>1</total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<transaction_id><![CDATA[4200000586202008157697754886]]></transaction_id>
</xml>

frp 调试

https://javen205.gitee.io/ijpay/guide/tools/frp

wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz
tar -zxvf frp_0.33.0_linux_amd64.tar.gz
tee /alidata/server/frp_0.33.0_linux_amd64/frps.ini <<-'EOF'
[common]
bind_port = 7900
#由于80端口已暂用这里我们使用Nginx做端口映射到80端口来做微信开发的调试,如何映射后文会介绍
vhost_http_port = 5010
#连接池
max_pool_count = 5
#token验证
privilege_token = javen
#自定义二级域名
subdomain_host = cloud.ling2.cn
#控制面板
dashboard_port = 7901
dashboard_user = javen
dashboard_pwd = javen

#日志
log_file = ./frps.log
log_level = info
log_max_days = 3
EOF
cd /alidata/server/frp_0.33.0_linux_amd64
 ./frps -c ./frps.ini


admin_addr = 39.98.201.16
admin_port = 7400
admin_user = admin
admin_pwd = admin
[common]
# 服务器IP
server_addr = 39.98.201.16
# 服务器bind_port
server_port = 7900
privilege_token = javen

[wx]
type = http
# 映射到本地的8080端口
local_port = 5010
subdomain = wx

# 如果不使用SSH可以将其注释掉
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000

ling-cloud-ui

cd /alidata/dockerdata/ling-cloud-ui
git pull
n 12.22.1
tyarn install
tyarn run dev
tyarn run build
n 16.5.0
cd /ling-cloud/ling-cloud-ui
nohup npm run dev > /ling-cloud/logs/ling-cloud-ui.log 2> /dev/null &

linux 安装

安装nodejs Centos安装yarn

yum install -y nodejs
npm install -g yarn
npm install -g nrm
nrm ls #可以查看npm的数据源,通过nrm use xxx进行切换
nrm use taobao
yarn install
nohup npm run dev &

动态路由与路由守卫

  • 初始化动态路由 src/router/router.js
Router.$avueRouter.formatRoutes(Store.state.user.menu, true)
  • 路由守卫src/permission.js
router.beforeEach((to, from, next) => {
  // 缓冲设置
  if (to.meta.keepAlive === true && store.state.tags.tagList.some(ele => {
    return ele.value === to.fullPath
  })) {
    to.meta.$keepAlive = true
  } else {
    NProgress.start()
    if (to.meta.keepAlive === true && validatenull(to.meta.$keepAlive)) {
      to.meta.$keepAlive = true
    } else {
      to.meta.$keepAlive = false
    }
  }
  const meta = to.meta || {}
  if (store.getters.access_token) {
    if (store.getters.isLock && to.path !== lockPage) {
      next({ path: lockPage })
    } else if (to.path === '/login') {
      next({ path: '/' })
    } else {
      if (store.getters.roles.length === 0) {
        store.dispatch('GetUserInfo').then(() => {
          next()
        }).catch(() => {
          store.dispatch('FedLogOut').then(() => {
            next({ path: '/login' })
          })
        })
      } else {
        const value = to.query.src || to.fullPath
        const label = to.query.name || to.name
        if (meta.isTab !== false && !validatenull(value) && !validatenull(label)) {
          store.commit('ADD_TAG', {
            label: label,
            value: value,
            params: to.params,
            query: to.query,
            group: router.$avueRouter.group || []
          })
        }
        next()
      }
    }
  } else {
    if (meta.isAuth === false) {
      next()
    } else {
      next('/login')
    }
  }
})

router.afterEach(() => {
  NProgress.done()
  const title = store.getters.tag.label
  router.$avueRouter.setTitle(title)
})

水印

f12查看代码,在</body>的最上行代码中看到相关代码 如:

<div id="POjclrsXEPVfivgX" style="
            position:fixed;
            bottom:0;
            left:0;
            width:100%;
            height:40px;
            text-align:center;
            z-index:9999;
            pointer-events:none;">Philips IDP</div>

修改文件 public/cdn/avue/index.js 根据如pointer-events:none这样的关键字找到对应位置代码,修改对应的水印

默认pageindex

根据关键字 pageSizes:[10,20,30,40,50,100] 查找 修改文件 public/cdn/avue/index.js currentPage:1-->currentPage:0

sizeChange方法中的this.defaultPage.currentPage = 0;

pageInit方法中this.defaultPage.currentPage===1&&(this.defaultPage.currentPage=0)

编码规范

编码规范文件

文件:阿里巴巴Java开发规范1.4.pdf

文件:SQL 语句书写与性能调优规范.doc

文件:数据库设计规范 v1.0.docx

新项目XXX项目初始化规范

项目基本模块

  • tax-xxx
    • tax-xxx-api
    • tax-xxx-biz

包结构规范

根package

其中xxx小写

com.deloitte.atrapa.tax.xxx

job 所有实现定时任务的job

com.deloitte.atrapa.tax.xxx.job

constants 存放静态变量和eumn

com.deloitte.atrapa.tax.xxx.constants

utils 存放项目特殊的工具类,一般不需要自己添加,需要工具类时首先查询Hutool中是否有相关实现

com.deloitte.atrapa.tax.xxx.common.utils
模块包结构

如果有功能比如case

包结构如下: entity hibernate或mybatis映射关系

这里mapping的package必须为model,否则自动代码会有问题

详细内容 参考 Ling2编码规范#model定义

com.deloitte.atrapa.tax.xxx.case.model

dao 实现dao访问

com.deloitte.atrapa.tax.xxx.case.dao

service 服务

com.deloitte.atrapa.tax.xxx.case.service

controller 控制器和restful api

com.deloitte.atrapa.tax.xxx.case.controller

入口Application规范 XXX大写

com.deloitte.atrapa.tax.xxx.XXXApplication
  • @EnableLingResourceServer和@EnableLingFeignClients必须同时使用,EnableLingFeignClients中包含了认证模块

内容例如:

package com.deloitte.atrapa.tax.cor;

import com.ling.cloud.common.security.annotation.EnableLingFeignClients;
import com.ling.cloud.common.swagger.annotation.EnableSwagger2;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;

/**
 * @author jasonbwang
 * COR管理系统
 */
@EnableSwagger2
@EnableTaxMicroServer
public class CORApplication {
    public static void main(String[] args) {
        SpringApplication.run(CORApplication.class, args);
    }

}

模板项目下载

数据库建模规范

文件:税务管理系统项目-数据库设计规范 v1.0.docx

model规范

com.deloitte.atrapa.tax.xxx.model

这里mapping的package必须为model,否则自动代码会有问题 详细内容 参考 Ling2编码规范#model定义

entity model,VO规范

  • VO包括和entity对应的数据库VO和负责前端页面需要返回或传递的复杂页面对象
  • 本规范尽量简单些.就model(entity)和VO,因为我们其实主要是通过controller向vue供数,feign用的也是controller的接口,所以我觉得VO比较合适,因此简单化为model(entity)和VO
  • 更多参考阿里云相关规范Vo,dto.PNG

自动生成 dao, service ,controller

参考 ling-cloud-builder

maven pom规范

方法的get,post,patch,delete使用规范

跨服务接口的编写

  • 避免maven层面的循环依赖,避免微服务层面的循环依赖
  • 通过@FeignClient定义RemoteService,并指明这个服务在哪个微服务提供
  • 定义Service通过调用RemoteService或redis等实现bean
  • 在src\main\resources\META-INF\spring.factories中注册spring 启动时需要初始化的bean
  • 参考UserDetailsServiceImpl

Lombok使用

多使用Lombok

@Transactional

@Transactional(rollbackFor = Exception.class)

RequestContextHolder

HttpServletRequest request = ((ServletRequestAttributes) Objects .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

SecurityContextHolder,TaxContextHolder,TaxContextUtils,TaxContextHolderFilter

  • 用户获取当前request和reponse上下文,获取当前用户信息,主要有TaxContextHolderFilter和SecurityContextHolder扩展支持实现

TaxContextHolder

	/**
	 * 获取客户端
	 *
	 * @return clientId
	 */
	private String getClientId() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication instanceof OAuth2Authentication) {
			OAuth2Authentication auth2Authentication = (OAuth2Authentication) authentication;
			return auth2Authentication.getOAuth2Request().getClientId();
		}
		return null;
	}

日志级别

  • 主要配置在ling-cloud-common-core的logback-spring.xml
  • 各自项目的bootstrap.yml中
  • 简单的修改日志级别为从ling-cloud-common-core中复制logback-spring.xml到项目,修改logback-spring.xml中的${logging.level}为7

Logback-spring.PNG

# Logger Config
logging:
  level:
    com.ling.cloud.admin.mapper: debug

Feign Remote服务

  • 入口程序使用@EnableLingFeignClients注解
  • 命名RemoteXXXService
  • 内容放在与service等同级别的feign中
  • controller接口需要添加@Inner
	 /**
	 * 获取用户的角色集合
	 *
	 * @param userName
	 * @return
	 */
	@Inner
	@GetMapping("/getUserRoles/{userName}")
	public R<List<String>> getUserRoles(@PathVariable String userName) {
		SysUserVO userVO = userMapper.getUserVoByUsername(userName);
		List<String> result = new ArrayList<>();
		if (userVO != null && userVO.getRoleList() != null) {
			for (SysRoleVO role : userVO.getRoleList()) {
				result.add(role.getRoleCode());
			}
		}
		return R.ok(result);
	}
  • ServiceNameConstants中添加service-id静态变量
@FeignClient(contextId = "remoteUserService", value = ServiceNameConstants.BASE_SERVICE)
public interface RemoteUserService {
    /**
     * 获取用户的角色集合
     *
     * @param userName
     * @return
     */
    @GetMapping("/user/getUserRoles/{userName}")
    R<List<String>> getUserRoles(String userName, @RequestHeader(SecurityConstants.FROM) String from);
}
  • 调用示例,内部不鉴权参数SecurityConstants.FROM_IN
    @Override
    @Cacheable(value = CacheConstants.USER_CACHE_DETAILS, key = "#userName  + '_byUserName'", unless = "#result == null")
    public SysUserVO getUserByUserName(String userName) {
//        return remoteUserService.info(userName, SecurityConstants.FROM_IN).getData().getSysUser();
        return remoteUserService.getUserByUserName(userName, SecurityConstants.FROM_IN).getData();
    }

@Around

更多参考@Around @Aspect

LingSecurityInnerAspect,TransactionAspect,SysLogAspect,RequestAspect

	@SneakyThrows
	@Around("@annotation(inner)")
	public Object around(ProceedingJoinPoint point, Inner inner) {
		String header = request.getHeader(SecurityConstants.FROM);
		if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_IN, header)) {
			log.warn("访问接口 {} 没有权限", point.getSignature().getName());
			throw new AccessDeniedException("Access is denied");
		}
		return point.proceed();
	}



	@SneakyThrows
	@Around("@annotation(com.codingapi.tx.annotation.TxTransaction)")
	public Object transactionRunning(ProceedingJoinPoint point) {
		log.debug("annotation-TransactionRunning-start---->");
		Object obj = txManagerInterceptor.around(point);
		log.debug("annotation-TransactionRunning-end---->");
		return obj;
	}

	@SneakyThrows
	@Around("this(com.codingapi.tx.annotation.ITxTransaction) && execution( * *(..))")
	public Object around(ProceedingJoinPoint point) {
		log.debug("interface-ITransactionRunning-start---->");
		Object obj = txManagerInterceptor.around(point);
		log.debug("interface-ITransactionRunning-end---->");
		return obj;
	}

	@SneakyThrows
	@Around("@annotation(sysLog)")
	public Object around(ProceedingJoinPoint point, SysLog sysLog) {
		String strClassName = point.getTarget().getClass().getName();
		String strMethodName = point.getSignature().getName();
		log.debug("[类名]:{},[方法]:{}", strClassName, strMethodName);

		SysLogVo logVo = SysLogUtils.getSysLog();
		logVo.setTitle(sysLog.value());
		// 发送异步日志事件
		Long startTime = System.currentTimeMillis();
		Object obj = point.proceed();
		Long endTime = System.currentTimeMillis();
		logVo.setTime(endTime - startTime);
		publisher.publishEvent(new SysLogEvent(logVo));
		return obj;
	}

linq

https://github.com/muxiangqiu/linq

SpringMVC常用注解@Controller,@Service,@repository,@Component

  • controller层使用@controller注解
  • service采用@service注解
  • dao层使用@repository注解
  • mapper层使用@Component注解

@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法。通俗来说,被Controller标记的类就是一个控制器,这个类中的方法,就是相应的动作。

  1. @controller 控制器(注入服务)
  2. @service 服务(注入dao)
  3. @repository dao(实现dao访问)
  4. @component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)

@SneakyThrows

@SneakyThrows

powerdesigner建模规范

创建逻辑模型-->创建表(XXX_)-->创建关系(REL_)-->设置主键-->设置主键名称(PK_)-->设置外键名称(FK_)由逻辑模型创建物理模型

自动生成表结构

 jpa:
   show-sql: true
   hibernate:
     ddl-auto: update

ERMaster

前端api和controller方法命名规范

  • 前端CorCaseEmailService.js==CorCaseEmailController.Java==@RequestMapping("/corCaseEmail")
  • 前端api方法==后端controller方法==restful方法

代码commit与代码格式化

  • 不推荐整个文件ctrl+alt+L
  • 选中自己的代码然后ctrl+alt+L

assert

assert tokens.length == 2;

缓存使用

已经默认提供基于redis的缓存机制,无需在子系统自定义cachemanager,否则或导致基于spring security的oauth机制失效

添加缓存

@Cacheable(value = "menu_details", key = "#roleId  + '_menu'")

清除缓存

@CacheEvict(value = "menu_details", allEntries = true)
@CacheEvict(value = SecurityConstants.CLIENT_DETAILS_KEY, key = "#clientDetails.clientId")
	@Override
	@Cacheable(value = "menu_details", key = "#roleId  + '_menu'")
	public List<MenuVO> findMenuByRoleId(Integer roleId) {
		return baseMapper.listMenusByRoleId(roleId);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@CacheEvict(value = "menu_details", allEntries = true)
	public R removeMenuById(Integer id) {
		// 查询父节点为当前节点的节点
		List<SysMenu> menuList = this.list(Wrappers.<SysMenu>query()
				.lambda().eq(SysMenu::getParentId, id));
		if (CollUtil.isNotEmpty(menuList)) {
			return R.builder()
					.code(CommonConstants.FAIL)
					.msg("菜单含有下级不能删除").build();
		}

		sysRoleMenuMapper
				.delete(Wrappers.<SysRoleMenu>query()
						.lambda().eq(SysRoleMenu::getMenuId, id));

		//删除当前菜单及其子菜单
		return new R(this.removeById(id));
	}

	@Override
	@CacheEvict(value = "menu_details", allEntries = true)
	public Boolean updateMenuById(SysMenu sysMenu) {
		return this.updateById(sysMenu);
	}

关闭mongodb

spring:

 autoconfigure:
   exclude:
     - org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
     - org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration

分布式锁RedisLock

RedisLock

@Inner @Task @Client注解的使用规范

@Inner

适用于不需要登陆就能访问的controller方法,比如登陆接口

注意,在添加了此注解的调用链中,不能有获取当前用户的代码

如果发现机制不生效,请删除本地的apiSecurityConfig类然后push,然后在ibiz上重新发布代码后重新生成此文件

@Task

适用于定时任务这类不能登陆,但调用链中的方法需要登陆的controller方法

系统会自动使用admin用户执行此方法

@Client

适用于第三方系统调用的controller方法,此类方法执行前会校验header中client-token的值是否存在且是否正确

参考示例

    @Task
    @Client
    @Inner
    @Transactional
//    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "获取accessToken,返回一串UUID")
    @GetMapping("/getAccessTokenTest")
    public ResponseEntity<String> getAccessTokenTest() {
        log.info("11111111111111");
        return ResponseEntity.ok("token");
    }

相关代码

@Client-->ClientAspect

@Task-->TaskAspect,FeignRequestInterceptor

@Inner-->SecurityInnerAspect,apiSecurityConfig

client-token的获取和@client使用

header的值不能又_下划线,不然有的不支持,用-可以

@Client适用于第三方系统调用的controller方法,此类方法执行前会校验header中client-token的值是否存在且是否正确

client-token从产生到失效为10分钟

client-token在10分钟内可以重复使用,但还是需要调用前通过getAccessToken获取client-token,保证client-token的有效性

预设值

假定服务提供者的url为http://127.0.0.1:3001

clientCode,clientSecret的申请

T_SYS_CLIENT表中录入相关信息或通过对应界面配置相关信息

获取access_token

将上一步申请到的clientCode和clientSecret调用getAccessToken获取access_token

curl --location --request POST 'http://39.98.126.59:30001/openapi/client/getAccessToken' \
--header 'Content-Type: application/json' \
--data-raw '{
    "clientCode": "clientCode",
    "clientSecret": "clientSecret"
}'

使用access_token

将上步getAccessToken中获取的的client-token放到请求的header中,key为client-token

curl --location --request GET 'http://39.98.126.59:30001/openapi/client/getAccessTokenTest' \
--header 'client-token: aefa12b7-b2f3-4750-a914-42f6cd65ec38'

@client相关代码

AccessTokenService,ClientAspect,ClientController

查询加速 Elasticsearch

https://www.yuque.com/ibiz/xgitp0/toh060

@Aspect
@Component
public class ESAspect
{
    @Autowired
    IHumanService esService;

    @AfterReturning(value = "execution(* net.ibizsys.sample.mng.core.*.service.*.create(..)) ||execution(* net.ibizsys.sample.mng.core.*.service.*Human*.update*(..)) ....")
    @Async
    public void SyncHuman(JoinPoint point) {
        syncSaveESData(point,"Human");
    }
    
    /**
     * 异步往es中保存数据
     * @param point
     */
    public void syncSaveESData(JoinPoint point, String deName) {
    	//获取实体服务对象,将数据存入es
    }
}

类似TAX已有系统用户,权限和菜单的接入

修改已有代码(已完成)

  • 将remote接口抽象成接口XxxService
  • 修改代码调用XxxService接口
  • 实现XxxService接口,设置condition 默认调用remoteService

对接系统实现

  • 实现接口 LogService,UserService

微服务间联调

https://spring.io/guides/gs/client-side-load-balancing/

  • 配置发起调用服务的listOfServers(参考如下)
  • 启动发起调用服务
  • 启动被调用服务
tax-doc-biz:
  ribbon:
    eureka:
      enabled: false
    listOfServers: localhost:8097
    ServerListRefreshInterval: 15000

tax-core-biz:
  ribbon:
    eureka:
      enabled: false
    listOfServers: localhost:8090,localhost:9092,localhost:9999
    ServerListRefreshInterval: 15000

提供服务给第三方

  • 单独的controller
  • 设置controller的权限 例如- /tirdparty/* 可以自由调用
  • 设置为内部访问
@RequestMapping(value = "/tryLdapLogin", method = RequestMethod.GET)
boolean tryLdapLogin(@RequestParam(value="userName")  String userName, @RequestParam(value="password")  String password,@RequestHeader(SecurityConstants.FROM) String from);
  • 实现上添加@Inner
   @Inner
   @RequestMapping(value = "/tryLdapLogin", method = RequestMethod.GET)
   public boolean tryLdapLogin(@RequestParam(value="userName")  String userName, @RequestParam(value="password")  String password) {
      return ldapService.authenticate(userName,password);
   }

api 单元测试

postman

https://www.getpostman.com/

Ling-cloud-postman-token.PNG

java junit

接口权限

@PreAuthorize("@pms.hasPermission('sys_menu_add')")

nginx 和api版本管理

location ^~/api/ver2.0/ {         
                       proxy_http_version 1.1;
           proxy_set_header   Connection       "";
           proxy_set_header   Host             $host;
           proxy_set_header   X-Real-IP        $remote_addr;
           proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
                       rewrite ^/api/ver2.0/(.*)$ /$1 break;
           proxy_pass http://10.172.64.204:11999;
        }

临时修改配置中心

spring:
  application:
    name: @artifactId@
  # 配置中心
  cloud:
    config:
      fail-fast: true
      name: ${spring.application.name}
      profile: ${spring.profiles.active}
      uri: http://127.0.0.1:11998

自定义路由,限流

KeyResolver

	@Bean(value = "remoteAddrKeyResolver")
	public KeyResolver remoteAddrKeyResolver() {
		return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
	}

NFLoadBalancerRuleClassName

ribbon.loadbalanced.services=tax-basic,tax-core,tax-ptc,tax-doc
ribbon.NFLoadBalancerRuleClassName=com.deloitte.atrapa.tax.main.config.RoundRobinRuleExt
package com.deloitte.atrapa.tax.main.config;

import com.deloitte.atrapa.tax.common.utils.IPUtil;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by quazhang@deloitte.com.cn on 27/11/2018.
 */
public class RoundRobinRuleExt extends AbstractLoadBalancerRule {

    private AtomicInteger nextServerCyclicCounter;
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    private static final boolean ALL_SERVERS = false;
    private static final String LOCAL = "local";
    private static final String DEV = "dev";
    private static final String LOCALHOST_LANADDRESS = IPUtil.getLocalHostLANAddress().getHostAddress();
    private static Logger log = LoggerFactory.getLogger(RoundRobinRuleExt.class);

    public RoundRobinRuleExt() {
        this.nextServerCyclicCounter = new AtomicInteger(0);
    }

    public RoundRobinRuleExt(ILoadBalancer lb) {
        this();
        this.setLoadBalancer(lb);
    }

    public Server choose(ILoadBalancer lb, Object key) {

        List<Server> reachableServers = lb.getReachableServers();
        List<Server> allServers = lb.getAllServers();

        String env = System.getProperty("spring.profiles.active");
        if ((LOCAL.equals(env) || DEV.equals(env)) && reachableServers.size() != 0 ){
            for(Server serv : allServers){
                if (LOCALHOST_LANADDRESS.equals(serv.getHost())){
                    return serv;
                }
            }
        }

        if (lb == null) {
            log.warn("no load balancer");
            return null;
        } else {
            Server server = null;

            int count = 0;

            while(true) {
                if (server == null && count++ < 10) {

                    int upCount = reachableServers.size();
                    int serverCount = allServers.size();
                    if (upCount != 0 && serverCount != 0) {
                        int nextServerIndex = this.incrementAndGetModulo(serverCount);
                        server = (Server)allServers.get(nextServerIndex);
                        if (server == null) {
                            Thread.yield();
                        } else {
                            if (server.isAlive() && server.isReadyToServe()) {
                                return server;
                            }

                            server = null;
                        }
                        continue;
                    }

                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }

                if (count >= 10) {
                    log.warn("No available alive servers after 10 tries from load balancer: " + lb);
                }

                return server;
            }
        }
    }

    private int incrementAndGetModulo(int modulo) {
        int current;
        int next;
        do {
            current = this.nextServerCyclicCounter.get();
            next = (current + 1) % modulo;
        } while(!this.nextServerCyclicCounter.compareAndSet(current, next));

        return next;
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

环境搭建

docker环境搭建参考

更多参考Docker常用服务#Docker network 替代--link Docker常用服务#创建manager节点集群和从节点

docker swarm init --advertise-addr 101.132.191.22
docker swarm join --token SWMTKN-1-63qiic7iky80zk6ucpwm14jyct7msaj3zlvgmh6jf5gbz6uapz-1346w49idfnrg0atywy10yrzd 101.132.191.22:2377

张家口

docker swarm init --advertise-addr 39.98.201.16
docker swarm join --token SWMTKN-1-3rpwuht9l05u5lu42fbq31m8p8z2tvxr2x6vh4dee19kjyan4w-4v572c86d0koal9cbe8ixcye1 39.98.201.16:2377
docker swarm join-token manager
docker node ls

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-2f05hbhghrznn8wqtjq4hjqrss33mat9sg9i7mz4qsll1ify1u-6dkdaagml1kn5wq8znlrscnox 172.19.107.252:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

docker network create overlay --driver --attachableling-cloud
docker network create -d overlay --attachable ling-cloud

mysql

docker run --name ling-cloud-mysql-base -p 3306:3306 --restart=always --network ling-cloud --network-alias ling-cloud-mysql -v /alidata/dockerdata/mysqldata/ling-cloud-mysql-base:/var/lib/mysql \
-v /alidata/dockerdata/mysqldata/ling-cloud-mysql-base.cnf:/etc/mysql/conf.d/config-file.cnf \
-e MYSQL_ROOT_PASSWORD=root -d ling-cloud-mysql-base


docker run --name ling-cloud-mysql-base -p 3306:3306 --restart=always --network ling-cloud --network-alias ling-cloud-mysql -v /alidata/dockerdata/mysqldata/ling-cloud-mysql-base:/var/lib/mysql \
-v /alidata/dockerdata/mysqldata/ling-cloud-mysql-base.cnf:/etc/mysql/conf.d/config-file.cnf \
 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.16

修改大小写

docker exec -it ling-cloud-mysql-base /bin/bash
tee /etc/mysql/mysql.conf.d/mysqld.cnf <<-'EOF'

# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

#
# The MySQL  Server configuration file.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
#log-error      = /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address   = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
lower_case_table_names=1

EOF
tee /alidata/dockerdata/mysqldata/ling-cloud-mysql-base.cnf <<-'EOF'
[mysqld]
lower_case_table_names=1

EOF

redis

docker run --name ling-cloud-redis --restart=always --network ling-cloud --network-alias ling-cloud-redis  -p 6379:6379 -d redis:4.0.10

ling-cloud

docker run --name dev_ling-cloud-config_8889 -p 8889:8888 -d  -e "server.port=3000" -e "eureka.client.service-url.defaultZone=http://pig:pig@101.132.191.22:8761/eureka/" ling-cloud-config
docker run --name dev_ling-cloud-base-biz_4000 -p 4000:4000 -d -v /ling-cloud/logs:/logs -e eureka.client.service-url.defaultZone=http://pig:pig@101.132.191.22:8761/eureka/ -e spring.cloud.config.uri=http://server.ling2.cn:8889,http://server.ling2.cn:8888 -e logging.path=/logs/ling-cloud-base-biz/dev_ling-cloud-base-biz_4000 --link ling-cloud-mysql-base:ling-cloud-mysql --link redis:ling-cloud-redis ling-cloud-base-biz
docker run -d -p 3306:3306 --name wp-mysql --network wp-net --network-alias mysql -e MYSQL_ROOT_PASSWORD=123 mysql
  • docker run:启动容器
  • -d:后台运行
  • -p 3306:3306:将容器的3306端口映射到宿主机的3306端口上
  • --name wp-mysql:指定容器的名称为wp-mysql
  • --network wp-net:将容器加入到wp-net网络中
  • --network-alias mysql:指定容器在wp-net网络中的别名是mysql
  • -e MYSQL_ROOT_PASSWORD=123:初始化数据库root用户的密码为123

jenkins

简单版本

docker stop ${JOB_NAME}
docker rm -f ${JOB_NAME}
docker rmi ${JOB_NAME}
cd /ling-cloud/${JOB_NAME}
docker build -t ${JOB_NAME}:latest .
docker run --name ${JOB_NAME} --link ling-cloud-eureka -p 8888:8888 -d ${JOB_NAME}

支持多实例版本

env=dev
service_name=${JOB_NAME}
service_port=8888
env_service_name=${env}_${service_name}
ipAddress=101.132.191.22
deploy_date=$(date +%Y%m%d%H%M)
eureka_client_service_url_defaultZone="http://pig:pig@101.132.191.22:8761/eureka/"

n=1
for((i=0;i<=n-1;i++));  
do   
       instance_port=$[ ${service_port} + i ]
       docker_name=${env_service_name}_${instance_port}
       echo docker stop ${docker_name}
       docker stop ${docker_name}
       sleep 3
       echo docker rm -f ${docker_name}
       docker rm -f ${docker_name}
       echo ps -ef|grep ${env_service_name}|grep ${instance_port}|grep -v grep|cut -c 9-15|xargs kill -9
       ps -ef|grep ${env_service_name}|grep ${instance_port}|grep -v grep|cut -c 9-15|xargs kill -9
       sleep 3
done 

docker rmi ${JOB_NAME}
cd /ling-cloud/${JOB_NAME}
docker build -t ${JOB_NAME}:latest .


for((i=0;i<=n-1;i++));  
do   
       instance_port=$[ ${service_port} + i ]
       docker_name=${env_service_name}_${instance_port}
       echo ${docker_name}
       echo docker run --name ${docker_name} -p ${instance_port}:${service_port} -d  -v /ling-cloud/logs:/logs -e "eureka.client.service-url.defaultZone=${eureka_client_service_url_defaultZone}"  -e "logging.path=/logs/${JOB_NAME}/${docker_name}" ${JOB_NAME}
       docker run --name ${docker_name} -p ${instance_port}:${service_port} -d  -v /ling-cloud/logs:/logs -e "eureka.client.service-url.defaultZone=${eureka_client_service_url_defaultZone}"  -e "logging.path=/logs/${JOB_NAME}/${docker_name}" ${JOB_NAME}
       sleep 5
done

nacos

https://gitee.com/ling2/nacos-spring-boot-project

监控

https://nacos.io/zh-cn/docs/monitor-guide.html

Nacos配置mysql数据库 https://blog.csdn.net/qq_28851503/article/details/88767286


项目部署

网络环境隔离

  • 非docker环境下请忽略
docker network create --driver overlay ling-cloud

版本管理

  • 给其他项目使用后,需要做一个版本号的分支,比如 version3.2-SNAPSHOT 用于deloitte覆盖回来获取bug修改的改进 为当前的稳定分支
  • 一个时间最多有一个改进分支,为当前工作分支,定时从稳定分支合并代码
  • 合并pigx代码在分支merge_pigx 定时从工作分支合并代码,稳定后再回到工作分支

ename cname

1.x DefaultUserDetails,AuthorizationServerConfig,SysUserVO,DefaultUserDetailsServiceImpl

3.x DefaultUserDetails,SecurityConstants,SysUserVO,SecurityEntityOperation,DefaultUserDetailsServiceImpl,SecurityUtils

问题解决

monitor中能找到服务,但为down状态还只能看到很少东西

MonitorApplication中

@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
@EnableAdminServer

-->

@EnableAdminServer
@SpringBootApplication


BaseServiceApplication中

@EnableDiscoveryClient
@SpringBootApplication

-->

@EnablePigxSwagger2
@SpringCloudApplication
@EnablePigxFeignClients

AuthServerApplication中

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients

-->

@SpringCloudApplication
@EnablePigxFeignClients

找不到RemoteTokenService

@Configuration
@AllArgsConstructor
@ConditionalOnProperty("security.oauth2.client.client-id")
public class FeignClientConfiguration {
.......
}


## spring security 配置
security:
  oauth2:
    client:
      client-id: ENC(ltJPpR50wT0oIY9kfOe1Iw==)
      client-secret: ENC(ltJPpR50wT0oIY9kfOe1Iw==)
      scope: server
      # 默认放行url,子模块重写时application-dev.yml中的公共配置会被覆盖,所以要把公共配置中的放行url再写一次
      ignore-urls:
        - /actuator/**
        - /v2/api-docs
        - /user/info/*
        - /social/info/*
        - /mobile/*
        - /log/save

String Encryptor custom Bean not found with name 'jasyptStringEncryptor'. Initializing Default String Encryptor

# 加解密根密码
jasypt:
  encryptor:
    password: pigx #根密码

RedisTemplateConfig.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.class] and overriding is disabled

spring.main.allow-bean-definition-overriding=true

maven jar包不更新

  • maven 文件目录删除相关目录

  • 停止应用时做 re import 不停止没用的

debug启动内容很少,直接起不来

01-15 18:22:49.009 DEBUG [c.u.j.c.EnableEncryptablePropertiesBeanFactoryPostProcessor] - Application Event Raised: ApplicationFailedEvent
Disconnected from the target VM, address: '127.0.0.1:63231', transport: 'socket'

改log级别后问题变为 spring boot 启动失败,不报错No active profile set, falling back to default profiles: default

ling-cloud-common-security/src/main/resources/META-INF/spring.factories问题导致的,所以出现这个问题要去检查其依赖模块spring.factories中是否存在问题

参考spring boot 启动失败,不报错No active profile set, falling back to default profiles: default

spring boot 启动失败,不报错No active profile set, falling back to default profiles: default

Index: ling-cloud-common/ling-cloud-common-security/src/main/resources/META-INF/spring.factories
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>GBK
===================================================================
--- ling-cloud-common/ling-cloud-common-security/src/main/resources/META-INF/spring.factories	(revision d5825b5409f61cc27e40b009b489a39862429dc9)
+++ ling-cloud-common/ling-cloud-common-security/src/main/resources/META-INF/spring.factories	(date 1547701246443)
@@ -1,5 +1,3 @@
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-  com.ling.cloud.common.security.filter.TenantContextHolderFilter,\
   com.ling.cloud.common.security.feign.PigxFeignClientConfiguration,\
-  com.ling.cloud.common.security.feign.PigxFeignTenantConfiguration,\
   com.ling.cloud.common.security.service.PigxUserDetailsServiceImpl

"http://ling-cloud-auth/oauth/check_token": Connection timed out

貌似ling-cloud-auth启动好等一会就好了

01-20 00:33:49.609 ERROR [io.undertow.request] - UT005022: Exception generating error page /error
java.lang.RuntimeException: java.lang.IllegalStateException: No instances available for ling-cloud-auth
        at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:485)
        at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:412)
        at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:319)
        at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
        at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
        at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
        at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
        at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
        at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
        at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
        at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
        at io.undertow.server.Connectors.executeRootHandler(Connectors.java:336)
        at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: No instances available for ling-cloud-auth
        at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:89)
        at org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor.intercept(LoadBalancerInterceptor.java:55)
        at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:92)
        at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:76)
        at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
        at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:687)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:644)
        at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:564)
        at org.springframework.security.oauth2.provider.token.RemoteTokenServices.postForMap(RemoteTokenServices.java:147)
        at org.springframework.security.oauth2.provider.token.RemoteTokenServices.loadAuthentication(RemoteTokenServices.java:106)
        at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager.authenticate(OAuth2AuthenticationManager.java:83)
        at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:150)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
        at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
        at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
        at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
        at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
        at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:65)
        at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
        at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
        at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
        at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:274)
        at io.undertow.servlet.handlers.ServletInitialHandler.dispatchToPath(ServletInitialHandler.java:209)
        at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:479)
        ... 15 common frames omitted

一切都能启动,前端503错误

开启common-core中开启debug模式发现 链接的数据库错误,动态路由配置不符合要求,所以操

intellijj小改动需要重启spring boot服务

devkit插件安装并启用

The header content contains invalid characters

General SSLEngine problem

https://segmentfault.com/a/1190000017155814 配置信任所有下游证书 网关路由可以同时支持路由到http和https的后端服务,如果路由到Https的后端,通过以下配置,网关可以设置为信任具有以下配置的所有下游证书:

application.yml.

spring:  
 cloud:    
   gateway:      
     httpclient:        
       ssl:          
         useInsecureTrustManager: true

Ambiguous handler methods mapped for

@GetMapping("/info/{username}")和@GetMapping("/info/{inStr}")冲突

Upgrade Required

  • ENCRYPTION_KEY和ling-cloud-gateway-dev.yml中的security.encode.key不匹配
  • 登录不能直接走auth服务,必须走gateway转发

认证过程中出现Auth ErrorError: Upgrade Required?

这个不用怀疑,原因一般不外乎三个。

  • 用户名或密码错误

可以打开谷歌开发者工具,观察request详情和response详情以及返回的状态码,如果是426的话,就证明获取用户信息的时候失败了,可以判断是作为缓存中间件的redis并没有启动,那么只要启动redis,另一个原因是redis中有脏数据,这个时候清空redis即可。清空的具体步骤如下: windows平台下可以打开redis-cli.exe,然后执行flushdb或者flushall命令即可。

如果是ldap登录,用户表的密码必须是加密后的123456

  • 使用了需要验证码的客户端

除了上面的原因,还有可能返回428的状态码,而会出现这个问题就是使用了需要验证码的客户端。

  • 跨域

排除所有不可能,剩下的那个不管多不可思议,都是事实真相。除开这两个原因,还有可能会出问题的,只有一种情况,那就是出现了跨域问题。 如果本地启动出现了问题,可以观察请求头里是否存在跨域,如果是OPTIONS请求基本就是跨域了。 在目前的项目机制下,开发时解决跨域的最简单的一个方案就是不要通过http://localhost:9999/swagger-ui.html或者http://127.0.0.1:9999/swagger-ui.html去访问网关上的swagger,而是直接通过http://pigx-gateway:9999/swagger-ui.html去访问,这样就能避免跨域的问题。


断点ResourceOwnerPasswordTokenGranter

Could not render e, see the console.

http://39.98.201.16:9999/swagger-resources内容为空

SwaggerProvider的配置问题

401 Unauthorized

  • 登录时/user/getToken添加到ignore-urls
      ignore-urls:
        - /actuator/**
        - /v2/api-docs
        - /user/info/*
        - /user/getToken
  • 头信息设置
    config.headers['isToken'] = false;
    config.headers['TENANT_ID'] = '1';
    config.headers['Authorization'] = 'Basic cGlnOnBpZw==';


ResourceAuthExceptionEntryPoint

SecurityUtils.getUser()为空

principal 为字符串

  • cache的userNmae为大写
if (principal instanceof DefaultUserDetails) {
			return (DefaultUserDetails) principal;
		}

could not acquire a semaphore for execution

服务死循环后熔断了

Basic Authentication Authorization header found

  • 前端注释
// config.headers['Authorization'] = 'Basic cGlnOnBpZw==';

Authentication failed: password does not match stored value

  • 加密方式或dao层方法没有取password

/auth/oauth/token?username= {"code":1,"msg":"Internal Server Error","data":"server_error"}

loadUserByUsername cache.clear();

serialVersionUID = 6775254084471734148, local class serialVersionUID = -3191682158440867530

重命名redis的dump.rdb,初始化base_oauth_client_details后解决

base_oauth_client_details 302 erro 登录后会往auth 的那个地址跳转

SecurityConstants-->AuthorizationServerConfig-->ClientDetailsServiceConfigurer


org.springframework.security.access.AccessDeniedException: Access is denied

auth 模块 设置为debug模式

Translating SQLException with SQL state 'S0002', error code '208', message [Invalid object name 'base_oauth_client_details'.]; SQL was [select id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove from base_oauth_client_details where id = ?] for task 


05-20 18:35:02.750 DEBUG [org.springframework.web.servlet.DispatcherServlet] - GET "/token/login", parameters={}
05-20 18:35:02.753 DEBUG [o.s.w.s.m.m.a.RequestMappingHandlerMapping] - Mapped to public org.springframework.web.servlet.ModelAndView com.ling.cloud.auth.endpoint.LingTokenEndpoint.require()
05-20 18:35:02.753 DEBUG [o.s.o.j.support.OpenEntityManagerInViewInterceptor] - Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
05-20 18:35:02.754 DEBUG [o.s.w.servlet.view.ContentNegotiatingViewResolver] - Selected '*/*' given [application/json, text/plain, */*]
05-20 18:35:02.755 DEBUG [o.s.web.servlet.view.freemarker.FreeMarkerView] - View name 'ftl/login', model {}
05-20 18:35:02.755 DEBUG [o.s.web.servlet.view.freemarker.FreeMarkerView] - Rendering [ftl/login.ftl]
05-20 18:35:02.762 DEBUG [o.s.security.web.header.writers.HstsHeaderWriter] - Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@2d8f5772
05-20 18:35:02.763 DEBUG [o.s.s.w.c.HttpSessionSecurityContextRepository] - SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
05-20 18:35:02.764 DEBUG [o.s.o.j.support.OpenEntityManagerInViewInterceptor] - Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
05-20 18:35:02.764 DEBUG [org.springframework.web.servlet.DispatcherServlet] - Completed 200 OK
CREATE TABLE base_oauth_client_details (
                                           ID varchar(36) NOT NULL,
                                           resource_ids varchar(256) DEFAULT NULL,
                                           client_secret varchar(256) DEFAULT NULL,
                                           scope varchar(256) DEFAULT NULL,
                                           authorized_grant_types varchar(256) DEFAULT NULL,
                                           web_server_redirect_uri varchar(256) DEFAULT NULL,
                                           authorities varchar(256) DEFAULT NULL,
                                           access_token_validity int DEFAULT NULL,
                                           refresh_token_validity int DEFAULT NULL,
                                           additional_information varchar(4096) DEFAULT NULL,
                                           autoapprove varchar(256) DEFAULT NULL,
                                           tenant_id varchar(36) NOT NULL DEFAULT '0',
                                           archive_base_date datetime DEFAULT NULL,
                                           creation_date datetime DEFAULT NULL,
                                           created_by varchar(50) DEFAULT NULL,
                                           created_by_en_name varchar(150) DEFAULT NULL,
                                           created_by_name varchar(150) DEFAULT NULL,
                                           data_owner_code varchar(36) DEFAULT NULL,
                                           del_flag varchar(10) DEFAULT NULL,
                                           deleted_by varchar(36) DEFAULT NULL,
                                           deletion_date datetime DEFAULT NULL,
                                           last_updated_by varchar(50) DEFAULT NULL,
                                           last_updated_by_enname varchar(150) DEFAULT NULL,
                                           last_updated_by_name varchar(150) DEFAULT NULL,
                                           last_update_date datetime DEFAULT NULL,
                                           operation_org_code varchar(36) DEFAULT NULL,
                                           status varchar(10) DEFAULT NULL,
                                           sub_system varchar(100) DEFAULT NULL,
                                           record_version int DEFAULT NULL,
                                           PRIMARY KEY (ID)
)

insert into base_oauth_client_details (ID, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, tenant_id, archive_base_date, creation_date, created_by, created_by_en_name, created_by_name, data_owner_code, del_flag, deleted_by, deletion_date, last_updated_by, last_updated_by_enname, last_updated_by_name, last_update_date, operation_org_code, status, sub_system, record_version) values('app',NULL,'app','server','password,refresh_token',NULL,NULL,NULL,NULL,NULL,'true','1',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
insert into base_oauth_client_details (ID, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, tenant_id, archive_base_date, creation_date, created_by, created_by_en_name, created_by_name, data_owner_code, del_flag, deleted_by, deletion_date, last_updated_by, last_updated_by_enname, last_updated_by_name, last_update_date, operation_org_code, status, sub_system, record_version) values('daemon',NULL,'daemon','server','password,refresh_token',NULL,NULL,NULL,NULL,NULL,'true','1',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
insert into base_oauth_client_details (ID, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, tenant_id, archive_base_date, creation_date, created_by, created_by_en_name, created_by_name, data_owner_code, del_flag, deleted_by, deletion_date, last_updated_by, last_updated_by_enname, last_updated_by_name, last_update_date, operation_org_code, status, sub_system, record_version) values('gen',NULL,'gen','server','password,refresh_token',NULL,NULL,NULL,NULL,NULL,'true','1',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
insert into base_oauth_client_details (ID, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, tenant_id, archive_base_date, creation_date, created_by, created_by_en_name, created_by_name, data_owner_code, del_flag, deleted_by, deletion_date, last_updated_by, last_updated_by_enname, last_updated_by_name, last_update_date, operation_org_code, status, sub_system, record_version) values('pig',NULL,'pig','server','password,refresh_token,authorization_code,client_credentials','http://localhost:4040/sso1/login,http://localhost:4041/sso1/login',NULL,NULL,NULL,NULL,'true','1',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
insert into base_oauth_client_details (ID, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, tenant_id, archive_base_date, creation_date, created_by, created_by_en_name, created_by_name, data_owner_code, del_flag, deleted_by, deletion_date, last_updated_by, last_updated_by_enname, last_updated_by_name, last_update_date, operation_org_code, status, sub_system, record_version) values('test',NULL,'test','server','password,refresh_token',NULL,NULL,NULL,NULL,NULL,'true','1',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);


select id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove from base_oauth_client_details where id = 'pig'

Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.

05-04 22:56:03.984 WARN  [o.s.s.oauth2.provider.token.RemoteTokenServices] - Null Client ID or Client Secret detected. Endpoint that requires authentication will reject request with 401 error.
05-04 22:56:03.984 DEBUG [org.springframework.web.client.RestTemplate] - HTTP POST http://tax-auth/oauth/check_token
05-04 22:56:03.985 DEBUG [org.springframework.web.client.RestTemplate] - Accept=[application/json, application/*+json]
05-04 22:56:03.985 DEBUG [org.springframework.web.client.RestTemplate] - Writing [{token=[09643ac1-a305-49d8-bdd3-27859ccd265f]}] as "application/x-www-form-urlencoded"
05-04 22:56:03.985 DEBUG [com.netflix.loadbalancer.ZoneAwareLoadBalancer] - Zone aware logic disabled or there is only one zone
05-04 22:56:04.829 DEBUG [org.springframework.web.client.RestTemplate] - Response 302 FOUND
05-04 22:56:04.830 DEBUG [o.s.s.web.context.SecurityContextPersistenceFilter] - SecurityContextHolder now cleared, as request processing completed
05-04 22:56:04.830 ERROR [o.a.c.c.C.[.[localhost].[/].[dispatcherServlet]] - Servlet.service() for servlet [dispatcherServlet] threw exception
java.lang.NullPointerException: null
	at org.springframework.security.oauth2.provider.token.RemoteTokenServices.loadAuthentication(RemoteTokenServices.java:108)

缺少配置

security:
  oauth2:
    client:
      client-id: ENC(ltJPpR50wT0oIY9kfOe1Iw==)
      client-secret: ENC(ltJPpR50wT0oIY9kfOe1Iw==)
      scope: server

或缺少

      ignore-urls:
        - /actuator/**
        - /v2/api-docs
        - /user/info/*
        - /user/getToken

InvalidClientException, Given client ID does not match authenticated client

C:\maven\repository\org\springframework\security\oauth\spring-security-oauth2\2.3.6.RELEASE\spring-security-oauth2-2.3.6.RELEASE.jar

org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken中打断点

字段是char(36)-->varchar(36)

Required request part ‘file’ is not present

使用Spring cloud Feign在后台服务之间调用传递Multipart无法传递的问题

前端后端传递的MultipartFile 的fieldName 要对应上为file

FileItem fileItem = new DiskFileItem("file", Files.probeContentType(f.toPath()), false, f.getName(),
					(int) f.length(), f.getParentFile());
			InputStream input = new FileInputStream(f);
			OutputStream os = fileItem.getOutputStream();
			try {
				IOUtils.copy(input, os);
				MultipartFile mulFile = new CommonsMultipartFile(fileItem);
				return taxDocProvider.uploadFile(mulFile, req.getFileId(),
						generateFileInfoJson(mulFile, prjId));

Servlet3SecurityContextHolderAwareRequestWrapper cannot be cast to MultipartHttpServletRequest

21 10:49:14.472 ERROR [o.a.c.c.C.[.[localhost].[/].[dispatcherServlet]] - Servlet.service() for servlet [dispatcherServlet] threw exception
java.lang.ClassCastException: org.springframework.security.web.servletapi.HttpServlet3RequestFactory$Servlet3SecurityContextHolderAwareRequestWrapper cannot be cast to org.springframework.web.multipart.MultipartHttpServletRequest
          at com.deloitte.atrapa.tax.common.web.interceptor.FileTypeSupportInterceptor.preHandle(FileTypeSupportInterceptor.java:31)
          at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:136)
          at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1033)
          at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
          at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
          at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
          at javax.servlet.http.HttpServlet.service(HttpServlet.java:665)
          at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
          at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
          at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
          at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:116)

with root cause The temporary upload location is not valid

https://www.jianshu.com/p/3e5d887c63c0

spring.http.multipart.location=${tmp.file.path} 注意:tmp.file.path 如果不存在,spring会认为是相对路径,对应根路径是tomcat临时文件目录

[C:\Users\shtang\AppData\Local\Temp\tomcat.6207581804214763646.11060\work\Tomcat\localhost\ROOT] is not valid] with root cause
java.io.IOException: The temporary upload location [C:\Users\shtang\AppData\Local\Temp\tomcat.6207581804214763646.11060\work\Tomcat\localhost\ROOT] is not valid
          at org.apache.catalina.connector.Request.parseParts(Request.java:2821)
          at org.apache.catalina.connector.Request.parseParameters(Request.java:3185)
          at org.apache.catalina.connector.Request.getParameter(Request.java:1116)
          at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381)


https://www.cnblogs.com/ldy-blogs/p/8509343.html

原因:

1.spring boot的应用服务在启动的时候,会生成在操作系统的/tmp目录下生成一个Tomcat.*的文件目录,用于"java.io.tmpdir"文件流操作TomcatEmbeddedServletContainerFactory

2.程序对文件的操作时:会生成临时文件,暂存在临时文件中;lunix 系统的tmpwatch 命令会删除10天未使用的临时文件;长时间不操作,导致/tmp下面的tomcat临时文件目录被删除,且删除的文件不可恢复,上传文件时获取不到文件目录,报错

解决方案:

1.重启服务,临时方案:会重新生成tomcat目录,但是生产环境不建议如此操作;

2.1增加服务配置,自定义baseDir:

2.2启动时增加参数-Djava.io.tmpdir=自定义目录

3.修改tmpwatch 删除文件的逻辑,系统级别的命令,不建议操作

4.在网上看到有:编码的方式catch异常,生成删除的文件夹;(方法未验证)

在网上还查阅到较好的解决办法: 注入一个Bean,手动配置临时目录,代码基本如下:

/**
 * 文件上传临时路径
 */
 @Bean
 MultipartConfigElement multipartConfigElement() {
    MultipartConfigFactory factory = new MultipartConfigFactory();
    factory.setLocation("/app/pttms/tmp");
    return factory.createMultipartConfig();
}

f5-->nginx-->gateway-->app-->remoteip

f5

nginx

https://www.jianshu.com/p/15f3498a7fad

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

$proxy_add_x_forwarded_for; nginx的这个变量含义就是,每次都追加$remote_address 到 xff头,如果xff头不存在,那么xff就被设置成跟$remote_address 一样了。如果本来就存在,就追加了 ip1, ip2这样的形式

proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Forwarded-Host $the_host;
proxy_set_header X-Forwarded-Proto $the_scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   X-Real-IP        $remote_addr;

gateway

app

key:Host value:[drtax.deloitte.com.cn]
key:X-Real-IP value:[10.172.64.202]
key:X-Forwarded-For value:[10.173.25.168, 10.172.128.71, 10.172.64.202]

feign

remoteip

当前用户的值有时候对有时候不对

[‎30/‎06/‎2020 16:49] Zhang, Kumilo Quan: 更新了

[‎30/‎06/‎2020 16:50] Wang, Jason Bo: 值还是不对么?

[‎30/‎06/‎2020 16:50] Zhang, Kumilo Quan: Object principal = authentication.getPrincipal(); SecurityUtil里面的 都是这样获取的 拿不到

[‎30/‎06/‎2020 16:51] Zhang, Kumilo Quan: 我刚看到原因了。TMD

[‎30/‎06/‎2020 16:51] Wang, Jason Bo: 在那里?

[‎30/‎06/‎2020 16:52] Zhang, Kumilo Quan: 还是回调没传tenantId引起的 缓存里面的key是FA:USER_DETAIL

[‎30/‎06/‎2020 16:53] Wang, Jason Bo: 哈哈 所以一直取的USER_DETAIL 的值而不是FA:USER_DETAIL

[‎30/‎06/‎2020 16:54] Zhang, Kumilo Quan: 对呀 是 null:USER_DETAIL Bo.wang讨论) 2020年6月30日 (二) 16:55 (CST)

[‎30/‎06/‎2020 16:54] Wang, Jason Bo: 所以有时候对,有时候不对.靠了

excel模板问题

build节点中添加

        <!--需要打开excel-->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>**/*.xlsx</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.xlsx</include>
                </includes>
            </resource>
        </resources>

excel大数据高并发导出方案

excel大数据高并发导出方案

nacos 一直走 http://localhost:8848

使用bootstrap.yml而不是application.yml

nacos读取不到配置

http://47.117.114.44:8848/nacos/v1/cs/configs?dataId=application.yml&group=DEFAULT_GROUP&namespace=ae11d41a-31a2-4386-90a6-372024d9a4b1

确保没问题情况下重启nacos

Illegal character ((CTRL-CHAR, code 31))

以下配置只能在<artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId>下有效

spring-boot-starter-undertow 不支持


https://blog.csdn.net/qq_36175946/article/details/114270579

# feign 配置
feign:
  sentinel:
    enabled: true
  hystrix:
    enabled: true
  okhttp:
    enabled: true
  httpclient:
    enabled: false
  client:
    config:
      default:
        connectTimeout: 10000
        readTimeout: 60000
 //下面的删除掉,或者设置为false
  compression:
    request:
      enabled: true
    response:
      enabled: true



#备份配置
#阿里sentinel熔断器
feign:
  sentinel:
    enabled: true
  hystrix:
    enabled: true
  okhttp:
    enabled: true
  httpclient:
    enabled: false
  client:
    config:
      default:
        connectTimeout: 10000
        readTimeout: 60000
  # compression:
  #   request:
  #     enabled: true
  #     mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain
  #     min-response-size: 10240
  #   response:
  #     enabled: true
### 启用Gzip压缩
# server:
#   compression:
#     enabled: true
#     mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain
#     min-response-size: 10240

RemoteTokenServices.loadAuthentication(RemoteTokenServices.java:108)

RedisAutoCacheManager 中getCache逻辑报错

token 201 错误

通过 CommonConstants.FAIL找到引用的地方

AuthorizationServerConfig 缺少 DefaultUserDetailsServiceImpl

使ling-cloud-common-security\src\main\resources\META-INF\spring.factories出错然后改对