refactor: 优化代码结构,完善资源(API)访问控制
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java
index 0d5c72d..53f3199 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java
@@ -42,6 +42,11 @@
   private String type;
 
   /**
+   * 菜单图标
+   */
+  private String icon;
+
+  /**
    * URL地址
    */
   private String url;
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/PermissionRoleSet.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/PermissionRoleSet.java
new file mode 100644
index 0000000..675e657
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/PermissionRoleSet.java
@@ -0,0 +1,5 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+public class PermissionRoleSet {
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/ResourceRoleSet.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/ResourceRoleSet.java
new file mode 100644
index 0000000..2d7940c
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/ResourceRoleSet.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import java.util.Collection;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class ResourceRoleSet extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -683204173918706673L;
+
+  public static final String ACCESS_ANONYMOUS = "anonymous"; // 匿名访问anonymous
+  public static final String ACCESS_AUTHENTICATE = "authenticate"; // 认证访问authenticate
+  public static final String ACCESS_AUTHORIZE = "authorize"; // 授权访问authorize
+  public static final String ACCESS_PERMIT_ALL = "permitAll"; // 允许所有permitAll
+  public static final String ACCESS_DENY_ALL = "denyAll"; // 拒绝所有denyAll
+
+  @Getter
+  @Setter
+  private String id;
+
+  /**
+   * 代码
+   */
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 请求方式(GET、POST、PUT、DELETE 等)
+   */
+  @Getter
+  @Setter
+  private String method;
+
+  /**
+   * 请求路径
+   */
+  @Getter
+  @Setter
+  private String path;
+
+  /**
+   * 访问规则(匿名访问anonymous、认证访问authenticate、授权访问authorize、允许所有permitAll、拒绝所有denyAll)
+   */
+  @Getter
+  @Setter
+  private String access;
+
+  @Getter
+  @Setter
+  Collection<Role> roles;
+  
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Route.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Route.java
new file mode 100644
index 0000000..9ca66c6
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Route.java
@@ -0,0 +1,68 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+public class Route extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 1254295553813507087L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  /**
+   * 代码
+   */
+  @Getter
+  @Setter
+  private String code;
+
+  /**
+   * 名称
+   */
+  @Getter
+  @Setter
+  private String name;
+
+  /**
+   * 备注
+   */
+  @Getter
+  @Setter
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  @Getter
+  @Setter
+  private String status;
+
+  /**
+   * 路径前缀
+   */
+  @Getter
+  @Setter
+  private String pathPrefix;
+
+  /**
+   * 路由地址
+   */
+  @Getter
+  @Setter
+  private String url;
+
+  /**
+   * 是否排除前缀
+   */
+  @Getter
+  @Setter
+  private Boolean stripPrefix;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnAccountRestTemplateConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnRestTemplateConfig.java
similarity index 71%
rename from gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnAccountRestTemplateConfig.java
rename to gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnRestTemplateConfig.java
index 5e98c4a..789f930 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnAccountRestTemplateConfig.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnRestTemplateConfig.java
@@ -17,7 +17,7 @@
 import org.springframework.web.client.RestTemplate;
 
 @Configuration
-public class AuthnAccountRestTemplateConfig {
+public class AuthnRestTemplateConfig {
 
   @Bean
   public ClientHttpRequestFactory simpleClientHttpRequestFactory(
@@ -35,6 +35,21 @@
       return factory;
     }
     
+    SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+    
+    if (trustStore == null || trustStore.isEmpty()) {
+    } else {
+      try {
+        sslContextBuilder
+          .loadTrustMaterial(
+              ResourceUtils.getFile(trustStore),
+              trustStorePassword.toCharArray()
+          );
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    
     if (keyStore == null || keyStore.isEmpty()) {
       SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
       factory.setReadTimeout(5000);// 单位为ms
@@ -42,42 +57,35 @@
       return factory;
     } else {
       try {
-        SSLContextBuilder sslContextBuilder = SSLContexts.custom();
-        if (trustStore == null || trustStore.isEmpty()) {
-        } else {
-          sslContextBuilder
-//            .loadTrustMaterial(TrustAllStrategy.INSTANCE)
-            .loadTrustMaterial(
-                ResourceUtils.getFile(trustStore),
-                trustStorePassword.toCharArray()
-            );
-        }
-        
         sslContextBuilder
           .loadKeyMaterial(
               ResourceUtils.getFile(keyStore),
               keyStorePassword.toCharArray(),
               keyPassword.toCharArray());
-        
-        SSLContext sslContext = sslContextBuilder.build();
-    
-        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
-            sslContext,
-            SSLConnectionSocketFactory.getDefaultHostnameVerifier());
-        
-        CloseableHttpClient httpClient = HttpClients.custom()
-            .setSSLSocketFactory(sslsf)
-            .build();
-        
-        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
-        factory.setReadTimeout(5000);// 单位为ms
-        factory.setConnectTimeout(5000);// 单位为ms
-        return factory;
       } catch (Exception e) {
         e.printStackTrace();
       }
     }
     
+    try {
+      SSLContext sslContext = sslContextBuilder.build();
+  
+      SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
+          sslContext,
+          SSLConnectionSocketFactory.getDefaultHostnameVerifier());
+      
+      CloseableHttpClient httpClient = HttpClients.custom()
+          .setSSLSocketFactory(sslsf)
+          .build();
+      
+      HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    
     SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
     factory.setReadTimeout(5000);// 单位为ms
     factory.setConnectTimeout(5000);// 单位为ms
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnAccountRemoteRestTemplate.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnRemoteRestTemplate.java
similarity index 64%
rename from gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnAccountRemoteRestTemplate.java
rename to gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnRemoteRestTemplate.java
index 6259608..17a3430 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnAccountRemoteRestTemplate.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnRemoteRestTemplate.java
@@ -12,10 +12,10 @@
 
 @Slf4j
 @Component
-public class AuthnAccountRemoteRestTemplate {
+public class AuthnRemoteRestTemplate {
   
   @Autowired
-  private RestTemplate authnAccountRestTemplate;
+  private RestTemplate authnRestTemplate;
   
   @Value(value = "${sw-backend-base-api.uri}/v1/authn")
   private String url;
@@ -37,7 +37,7 @@
       final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
       log.debug(url);
       
-      return authnAccountRestTemplate.getForObject(url, JSONObject.class);
+      return authnRestTemplate.getForObject(url, JSONObject.class);
     } catch (Exception e) {
       e.printStackTrace();
       
@@ -45,14 +45,14 @@
     }
   }
 
-  public JSONObject roles(String username) {
+  public JSONObject accountRoles(String username) {
 
     try {
       final String path = "/{username}/roles";
       final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
       log.debug(url);
       
-      return authnAccountRestTemplate.getForObject(url, JSONObject.class);
+      return authnRestTemplate.getForObject(url, JSONObject.class);
     } catch (Exception e) {
       e.printStackTrace();
       
@@ -60,14 +60,14 @@
     }
   }
 
-  public JSONObject applications(String username, String applicationId) {
+  public JSONObject accountApplications(String username, String applicationId) {
 
     try {
       final String path = "/{username}/applications";
       final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
       log.debug(url);
       
-      return authnAccountRestTemplate.getForObject(url, JSONObject.class);
+      return authnRestTemplate.getForObject(url, JSONObject.class);
     } catch (Exception e) {
       e.printStackTrace();
       
@@ -75,14 +75,14 @@
     }
   }
 
-  public JSONObject menus(String username, String applicationId) {
+  public JSONObject accountMenus(String username, String applicationId) {
 
     try {
       final String path = "/{username}/menus";
       final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
       log.debug(url);
       
-      return authnAccountRestTemplate.getForObject(url, JSONObject.class);
+      return authnRestTemplate.getForObject(url, JSONObject.class);
     } catch (Exception e) {
       e.printStackTrace();
       
@@ -90,14 +90,14 @@
     }
   }
 
-  public JSONObject operations(String username, String applicationId) {
+  public JSONObject accountOperations(String username, String applicationId) {
 
     try {
       final String path = "/{username}/operations";
       final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
       log.debug(url);
       
-      return authnAccountRestTemplate.getForObject(url, JSONObject.class);
+      return authnRestTemplate.getForObject(url, JSONObject.class);
     } catch (Exception e) {
       e.printStackTrace();
       
@@ -105,14 +105,45 @@
     }
   }
 
-  public JSONObject resources(String username, String applicationId) {
+  public JSONObject accountResources(String username, String applicationId) {
 
     try {
       final String path = "/{username}/resources";
       final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
       log.debug(url);
       
-      return authnAccountRestTemplate.getForObject(url, JSONObject.class);
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject resourceRoleSets() {
+
+    try {
+      final String path = "/resourceRoleSets";
+      final String url = this.url + path;
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+
+  public JSONObject routes() {
+
+    try {
+      final String path = "/routes";
+      final String url = this.url + path;
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
     } catch (Exception e) {
       e.printStackTrace();
       
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnAccountService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnAccountService.java
deleted file mode 100644
index beeb74a..0000000
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnAccountService.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.supwisdom.institute.backend.gateway.authn.service;
-
-import java.util.List;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
-
-import com.alibaba.fastjson.JSONObject;
-import com.supwisdom.institute.backend.gateway.authn.model.Account;
-import com.supwisdom.institute.backend.gateway.authn.model.Permission;
-import com.supwisdom.institute.backend.gateway.authn.model.Role;
-import com.supwisdom.institute.backend.gateway.authn.remote.web.client.AuthnAccountRemoteRestTemplate;
-
-@Service
-public class AuthnAccountService {
-  
-//  @Autowired
-//  private AuthnAccountRemoteFeignClient authnAccountRemote;
-  
-  @Autowired
-  private AuthnAccountRemoteRestTemplate authnAccountRemote;
-  
-  public Account account(String username) {
-    
-    JSONObject jsonObject = authnAccountRemote.account(username);
-    if (jsonObject == null) {
-      return null;
-    }
-    
-    if (jsonObject.getIntValue("code") == 0) {
-      JSONObject data = jsonObject.getJSONObject("data");
-      
-      return data.toJavaObject(Account.class);
-    }
-    
-    return null;
-  }
-
-  public List<Role> roles(String username) {
-    
-    JSONObject jsonObject = authnAccountRemote.roles(username);
-    if (jsonObject == null) {
-      return null;
-    }
-    
-    if (jsonObject.getIntValue("code") == 0) {
-      JSONObject data = jsonObject.getJSONObject("data");
-      
-      return data.getJSONArray("roles").toJavaList(Role.class);
-    }
-    
-    return null;
-  }
-
-  public List<Permission> menus(String username, String applicationId) {
-    
-    JSONObject jsonObject = authnAccountRemote.menus(username, applicationId);
-    if (jsonObject == null) {
-      return null;
-    }
-    
-    if (jsonObject.getIntValue("code") == 0) {
-      JSONObject data = jsonObject.getJSONObject("data");
-      
-      return data.getJSONArray("permissions").toJavaList(Permission.class);
-    }
-    
-    return null;
-  }
-
-}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnService.java
new file mode 100644
index 0000000..13434ec
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnService.java
@@ -0,0 +1,92 @@
+package com.supwisdom.institute.backend.gateway.authn.service;
+
+import java.util.List;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.gateway.authn.model.Account;
+import com.supwisdom.institute.backend.gateway.authn.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.gateway.authn.model.Role;
+import com.supwisdom.institute.backend.gateway.authn.model.Route;
+import com.supwisdom.institute.backend.gateway.authn.remote.web.client.AuthnRemoteRestTemplate;
+
+@Slf4j
+@Service
+public class AuthnService {
+  
+  @Autowired
+  private AuthnRemoteRestTemplate authnRemote;
+  
+  public Account account(String username) {
+    
+    JSONObject jsonObject = authnRemote.account(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.toJavaObject(Account.class);
+    }
+    
+    return null;
+  }
+
+  public List<Role> accountRoles(String username) {
+    
+    JSONObject jsonObject = authnRemote.accountRoles(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("roles").toJavaList(Role.class);
+    }
+    
+    return null;
+  }
+
+  public List<ResourceRoleSet> resourceRoleSets() {
+    
+    JSONObject jsonObject = authnRemote.resourceRoleSets();
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("resourceRoleSets").toJavaList(ResourceRoleSet.class);
+    }
+    
+    return null;
+  }
+
+  public List<Route> routes() {
+    
+    JSONObject jsonObject = authnRemote.routes();
+    if (jsonObject == null) {
+      return null;
+    }
+    log.debug("{}", jsonObject.toJSONString());
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("routes").toJavaList(Route.class);
+    }
+    
+    return null;
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/AccessControlGlobalFilter.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/AccessControlGlobalFilter.java
index 266dfa7..6ef6b7e 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/AccessControlGlobalFilter.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/filter/AccessControlGlobalFilter.java
@@ -4,6 +4,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -11,6 +12,7 @@
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 import org.springframework.cloud.gateway.filter.GlobalFilter;
 import org.springframework.core.Ordered;
@@ -30,14 +32,15 @@
 import org.springframework.web.server.ServerWebExchange;
 
 import com.supwisdom.infras.security.core.userdetails.InfrasUser;
+import com.supwisdom.institute.backend.gateway.authn.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.gateway.authn.model.Role;
+import com.supwisdom.institute.backend.gateway.authn.service.AuthnService;
 
 import reactor.core.publisher.Mono;
 
-//import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
-
 @Slf4j
 public class AccessControlGlobalFilter implements GlobalFilter, Ordered {
-
+  
   @Override
   public int getOrder() {
     return Ordered.HIGHEST_PRECEDENCE;
@@ -48,17 +51,17 @@
     log.debug("AccessControlGlobalFilter.filter");
     
     // 获取 请求路径 对应的 资源
-    Collection<ConfigAttribute> attributes = this.getAttributes(exchange);
-    log.debug("request's attributes is {}", attributes);
+    Collection<ConfigAttribute> configAttributes = this.getAttributes(exchange);
+    log.debug("request's attributes is {}", configAttributes);
     
     // 判断 该资源 是否需要登录才能访问
-    if (attributes == null) {
+    if (configAttributes == null || configAttributes.size() <= 0) {
       return chain.filter(exchange);  // FIXME: 
     }
     
     // 获取 当前登录用户(包括角色信息)
     
-    // 判断 登录用户 是否可以访问 该资源
+    // FIXME: 判断 登录用户 是否可以访问 该资源
     
     return ReactiveSecurityContextHolder.getContext()
         .filter(c -> {
@@ -72,7 +75,32 @@
           
           boolean hasPermission = false;
           
-          for (ConfigAttribute ca : attributes) {
+          ConfigAttribute ca;
+          String needRole;
+          for (Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext();) {
+            ca = iter.next();
+            needRole = ca.getAttribute();
+            
+            if (needRole == null || needRole.isEmpty()) {
+              continue;
+            }
+            
+            if (needRole.startsWith("ACCESS_")) {
+              String access = needRole.substring("ACCESS_".length()); log.debug("Access is {}", access);
+              if ("anonymous".equals(access)) {
+                hasPermission = false;
+              } else if ("authenticate".equals(access)) {
+                hasPermission = true;
+              } else if ("permitAll".equals(access)) {
+                hasPermission = true;
+              } else if ("denyAll".equals(access)) {
+                hasPermission = false;
+              } else {
+                hasPermission = false;
+              }
+              break;
+            }
+            
             hasPermission = infrasUser.getRoles().contains(ca.getAttribute());
             if (hasPermission) {
               log.debug("match attribute is {}", ca.getAttribute());
@@ -81,35 +109,83 @@
           }
           
           if (!hasPermission) {
-            throw new RuntimeException("no permission");
+            throw new RuntimeException("no right");
           }
           
           return exchange;
         })
         .flatMap(ex -> chain.filter(ex));
   }
-  
+
+  @Autowired
+  private AuthnService authnService;
+
   private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = new ConcurrentHashMap<RequestMatcher, Collection<ConfigAttribute>>();
 
-  @Scheduled(initialDelayString = "${sw-backend-gateway.resource.refresh-delay:10000}", fixedDelayString = "${sw-backend-gateway.resource.refresh-delay:10000}")
+  @Scheduled(
+      initialDelayString = "${sw-backend-gateway.resource.refresh-delay:200}", 
+      fixedDelayString = "${sw-backend-gateway.resource.refresh-delay:10000}")
   protected void refreshRequestMap() {
     
     log.debug("AccessControlGlobalFilter.refreshRequestMap");
     
+    requestMap.clear();
     loadRequestMap();
   }
 
   // 定时刷新 资源 与 可访问角色 的 Map
   private void loadRequestMap() {
-    requestMap.clear();
+
+    if (requestMap.isEmpty()) {
+//    AntPathRequestMatcher requestMatcher0 = new AntPathRequestMatcher("/api/**");
+//    Collection<ConfigAttribute> attributes0 = new ArrayList<ConfigAttribute>();
+//    attributes0.add(new SecurityConfig("user"));
+//    requestMap.put(requestMatcher0, attributes0);
+    
+      // 从 后端接口 加载
+      List<ResourceRoleSet> resourceRoleSets = authnService.resourceRoleSets();
+      if (resourceRoleSets != null) {
+        for (ResourceRoleSet resourceRoleSet : resourceRoleSets) {
+          String method = resourceRoleSet.getMethod();
+          String path = resourceRoleSet.getPath();
+          String access = resourceRoleSet.getAccess();
+          
+          final RequestMatcher requestMatcher = new AntPathRequestMatcher(path, method);
+          
+          Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
+          
+          if (access != null) {
+            if (access.equals(ResourceRoleSet.ACCESS_ANONYMOUS)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_ANONYMOUS));
+            } else if (access.equals(ResourceRoleSet.ACCESS_AUTHENTICATE)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_AUTHENTICATE));
+            } else if (access.equals(ResourceRoleSet.ACCESS_AUTHORIZE)) {
+              for (Role r : resourceRoleSet.getRoles()) {
+                ConfigAttribute ca = new SecurityConfig(r.getCode());
+                attributes.add(ca);
+              }
+            } else if (access.equals(ResourceRoleSet.ACCESS_PERMIT_ALL)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_PERMIT_ALL));
+            } else if (access.equals(ResourceRoleSet.ACCESS_DENY_ALL)) {
+              attributes.add(new SecurityConfig("ACCESS_"+ResourceRoleSet.ACCESS_DENY_ALL));
+            } else {
+              for (Role r : resourceRoleSet.getRoles()) {
+                ConfigAttribute ca = new SecurityConfig(r.getCode());
+                attributes.add(ca);
+              }
+            }
+          } else {
+            for (Role r : resourceRoleSet.getRoles()) {
+              ConfigAttribute ca = new SecurityConfig(r.getCode());
+              attributes.add(ca);
+            }
+          }
+          
+          requestMap.put(requestMatcher, attributes);
+        }
+      }
       
-    AntPathRequestMatcher requestMatcher0 = new AntPathRequestMatcher("/api/**");
-    Collection<ConfigAttribute> attributes0 = new ArrayList<ConfigAttribute>();
-    attributes0.add(new SecurityConfig("user"));
-    requestMap.put(requestMatcher0, attributes0);
-    
-    // FIXME: 从 后端接口 加载
-    
+    }
 
   }
   
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
index ef7d0e2..8a30025 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
@@ -16,7 +16,7 @@
 
 import com.supwisdom.institute.backend.gateway.authn.model.Account;
 import com.supwisdom.institute.backend.gateway.authn.model.Role;
-import com.supwisdom.institute.backend.gateway.authn.service.AuthnAccountService;
+import com.supwisdom.institute.backend.gateway.authn.service.AuthnService;
 
 import reactor.core.publisher.Mono;
 import lombok.extern.slf4j.Slf4j;
@@ -28,7 +28,7 @@
   PasswordEncoder passwordEncoder;
 
   @Autowired
-  AuthnAccountService authnAccountService;
+  AuthnService authnAccountService;
   
   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO: 
@@ -56,7 +56,7 @@
       throw new UsernameNotFoundException(String.format("%s not found", username));
     }
     
-    List<Role> roles = authnAccountService.roles(username);
+    List<Role> roles = authnAccountService.accountRoles(username);
     
     List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
     for (Role role : roles) {
diff --git a/gateway/src/main/resources/application.yml b/gateway/src/main/resources/application.yml
index c72cbc7..1f3be47 100644
--- a/gateway/src/main/resources/application.yml
+++ b/gateway/src/main/resources/application.yml
@@ -44,7 +44,7 @@
         filters:
         - RewritePath=/api/system/(?<suffix>.*), /$\{suffix}
       - id: biz-api
-        uri: http://localhost:8082
+        uri: http://localhost:8083
         predicates:
         - Path=/api/biz/**
         filters:
@@ -72,14 +72,13 @@
 
 infras.security.jwt.public-key-pem: |-
   -----BEGIN PUBLIC KEY-----
-  MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBQw6TmvJ+nOuRaLoHsZJGIBzRg/wbskNv6UevL3/nQioYooptPfdIHVzPiKRVT5+DW5+nqzav3DOxY+HYKjO9nFjYdj0sgvRae6iVpa5Ji1wbDKOvwIDNukgnKbqvFXX2Isfl0RxeN3uEKdjeFGGFdr38I3ADCNKFNxtbmfqvjQIDAQAB
+  MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgzXhvHLKypr+G+gJgOJNt8Lu8ygFFCU0eO4qJ4j2vDzpGwTOWKmD/u7dwIWKyHR43hUSN+FN4SSy1AmHjEKxz0btm7Cki+0YFw0BE4/mB/0wPD251wOS3w0CLsRTfoov9OaGaXApjVSMM74aIX8D46CbwHioLHdAj0/jlVU6gZQIDAQAB
   -----END PUBLIC KEY-----
 infras.security.jwt.private-key-pem-pkcs8: |-
   -----BEGIN PRIVATE KEY-----
-  MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMFDDpOa8n6c65FougexkkYgHNGD/BuyQ2/pR68vf+dCKhiiim0990gdXM+IpFVPn4Nbn6erNq/cM7Fj4dgqM72cWNh2PSyC9Fp7qJWlrkmLXBsMo6/AgM26SCcpuq8VdfYix+XRHF43e4Qp2N4UYYV2vfwjcAMI0oU3G1uZ+q+NAgMBAAECgYA7jA7UuhxXmMAYmJ0hO7xnMQPQJouqeP3AYK9+sfMF7WQNHR/r0vj7Vli/dUm1I4hxr+x8fAuomf+ve6gds7sm+v2JHLzEIyPPiogoC7IcBmjJ3yVzW/26cXeOmTiPC/fW2g4BpYxSM8HLDaSkrtqzy8e9ijlzMpHBvvwLikufnQJBAOXaqIPuZ7Vm/JwQHAmX2HV+Qk6GMi/H7mL8X0AaW68w+Iccdbz1hzmMBfdn5NMmx2AOwoBAVivgjt0a1OfksHMCQQDXPtXxwFy4dQ4TbPu8L38P8s/bPo9ib1YkEMp57yBw+IvxB7jnpA9rUYTfZM/HpVP7r9rfVEUylVXXzhz1qx//AkEApWJOTBdW8bQ3YEdLFS/3pJqDNSLjq3OMuBZkpqgQfh6bRAQbRynW8XYpuNk9URye6iPUmRkxp4J86ORseqoWtwJAJb5a/b1hhObhxP5DVkht23oUgLmDoxsq28AmASOxaJ3szCMyhUv7eDIfPp0K4lNXWrcHhkncqHYPS3xVD68mOQJAV4SRDdWpgAbQOUODotohE48RxrabHo0l228CJ/pnm0q7gplPs4iSNJ2eijFuOMXfKkq3z/vxiNSA59FcdoCOHQ==
+  MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKDNeG8csrKmv4b6AmA4k23wu7zKAUUJTR47ioniPa8POkbBM5YqYP+7t3AhYrIdHjeFRI34U3hJLLUCYeMQrHPRu2bsKSL7RgXDQETj+YH/TA8PbnXA5LfDQIuxFN+ii/05oZpcCmNVIwzvhohfwPjoJvAeKgsd0CPT+OVVTqBlAgMBAAECgYAtNxlyROOKkJCyZ4JbhA0QkOx5PWP2AZOJuLxP4SnvG50LYDAdPXVg82u1P+38n2truTF5qiXuYMUNcMoNixayWEZ074kVTI+FluLO405wwMYHvGPKOJVFIUTsKz+xkg4r48R963D5DZ6ZjoPIjLWvxL1zdrsgi9AOz/skAl0yAQJBANO0yadz1fYinSmYa2O27lgE1DpTvYBXGkY2qG7D/QJv2FwP6pdBy9ejym45UXce4wR1Yrlvh9wsErI4p790XOECQQDCcjti2nbIuZP3Dy5Ej97Y6sIbIEu5MpJW8kBjUzUssxgdE9urA/yWVzT8lmj34he+uWJv6s+e/HBDV5tc0tAFAkBK+q2s4+a0jN/SuovWPhS+Eb/EhKIKEU9Z7MPMrxctxMUBHhX8yi3SyszIKv7CTKskihqUCH86qFVaz5wBv8mhAkBgnQea13ebxnGZmSZhFKciWoq1lbdqPpFtuBJ8B5TtL9N0ZzCHaYSwYoZGVqmzONiZgF1DxIUCtuVE4JumZGzNAkB5B1sUdZfLo4q3jOiX5UQ/a4u17ptemvFPR4OynHkuVkgyAfTIo9SAB8/KIntHMlrgcP03G41ciJrYeP5zv8xm
   -----END PRIVATE KEY-----
 
-
 ##
 # infras.security cas
 #