|
|
@@ -0,0 +1,146 @@
|
|
|
+package com.inspur.smsb.gateway.filter;
|
|
|
+
|
|
|
+import com.alibaba.cola.dto.Response;
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.inspur.smsb.gateway.dto.license.LicenseDto;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.factory.DisposableBean;
|
|
|
+import org.springframework.beans.factory.InitializingBean;
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
|
|
+import org.springframework.cloud.gateway.filter.GlobalFilter;
|
|
|
+import org.springframework.core.Ordered;
|
|
|
+import org.springframework.core.io.buffer.DataBufferFactory;
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.http.server.reactive.ServerHttpResponse;
|
|
|
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.web.server.ServerWebExchange;
|
|
|
+import reactor.core.publisher.Mono;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.io.*;
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.Optional;
|
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author zengweijie
|
|
|
+ * @version 1.0
|
|
|
+ * @date 2022/7/27 11:20
|
|
|
+ **/
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class LicenseFilter implements GlobalFilter, Ordered, InitializingBean, DisposableBean {
|
|
|
+ @Resource
|
|
|
+ private ObjectMapper objectMapper;
|
|
|
+ private final AtomicBoolean sysLocked = new AtomicBoolean(false);
|
|
|
+ private final AtomicBoolean timeRollBack = new AtomicBoolean(false);
|
|
|
+ private static final int DEFAULT_DURATION = 60;
|
|
|
+ private static final int STEP = 5;
|
|
|
+ @Value("${license-dir}")
|
|
|
+ private String licenseDir;
|
|
|
+ private ThreadPoolTaskExecutor threadPoolTaskExecutor;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
|
|
+ if (sysLocked.get() || timeRollBack.get()) {
|
|
|
+ ServerHttpResponse response = exchange.getResponse();
|
|
|
+ response.setStatusCode(HttpStatus.LOCKED);
|
|
|
+ response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
|
|
+ String msg = sysLocked.get() ? "系统已锁定" : "发现时间回退,系统已锁定";
|
|
|
+ return response.writeWith(Mono.fromSupplier(() -> {
|
|
|
+ DataBufferFactory bufferFactory = response.bufferFactory();
|
|
|
+ try {
|
|
|
+ return bufferFactory.wrap(objectMapper.writeValueAsBytes(Response.buildFailure(String.valueOf(HttpStatus.LOCKED.value()), msg)));
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ log.error("Error writing response", e);
|
|
|
+ return bufferFactory.wrap(new byte[0]);
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ return chain.filter(exchange);
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean checkSysLocked() {
|
|
|
+ if (sysLocked.get() || timeRollBack.get()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ File directory = new File(licenseDir);
|
|
|
+ if (!directory.exists() && !directory.mkdirs()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ File licenseFile = new File(directory.getAbsolutePath() + File.separator + "license.json");
|
|
|
+ if (!licenseFile.exists()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ LicenseDto licenseDto = new LicenseDto();
|
|
|
+ try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(licenseFile))) {
|
|
|
+ if (inputStream.available() > 0) {
|
|
|
+ licenseDto = objectMapper.readValue(inputStream.readAllBytes(), LicenseDto.class);
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ if (Objects.nonNull(licenseDto.getLastUpdate()) && licenseDto.getLastUpdate().isAfter(now)) {
|
|
|
+ timeRollBack.set(true);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int duration = Optional.ofNullable(licenseDto.getDuration()).orElse(DEFAULT_DURATION);
|
|
|
+ int account = Optional.ofNullable(licenseDto.getAccount()).orElse(0);
|
|
|
+ if (duration <= account) {
|
|
|
+ sysLocked.set(true);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ licenseDto.setDuration(duration);
|
|
|
+ licenseDto.setAccount(account + STEP);
|
|
|
+ licenseDto.setLastUpdate(now);
|
|
|
+ try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(licenseFile))) {
|
|
|
+ outputStream.write(objectMapper.writeValueAsBytes(licenseDto));
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int getOrder() {
|
|
|
+ return HIGHEST_PRECEDENCE;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void destroy() {
|
|
|
+ threadPoolTaskExecutor.shutdown();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void afterPropertiesSet() {
|
|
|
+ threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
|
|
|
+ threadPoolTaskExecutor.setCorePoolSize(1);
|
|
|
+ threadPoolTaskExecutor.setMaxPoolSize(1);
|
|
|
+ threadPoolTaskExecutor.setQueueCapacity(0);
|
|
|
+ threadPoolTaskExecutor.setKeepAliveSeconds(60);
|
|
|
+ threadPoolTaskExecutor.setThreadNamePrefix("license-Async-Executor-");
|
|
|
+ threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(false);
|
|
|
+ threadPoolTaskExecutor.setAwaitTerminationSeconds(0);
|
|
|
+ threadPoolTaskExecutor.initialize();
|
|
|
+ threadPoolTaskExecutor.execute(() -> {
|
|
|
+ while (checkSysLocked()) {
|
|
|
+ try {
|
|
|
+ Thread.sleep(Duration.ofMinutes(STEP).toMillis());
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ threadPoolTaskExecutor.shutdown();
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|