|
|
@@ -0,0 +1,186 @@
|
|
|
+package com.webflux.launchadmin.mysql.service.ip;
|
|
|
+
|
|
|
+import cn.hutool.core.util.RandomUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.google.common.cache.Cache;
|
|
|
+import com.webflux.launchadmin.mysql.entity.planNew.WxUrl;
|
|
|
+import jakarta.annotation.PostConstruct;
|
|
|
+import jakarta.annotation.Resource;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.jetbrains.annotations.NotNull;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
|
|
+import org.springframework.data.redis.core.ReactiveRedisTemplate;
|
|
|
+import org.springframework.data.relational.core.query.Criteria;
|
|
|
+import org.springframework.data.relational.core.query.Query;
|
|
|
+import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.server.ServerWebExchange;
|
|
|
+import reactor.core.publisher.Mono;
|
|
|
+
|
|
|
+import java.net.InetSocketAddress;
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+import java.util.concurrent.atomic.AtomicReference;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class BlacklistService {
|
|
|
+ @Resource
|
|
|
+ private ReactiveRedisTemplate<String,String> reactiveRedisTemplate;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private R2dbcEntityTemplate template;
|
|
|
+
|
|
|
+ private static final List<String> firstUrl = List.of("out/planNew/hfoInfo","out/planNew/hfoInfo","out/listening/landingPageList");
|
|
|
+ private static final CopyOnWriteArrayList<String> ip_balck_list = new CopyOnWriteArrayList<>();
|
|
|
+ private static final CopyOnWriteArrayList<String> ip_white_list = new CopyOnWriteArrayList<>();
|
|
|
+ private static final AtomicInteger ipRateLimitRange= new AtomicInteger();//访问次数
|
|
|
+ private static final String key="ip_rate_limit:";//为用户ip访问记录次数ip_rate_limit:1.180.116.135
|
|
|
+ public static final AtomicReference<String> wxUrl=new AtomicReference<>();
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param exchange
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public Mono<Boolean> getRateReturn(ServerWebExchange exchange){
|
|
|
+ ServerHttpRequest httpRequest = exchange.getRequest();
|
|
|
+ InetSocketAddress remoteAddress = httpRequest.getRemoteAddress();
|
|
|
+ String ipAddress;
|
|
|
+ if (remoteAddress != null) {
|
|
|
+ ipAddress = remoteAddress.getAddress().getHostAddress();
|
|
|
+ } else {
|
|
|
+ ipAddress = null;
|
|
|
+ }
|
|
|
+ if (Objects.nonNull(ipAddress)) {
|
|
|
+ try {
|
|
|
+ return getIpRateNum(ipAddress);
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("ip黑名单验证Error:::"+e.getMessage());
|
|
|
+ return Mono.just(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Mono.just(false);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取ip访问次数
|
|
|
+ */
|
|
|
+ public Mono<Boolean> getIpRateNum(String ip){
|
|
|
+ if (ip_white_list.contains(ip)) {
|
|
|
+ return Mono.just(true);
|
|
|
+ }
|
|
|
+ if (ip_balck_list.contains(ip)) {
|
|
|
+ return Mono.just(false);
|
|
|
+ }
|
|
|
+ String keys=key+ip;
|
|
|
+ long secondsUntilNextMidnight = getSecondsUntilNextMidnight();
|
|
|
+ return reactiveRedisTemplate.opsForValue().get(keys)
|
|
|
+ .switchIfEmpty(Mono.just("0")) //
|
|
|
+ .flatMap(f->{
|
|
|
+ if(Integer.parseInt(f)==0){
|
|
|
+ Mono<Long> increment = reactiveRedisTemplate.opsForValue().increment(keys);
|
|
|
+ Mono<Boolean> expire = reactiveRedisTemplate.expire(keys, Duration.ofMinutes(secondsUntilNextMidnight));
|
|
|
+ return increment.then(expire).flatMap(tr->Mono.just(true));
|
|
|
+ }
|
|
|
+ if (Integer.parseInt(f) > ipRateLimitRange.get()) {
|
|
|
+ return Mono.just(false);
|
|
|
+ }
|
|
|
+ Mono<Long> increment = reactiveRedisTemplate.opsForValue().increment(keys);
|
|
|
+ return increment.flatMap(num->{
|
|
|
+ if (num > ipRateLimitRange.get()) {
|
|
|
+ return Mono.just(false);
|
|
|
+ }
|
|
|
+ return Mono.just(true);
|
|
|
+ });});
|
|
|
+ }
|
|
|
+ private static long getSecondsUntilNextMidnight() {
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ LocalDateTime midnight = now.toLocalDate().plusDays(1).atStartOfDay().plusHours(0).plusSeconds(1).plusMinutes(0);
|
|
|
+ long l = Duration.between(now, midnight).toMinutes();
|
|
|
+ return l;
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostConstruct
|
|
|
+ public void postConstruct() {
|
|
|
+ getIpBalckList();
|
|
|
+ getIpRateLimitRange();
|
|
|
+ getWxUrl();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取缓存中设置次数
|
|
|
+ */
|
|
|
+ @Scheduled(fixedDelay = 160000)
|
|
|
+ public void getWxUrl(){
|
|
|
+ template.select(Query.query(Criteria.empty()), WxUrl.class).collectList().flatMap(list->{
|
|
|
+ if (!list.isEmpty()) {
|
|
|
+ list.stream().map(WxUrl::url).findFirst().ifPresent(wxUrl::set);
|
|
|
+ }else {
|
|
|
+ log.error("非异常:::提醒::: ip限制后跳转微信url 未配置");
|
|
|
+ }
|
|
|
+ return Mono.just("");
|
|
|
+ }).subscribe();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取缓存中设置次数
|
|
|
+ */
|
|
|
+ @Scheduled(fixedDelay = 160000)
|
|
|
+ public void getIpRateLimitRange(){
|
|
|
+ reactiveRedisTemplate.opsForValue().get("ip_rate_limit_range").flatMap(range->{
|
|
|
+ ipRateLimitRange.set(Integer.parseInt(range));
|
|
|
+ return Mono.just(range);
|
|
|
+ }).subscribe();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 数据源同步本地 黑白名单
|
|
|
+ */
|
|
|
+ @Scheduled(fixedDelay = 160000)
|
|
|
+ public void getIpBalckList(){
|
|
|
+ template.select(Query.query(Criteria.where("status").is(1)), IpWhite.class).collectList().flatMap(ipInfo -> {
|
|
|
+ Map<Integer, List<IpWhite>> collect = ipInfo.stream().filter(f -> Objects.nonNull(f.type())).collect(Collectors.groupingBy(IpWhite::type));
|
|
|
+ List<String> ips = getCollect(collect,1);
|
|
|
+ if(!ips.isEmpty()){
|
|
|
+ List<String> addIps = getAddIps(ips, ip_white_list);
|
|
|
+ if (!addIps.isEmpty()) {
|
|
|
+ ip_white_list.addAll(addIps);
|
|
|
+ }
|
|
|
+ List<String> removers = getAddIps(ip_white_list, ips);
|
|
|
+ if(!removers.isEmpty()){
|
|
|
+ ip_white_list.removeAll(removers);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ List<String> ips2 = getCollect(collect,2);
|
|
|
+ if(!ips.isEmpty()){
|
|
|
+ List<String> addIps2 = getAddIps(ips2, ip_balck_list);
|
|
|
+ if (!addIps2.isEmpty()) {
|
|
|
+ ip_balck_list.addAll(addIps2);
|
|
|
+ }
|
|
|
+ List<String> removers2 = getAddIps(ip_balck_list, ips2);
|
|
|
+ if(!removers2.isEmpty()){
|
|
|
+ ip_balck_list.removeAll(removers2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Mono.just(ipInfo);
|
|
|
+ }).subscribe();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @NotNull
|
|
|
+ private List<String> getAddIps(List<String> ips, List<String> ip_white_list) {
|
|
|
+ return ips.stream().filter(o -> !ip_white_list.contains(o)).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ @NotNull
|
|
|
+ private List<String> getCollect(Map<Integer, List<IpWhite>> collect,Integer type) {
|
|
|
+ return collect.get(type).stream().map(IpWhite::ipAddr).distinct().collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|