Jelajahi Sumber

fix: 小程序鉴权相关

lijiaqi 1 tahun lalu
induk
melakukan
ba395612e0

+ 5 - 1
src/main/java/com/inspur/smsb/gateway/config/AuthorizationManager.java

@@ -16,6 +16,7 @@ import org.springframework.util.CollectionUtils;
 import org.springframework.util.PathMatcher;
 import reactor.core.publisher.Mono;
 
+import javax.annotation.Resource;
 import java.util.*;
 
 /**
@@ -29,6 +30,9 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
     @Autowired
     private PathRolesMap pathRolesMap;
 
+    @Resource
+    private UrlAuthorizationDecision urlAuthorizationDecision;
+
     @Override
     public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
         ServerHttpRequest request = authorizationContext.getExchange().getRequest();
@@ -47,7 +51,7 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
             .any(roleId -> {
                 return CollectionUtils.isEmpty(authorities)?Boolean.TRUE:authorities.contains(roleId);
             })
-            .map(AuthorizationDecision::new)
+            .map(auth -> new AuthorizationDecision(urlAuthorizationDecision.check(path)))
             .defaultIfEmpty(new AuthorizationDecision(false));
     }
 }

+ 41 - 0
src/main/java/com/inspur/smsb/gateway/config/UrlAuthorizationDecision.java

@@ -0,0 +1,41 @@
+package com.inspur.smsb.gateway.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.List;
+
+/**
+ * @author lijiaqi
+ * @date 2024-05-28 15:03
+ */
+@Component
+@EnableScheduling
+public class UrlAuthorizationDecision {
+
+    /**
+     * 路径白名单
+     */
+    @Value("${whitelist.paths}")
+    private List<String> whiteList;
+
+    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+    /**
+     * 白名单放权
+     *
+     * @param url 请求的Url
+     * @return 是否通过鉴权
+     */
+    public boolean check(String url) {
+        // 判断该路径是否在白名单里,如果是直接放行
+        for (String permittedUrl : whiteList) {
+            if (antPathMatcher.match(permittedUrl, url)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 25 - 7
src/main/java/com/inspur/smsb/gateway/filter/WebFluxUserRequestInfoFilter.java

@@ -31,6 +31,8 @@ import reactor.core.publisher.Mono;
 import javax.annotation.Resource;
 import java.security.NoSuchAlgorithmException;
 import java.text.ParseException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -63,9 +65,17 @@ public class WebFluxUserRequestInfoFilter implements GlobalFilter {
     @Value("${wxapplet.appId}")
     private String appId;
 
+    @Value("${wxapplet.minusMinute:2}")
+    private Integer minusMinute;
+
+    @Value("${wxapplet.plusMinute:2}")
+    private Integer plusMinute;
+
     @Resource
     private ObjectMapper objectMapper;
 
+    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
     @Override
     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
         String wxAppletId = exchange.getRequest().getHeaders().getFirst("WxAppletId");
@@ -136,18 +146,17 @@ public class WebFluxUserRequestInfoFilter implements GlobalFilter {
                     // 把新的 exchange 放回到过滤链
                     return chain.filter(exchange.mutate().request(request).build());
                 }
-            } else {
-                // 三级等保,若小程序请求不带token,则走该校验方法
-                if (!isEncryptedRequest(exchange.getRequest())) {
-                    exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
-                    return exchange.getResponse().setComplete();
-                }
             }
             return chain.filter(exchange);
         } else {
             try {
                 String token = exchange.getRequest().getHeaders().getFirst("Authorization");
                 if (Strings.isNullOrEmpty(token)) {
+                    // 三级等保,若小程序请求不带token,则走该校验方法
+                    if (!isEncryptedRequest(exchange.getRequest())) {
+                        exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
+                        return exchange.getResponse().setComplete();
+                    }
                     return chain.filter(exchange);
                 }
                 // 由于漏洞扫描发现退出登陆后,token在一定时间范围内还是有效,故此处做黑名单限制,
@@ -272,7 +281,16 @@ public class WebFluxUserRequestInfoFilter implements GlobalFilter {
     private boolean isEncryptedRequest(ServerHttpRequest request) {
         String sign = request.getHeaders().getFirst("sign");
         String time = request.getHeaders().getFirst("time");
-        return md5(time).equals(sign);
+        return isValidTime(time) && md5(time).equals(sign);
+    }
+
+    private boolean isValidTime(String time) {
+        // 服务器前后两分钟
+        LocalDateTime datetime = LocalDateTime.parse(time, TIME_FORMATTER);
+        LocalDateTime m2t = LocalDateTime.now().minusMinutes(minusMinute);
+        LocalDateTime p2t = LocalDateTime.now().plusMinutes(plusMinute);
+        return (datetime.isAfter(m2t) || datetime.isEqual(m2t)) &&
+            (datetime.isBefore(p2t) || datetime.isAfter(p2t));
     }
 
     private String md5(String time) {