feat: 新增基于 zuul 的网关
diff --git a/pom.xml b/pom.xml
index d3c0f58..f9cf939 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,6 +22,7 @@
     <module>admin-bff</module>
 
     <module>gateway</module>
+    <module>zuul</module>
   </modules>
 
   <properties>
diff --git a/zuul/.gitignore b/zuul/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/zuul/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/zuul/Dockerfile b/zuul/Dockerfile
new file mode 100644
index 0000000..f7bf86e
--- /dev/null
+++ b/zuul/Dockerfile
@@ -0,0 +1,18 @@
+FROM harbor.supwisdom.com/institute/openjdk:8-jre-alpine
+
+ENV ENABLE_JMX_SSL=false
+ENV JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -Dspring.profiles.active=docker
+ENV SPRING_PROFILES_ACTIVE=docker
+
+ARG NAME
+ARG VERSION
+ARG JAR_FILE
+
+LABEL name=$NAME \
+      version=$VERSION
+
+EXPOSE 8080
+
+EXPOSE 8443
+
+COPY --chown=java-app:java-app target/${JAR_FILE} /home/java-app/lib/app.jar
diff --git a/zuul/pom.xml b/zuul/pom.xml
new file mode 100644
index 0000000..dc01e70
--- /dev/null
+++ b/zuul/pom.xml
@@ -0,0 +1,296 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.buildcommons</groupId>
+    <artifactId>spring-cloud-parent</artifactId>
+    <version>Finchley.RELEASE-1.1</version>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-zuul</artifactId>
+  <version>0.0.2-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Zuul</name>
+  <description>Supwisdom Backend Framework Zuul project</description>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <java.version>1.8</java.version>
+
+    <argLine>-Dfile.encoding=UTF-8</argLine>
+
+    <downloadSources>true</downloadSources>
+    <downloadJavadocs>true</downloadJavadocs>
+
+    <maven.compiler.source>${java.version}</maven.compiler.source>
+    <maven.compiler.target>${java.version}</maven.compiler.target>
+
+    <dockerfile-maven-plugin.version>1.4.8</dockerfile-maven-plugin.version>
+    <dockerfile.image.server>harbor.supwisdom.com</dockerfile.image.server>
+    <dockerfile.image.prefix>sw-admin-framework</dockerfile.image.prefix>
+
+    <infras.version>0.1.2-SNAPSHOT</infras.version>
+    
+    <sw-backend-common.version>0.0.2-SNAPSHOT</sw-backend-common.version>
+
+    <io.springfox.version>2.9.2</io.springfox.version>
+
+    <start-class>com.supwisdom.institute.backend.zuul.Application</start-class>
+  </properties>
+
+  <distributionManagement>
+    <repository>
+      <id>supwisdom-releases</id>
+      <name>internal release</name>
+      <url>https://app.supwisdom.com/nexus/content/repositories/releases</url>
+    </repository>
+    <snapshotRepository>
+      <id>supwisdom-snapshots</id>
+      <name>internal snapshots</name>
+      <url>https://app.supwisdom.com/nexus/content/repositories/snapshots</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <repositories>
+    <repository>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+      <id>supwisdom</id>
+      <url>https://app.supwisdom.com/nexus/content/groups/public/</url>
+    </repository>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>central</id>
+      <url>http://repo.maven.apache.org/maven2</url>
+    </repository>
+  </repositories>
+
+
+  <dependencyManagement>
+    <dependencies>
+
+      <dependency>
+        <groupId>com.supwisdom.infras</groupId>
+        <artifactId>infras-bom</artifactId>
+        <version>${infras.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-core</artifactId>
+        <version>${sw-backend-common.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-utils</artifactId>
+        <version>${sw-backend-common.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-framework</artifactId>
+        <version>${sw-backend-common.version}</version>
+      </dependency>
+
+      <!-- <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpclient</artifactId>
+        <version>4.5.7</version>
+      </dependency> -->
+
+      <dependency>
+        <groupId>com.alibaba</groupId>
+        <artifactId>fastjson</artifactId>
+        <version>1.2.56</version>
+      </dependency>
+
+
+    </dependencies>
+  </dependencyManagement>
+
+
+
+  <dependencies>
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+    
+
+    <!-- <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-mvc</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-object-mapper</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-i18n</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-lang</artifactId>
+    </dependency> -->
+
+    <dependency>
+      <groupId>com.supwisdom.infras</groupId>
+      <artifactId>infras-security</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>io.jsonwebtoken</groupId>
+      <artifactId>jjwt</artifactId>
+      <version>0.9.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.security</groupId>
+      <artifactId>spring-security-cas</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
+    </dependency>
+
+
+    <!-- <dependency>
+      <groupId>org.springframework.cloud</groupId>
+      <artifactId>spring-cloud-starter-openfeign</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.netflix.feign</groupId>
+      <artifactId>feign-httpclient</artifactId>
+    </dependency> -->
+
+
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.alibaba</groupId>
+      <artifactId>fastjson</artifactId>
+    </dependency>
+
+
+    <!-- 热部署,无需重启项目 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>com.spotify</groupId>
+          <artifactId>dockerfile-maven-plugin</artifactId>
+          <version>${dockerfile-maven-plugin.version}</version>
+          <configuration>
+            <repository>${dockerfile.image.server}/${dockerfile.image.prefix}/${project.artifactId}</repository>
+            <tag>${project.version}</tag>
+            <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+            <buildArgs>
+              <JAR_FILE>${project.build.finalName}.${project.packaging}</JAR_FILE>
+              <VERSION>${project.version}</VERSION>
+              <NAME>${project.artifactId}</NAME>
+            </buildArgs>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/Application.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/Application.java
new file mode 100644
index 0000000..b36b06e
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/Application.java
@@ -0,0 +1,49 @@
+package com.supwisdom.institute.backend.zuul;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+import com.supwisdom.infras.security.configure.basic.EnableInfrasBasicApi;
+import com.supwisdom.infras.security.configure.cas.EnableInfrasCasSecurity;
+import com.supwisdom.infras.security.configure.jwt.EnableInfrasJWTApi;
+import com.supwisdom.institute.base.transmit.annotation.EnableSimpleUserTransmitZuul;
+
+@SpringBootApplication
+
+@EnableZuulProxy
+@EnableSimpleUserTransmitZuul
+
+@EnableInfrasCasSecurity
+
+@EnableInfrasBasicApi
+@EnableInfrasJWTApi
+
+@EnableScheduling
+public class Application {
+
+  public static void main(String[] args) {
+    SpringApplication.run(Application.class, args);
+  }
+  
+  @Bean
+  public CorsFilter corsFilter() {
+      final CorsConfiguration config = new CorsConfiguration();
+      //config.setAllowCredentials(true);
+      config.addAllowedOrigin("*");
+      config.addAllowedHeader("*");
+      config.addAllowedMethod("*");
+
+      final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+      source.registerCorsConfiguration("/jwt/token/**", config);
+      source.registerCorsConfiguration("/api/**", config);
+
+      return new CorsFilter(source);
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/model/Role.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/model/Role.java
new file mode 100644
index 0000000..e1fd12e
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/model/Role.java
@@ -0,0 +1,41 @@
+package com.supwisdom.institute.backend.zuul.agent.poa.model;
+
+import java.io.Serializable;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@ToString
+public class Role implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -273297347381636597L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  @Getter
+  @Setter
+  private String code;
+
+  @Getter
+  @Setter
+  private String name;
+  
+  @Getter
+  @Setter
+  private String description;
+
+  @Getter
+  @Setter
+  private Boolean enabled;
+
+  @Getter
+  @Setter
+  private String externalId;
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/model/UserInfoModel.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/model/UserInfoModel.java
new file mode 100644
index 0000000..ab8027c
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/model/UserInfoModel.java
@@ -0,0 +1,124 @@
+package com.supwisdom.institute.backend.zuul.agent.poa.model;
+
+import java.io.Serializable;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+@ToString
+public class UserInfoModel implements Serializable {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -822608981040243176L;
+
+  @Getter
+  @Setter
+  private String id;
+
+  @Getter
+  @Setter
+  private String accountName;
+
+  @Getter
+  @Setter
+  private String identityTypeId;
+  @Getter
+  @Setter
+  private String identityTypeCode;
+  @Getter
+  @Setter
+  private String identityTypeName;
+  
+  @Getter
+  @Setter
+  private String organizationId;
+  @Getter
+  @Setter
+  private String organizationCode;
+  @Getter
+  @Setter
+  private String organizationName;
+  
+  @Getter
+  @Setter
+  private String uid;
+
+  @Getter
+  @Setter
+  private String name;
+
+  @Getter
+  @Setter
+  private String fullNameSpelling;
+  @Getter
+  @Setter
+  private String nameSpelling;
+
+  @Getter
+  @Setter
+  private String certificateTypeId;
+  @Getter
+  @Setter
+  private String certificateTypeCode;
+  @Getter
+  @Setter
+  private String certificateTypeName;
+  @Getter
+  @Setter
+  private String certificateNumber;
+
+  @Getter
+  @Setter
+  private String genderId;
+  @Getter
+  @Setter
+  private String genderCode;
+  @Getter
+  @Setter
+  private String genderName;
+
+  @Getter
+  @Setter
+  private String nationId;
+  @Getter
+  @Setter
+  private String nationCode;
+  @Getter
+  @Setter
+  private String nationName;
+
+  @Getter
+  @Setter
+  private String countryId;
+  @Getter
+  @Setter
+  private String countryCode;
+  @Getter
+  @Setter
+  private String countryName;
+
+  @Getter
+  @Setter
+  private String addressId;
+  @Getter
+  @Setter
+  private String addressCode;
+  @Getter
+  @Setter
+  private String addressName;
+
+  @Getter
+  @Setter
+  private String phoneNumber;
+  @Getter
+  @Setter
+  private String email;
+
+  @Getter
+  @Setter
+  private String imageUrl;
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/remote/configuration/AgentPoaRestTemplateConfig.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/remote/configuration/AgentPoaRestTemplateConfig.java
new file mode 100644
index 0000000..fee8167
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/remote/configuration/AgentPoaRestTemplateConfig.java
@@ -0,0 +1,100 @@
+package com.supwisdom.institute.backend.zuul.agent.poa.remote.configuration;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.util.ResourceUtils;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class AgentPoaRestTemplateConfig {
+
+  @Bean
+  public ClientHttpRequestFactory simpleClientHttpRequestFactory(
+      @Value("${sw-backend-agent-poa.client-auth.enabled:false}") boolean enabled,
+      @Value("${sw-backend-agent-poa.client-auth.key-password:}") String keyPassword,
+      @Value("${sw-backend-agent-poa.client-auth.key-store:}") String keyStore,
+      @Value("${sw-backend-agent-poa.client-auth.key-store-password:}") String keyStorePassword,
+      @Value("${sw-backend-agent-poa.client-auth.trust-store:}") String trustStore,
+      @Value("${sw-backend-agent-poa.client-auth.trust-store-password:}") String trustStorePassword
+  ) {
+    if (!enabled) {
+      SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      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
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    } else {
+      try {
+        sslContextBuilder
+          .loadKeyMaterial(
+              ResourceUtils.getFile(keyStore),
+              keyStorePassword.toCharArray(),
+              keyPassword.toCharArray());
+      } 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
+    return factory;
+  }
+  
+  @Bean(name = "agentPoaRestTemplate")
+  public RestTemplate agentPoaRestTemplate(ClientHttpRequestFactory requestFactory) {
+    return new RestTemplate(requestFactory);
+  }
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/remote/web/client/AgentPoaRemoteClient.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/remote/web/client/AgentPoaRemoteClient.java
new file mode 100644
index 0000000..39b7166
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/remote/web/client/AgentPoaRemoteClient.java
@@ -0,0 +1,61 @@
+package com.supwisdom.institute.backend.zuul.agent.poa.remote.web.client;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import com.alibaba.fastjson.JSONObject;
+
+@Slf4j
+@Component
+public class AgentPoaRemoteClient {
+  
+  @Autowired
+  private RestTemplate agentPoaRestTemplate;
+
+  @Value(value = "${sw-backend-agent-poa.uri}/v1/poa")
+  private String url;
+  
+  private JSONObject defaultErrorJson(Throwable cause) {
+    JSONObject error = new JSONObject();
+    
+    error.put("code", -1);
+    error.put("message", cause.getMessage());
+    error.put("error", cause.getMessage());
+    
+    return error;
+  }
+
+  public JSONObject loadUserInfoByAccountName(String accountName) {
+    try {
+      final String path = "/user/users/accountName/{accountName}";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{accountName}"}, new String[] {accountName});
+      log.debug(url);
+      
+      return agentPoaRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      //e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject loadAccountApplicationRoles(String username) {
+    try {
+      final String path = "/authz/roles/account/{username}";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return agentPoaRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      //e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/service/AuthzService.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/service/AuthzService.java
new file mode 100644
index 0000000..ee96527
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/service/AuthzService.java
@@ -0,0 +1,42 @@
+package com.supwisdom.institute.backend.zuul.agent.poa.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.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.zuul.agent.poa.model.Role;
+import com.supwisdom.institute.backend.zuul.agent.poa.remote.web.client.AgentPoaRemoteClient;
+
+@Slf4j
+@Service
+public class AuthzService {
+  
+  @Autowired
+  private AgentPoaRemoteClient agentPoaRemoteClient;
+  
+  public List<Role> loadAccountApplicationRoles(String username) {
+
+    JSONObject jsonObject = agentPoaRemoteClient.loadAccountApplicationRoles(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      JSONArray roleArray = data.getJSONArray("roles");
+      
+      List<Role> roles = roleArray.toJavaList(Role.class);
+      log.debug("roles: [{}]", roles);
+      
+      return roles;
+    }
+    
+    return null;
+  }
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/service/UserService.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/service/UserService.java
new file mode 100644
index 0000000..3773344
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/agent/poa/service/UserService.java
@@ -0,0 +1,38 @@
+package com.supwisdom.institute.backend.zuul.agent.poa.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.zuul.agent.poa.model.UserInfoModel;
+import com.supwisdom.institute.backend.zuul.agent.poa.remote.web.client.AgentPoaRemoteClient;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+public class UserService {
+
+  @Autowired
+  private AgentPoaRemoteClient agentPoaRemoteClient;
+  
+  public UserInfoModel loadUserInfoByAccountName(String accountName) {
+    
+    JSONObject jsonObject = agentPoaRemoteClient.loadUserInfoByAccountName(accountName);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      UserInfoModel userInfoModel = data.toJavaObject(UserInfoModel.class);
+      log.debug("userInfoModel: [{}]", userInfoModel);
+      
+      return userInfoModel;
+    }
+    
+    return null;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Account.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Account.java
new file mode 100644
index 0000000..325924a
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Account.java
@@ -0,0 +1,70 @@
+package com.supwisdom.institute.backend.zuul.authn.model;
+
+import com.supwisdom.institute.base.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Account extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4889952442290543101L;
+
+  private String id;
+
+  /**
+   * 用户名
+   */
+  private String username;
+  
+  /**
+   * 密码
+   */
+  private String password;
+
+  /**
+   * 是否可用,1 可用,0 不可用,默认:1
+   */
+  private Boolean enabled;
+  /**
+   * 账号未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean accountNonExpired;
+  /**
+   * 账号未锁定,1 未锁定,0 锁定,默认:1
+   */
+  private Boolean accountNonLocked;
+  /**
+   * 密码未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean credentialsNonExpired;
+
+  /**
+   * 姓名
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 登录手机
+   */
+  private String mobile;
+  /**
+   * 登录邮箱
+   */
+  private String email;
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Permission.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Permission.java
new file mode 100644
index 0000000..fd4e6b5
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Permission.java
@@ -0,0 +1,69 @@
+package com.supwisdom.institute.backend.zuul.authn.model;
+
+import com.supwisdom.institute.base.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Permission extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3042842657207449148L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 类型(1 应用,2 菜单,3 操作)
+   */
+  private String type;
+
+  /**
+   * 菜单图标
+   */
+  private String icon;
+
+  /**
+   * URL地址
+   */
+  private String url;
+
+  /**
+   * 系统ID
+   */
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  private Integer order;
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/PermissionRoleSet.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/PermissionRoleSet.java
new file mode 100644
index 0000000..9f4d94a
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/PermissionRoleSet.java
@@ -0,0 +1,5 @@
+package com.supwisdom.institute.backend.zuul.authn.model;
+
+public class PermissionRoleSet {
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/ResourceRoleSet.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/ResourceRoleSet.java
new file mode 100644
index 0000000..90b1298
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/ResourceRoleSet.java
@@ -0,0 +1,80 @@
+package com.supwisdom.institute.backend.zuul.authn.model;
+
+import java.util.Collection;
+
+import com.supwisdom.institute.base.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/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Role.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Role.java
new file mode 100644
index 0000000..f291d19
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Role.java
@@ -0,0 +1,39 @@
+package com.supwisdom.institute.backend.zuul.authn.model;
+
+import com.supwisdom.institute.base.model.ABaseModel;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Role extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8551951601186240995L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Route.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Route.java
new file mode 100644
index 0000000..c7d7dfb
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/model/Route.java
@@ -0,0 +1,68 @@
+package com.supwisdom.institute.backend.zuul.authn.model;
+
+import com.supwisdom.institute.base.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/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/remote/configuration/AuthnRestTemplateConfig.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/remote/configuration/AuthnRestTemplateConfig.java
new file mode 100644
index 0000000..fd1db01
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/remote/configuration/AuthnRestTemplateConfig.java
@@ -0,0 +1,100 @@
+package com.supwisdom.institute.backend.zuul.authn.remote.configuration;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.util.ResourceUtils;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class AuthnRestTemplateConfig {
+
+  @Bean
+  public ClientHttpRequestFactory simpleClientHttpRequestFactory(
+      @Value("${sw-backend-base-api.client-auth.enabled:false}") boolean enabled,
+      @Value("${sw-backend-base-api.client-auth.key-password:}") String keyPassword,
+      @Value("${sw-backend-base-api.client-auth.key-store:}") String keyStore,
+      @Value("${sw-backend-base-api.client-auth.key-store-password:}") String keyStorePassword,
+      @Value("${sw-backend-base-api.client-auth.trust-store:}") String trustStore,
+      @Value("${sw-backend-base-api.client-auth.trust-store-password:}") String trustStorePassword
+  ) {
+    if (!enabled) {
+      SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      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
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    } else {
+      try {
+        sslContextBuilder
+          .loadKeyMaterial(
+              ResourceUtils.getFile(keyStore),
+              keyStorePassword.toCharArray(),
+              keyPassword.toCharArray());
+      } 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
+    return factory;
+  }
+  
+  @Bean(name = "authnRestTemplate")
+  public RestTemplate authnRestTemplate(ClientHttpRequestFactory requestFactory) {
+    return new RestTemplate(requestFactory);
+  }
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/remote/web/client/AuthnRemoteClient.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/remote/web/client/AuthnRemoteClient.java
new file mode 100644
index 0000000..d9e36e5
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/remote/web/client/AuthnRemoteClient.java
@@ -0,0 +1,154 @@
+package com.supwisdom.institute.backend.zuul.authn.remote.web.client;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import com.alibaba.fastjson.JSONObject;
+
+@Slf4j
+@Component
+public class AuthnRemoteClient {
+  
+  @Autowired
+  private RestTemplate authnRestTemplate;
+  
+  @Value(value = "${sw-backend-base-api.uri}/v1/authn")
+  private String url;
+  
+  private JSONObject defaultErrorJson(Throwable cause) {
+    JSONObject error = new JSONObject();
+    
+    error.put("code", -1);
+    error.put("message", cause.getMessage());
+    error.put("error", cause.getMessage());
+    
+    return error;
+  }
+
+  public JSONObject account(String username) {
+    
+    try {
+      final String path = "/{username}/account";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  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 authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  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 authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  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 authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  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 authnRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  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 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();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/service/AuthnService.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/service/AuthnService.java
new file mode 100644
index 0000000..1644811
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/authn/service/AuthnService.java
@@ -0,0 +1,92 @@
+package com.supwisdom.institute.backend.zuul.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.zuul.authn.model.Account;
+import com.supwisdom.institute.backend.zuul.authn.model.ResourceRoleSet;
+import com.supwisdom.institute.backend.zuul.authn.model.Role;
+import com.supwisdom.institute.backend.zuul.authn.model.Route;
+import com.supwisdom.institute.backend.zuul.authn.remote.web.client.AuthnRemoteClient;
+
+@Slf4j
+@Service
+public class AuthnService {
+  
+  @Autowired
+  private AuthnRemoteClient authnRemoteClient;
+  
+  public Account account(String username) {
+    
+    JSONObject jsonObject = authnRemoteClient.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 = authnRemoteClient.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 = authnRemoteClient.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 = authnRemoteClient.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/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/InfrasFilterSecurityInterceptorConfig.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/InfrasFilterSecurityInterceptorConfig.java
new file mode 100644
index 0000000..a443263
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/InfrasFilterSecurityInterceptorConfig.java
@@ -0,0 +1,63 @@
+package com.supwisdom.institute.backend.zuul.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+
+import com.supwisdom.infras.security.web.access.intercept.InfrasFilterSecurityInterceptor;
+import com.supwisdom.institute.backend.zuul.security.web.access.MyAccessDecisionManager;
+import com.supwisdom.institute.backend.zuul.security.web.access.intercept.InMemeryFilterInvocationSecurityMetadataSource;
+import com.supwisdom.institute.backend.zuul.security.web.access.intercept.MyFilterSecurityInterceptor;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class InfrasFilterSecurityInterceptorConfig {
+
+//  @Bean
+//  public FilterInvocationSecurityMetadataSource securityMetadataSource() {
+//    MyFilterInvocationSecurityMetadataSource securityMetadataSource = new MyFilterInvocationSecurityMetadataSource();
+//    log.debug("InfrasFilterSecurityInterceptorConfig securityMetadataSource is {}", securityMetadataSource);
+//
+//    return securityMetadataSource;
+//  }
+  
+  @Bean
+  public FilterInvocationSecurityMetadataSource securityMetadataSource() {
+    InMemeryFilterInvocationSecurityMetadataSource securityMetadataSource = new InMemeryFilterInvocationSecurityMetadataSource();
+    log.debug("InfrasFilterSecurityInterceptorConfig securityMetadataSource is {}", securityMetadataSource);
+
+    return securityMetadataSource;
+  }
+  
+  
+  @Bean
+  public AccessDecisionManager accessDecisionManager() {
+    MyAccessDecisionManager accessDecisionManager = new MyAccessDecisionManager();
+    log.debug("InfrasFilterSecurityInterceptorConfig accessDecisionManager is {}", accessDecisionManager);
+
+    return accessDecisionManager;
+  }
+  
+  @Bean
+  public InfrasFilterSecurityInterceptor infrasFilterSecurityInterceptor() throws Exception {
+    MyFilterSecurityInterceptor myFilterSecurityInterceptor = new MyFilterSecurityInterceptor();
+    myFilterSecurityInterceptor.setRejectPublicInvocations(false);
+    log.debug("InfrasFilterSecurityInterceptorConfig infrasFilterSecurityInterceptor is {}", myFilterSecurityInterceptor);
+
+    return myFilterSecurityInterceptor;
+  }
+  
+//  @Bean
+//  public ServletListenerRegistrationBean<MyFilterInvocationSecurityMetadataSourceRefreshListener> myFilterInvocationSecurityMetadataSourceRefreshListener(){
+//    MyFilterInvocationSecurityMetadataSourceRefreshListener listener = new MyFilterInvocationSecurityMetadataSourceRefreshListener();
+//    listener.setSecurityMetadataSource(securityMetadataSource());
+//    
+//    ServletListenerRegistrationBean<MyFilterInvocationSecurityMetadataSourceRefreshListener> refreshListener = 
+//        new ServletListenerRegistrationBean<MyFilterInvocationSecurityMetadataSourceRefreshListener>(listener);
+//    return refreshListener;
+//  }
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/PasswordEncoderConfig.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/PasswordEncoderConfig.java
new file mode 100644
index 0000000..2031b9c
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/PasswordEncoderConfig.java
@@ -0,0 +1,29 @@
+package com.supwisdom.institute.backend.zuul.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class PasswordEncoderConfig {
+
+  @Bean
+  public PasswordEncoder passwordEncoder() {
+    
+    PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+
+    if (passwordEncoder instanceof DelegatingPasswordEncoder) {
+      ((DelegatingPasswordEncoder)passwordEncoder).setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance());
+    }
+
+    log.debug("PasswordEncoderConfig passwordEncoder is {}", passwordEncoder);
+    return passwordEncoder;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/RouteConfiguration.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/RouteConfiguration.java
new file mode 100644
index 0000000..4e118d0
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/RouteConfiguration.java
@@ -0,0 +1,38 @@
+package com.supwisdom.institute.backend.zuul.configuration;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.supwisdom.institute.backend.zuul.route.RouteLocator;
+import com.supwisdom.institute.backend.zuul.route.listener.RouteRefreshListener;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+//@Configuration
+public class RouteConfiguration {
+
+  @Autowired
+  ServerProperties server;
+
+  @Autowired
+  ZuulProperties zuulProperties;
+
+  @Bean
+  public RouteLocator routeLocator() {
+    RouteLocator routeLocator = new RouteLocator(this.server.getServlet().getServletPrefix(), this.zuulProperties);
+    log.debug("RouteConfiguration routeLocator is {}", routeLocator);
+    
+    return routeLocator;
+  }
+  
+  
+  @Bean
+  public RouteRefreshListener routeRefreshListener() {
+    return new RouteRefreshListener();
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/UserDetailsServiceConfig.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/UserDetailsServiceConfig.java
new file mode 100644
index 0000000..6f171ac
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/UserDetailsServiceConfig.java
@@ -0,0 +1,45 @@
+package com.supwisdom.institute.backend.zuul.configuration;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+import com.supwisdom.institute.backend.zuul.security.core.userdetails.InMemeryUserDetailsService;
+import com.supwisdom.institute.backend.zuul.security.core.userdetails.MyUserDetailsService;
+import com.supwisdom.institute.backend.zuul.security.core.userdetails.PoaUserDetailsService;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class UserDetailsServiceConfig {
+
+  @Bean
+  @ConditionalOnProperty(name = "sw-backend-gateway.security.core.userdetails.service.impl", havingValue = "memery", matchIfMissing = true)
+  public InMemeryUserDetailsService inMemeryUserDetailsService() throws Exception {
+    InMemeryUserDetailsService inMemeryUserDetailsService = new InMemeryUserDetailsService();
+    log.debug("UserDetailsServiceConfig inMemeryUserDetailsService is {}", inMemeryUserDetailsService);
+
+    return inMemeryUserDetailsService;
+  }
+  
+  @Bean
+  @ConditionalOnProperty(name = "sw-backend-gateway.security.core.userdetails.service.impl", havingValue = "poa", matchIfMissing = false)
+  public PoaUserDetailsService poaUserDetailsService() throws Exception {
+    PoaUserDetailsService poaUserDetailsService = new PoaUserDetailsService();
+    log.debug("UserDetailsServiceConfig poaUserDetailsService is {}", poaUserDetailsService);
+
+    return poaUserDetailsService;
+  }
+
+  @Bean
+  @ConditionalOnProperty(name = "sw-backend-gateway.security.core.userdetails.service.impl", havingValue = "base", matchIfMissing = false)
+  public MyUserDetailsService myUserDetailsService() throws Exception {
+    MyUserDetailsService myUserDetailsService = new MyUserDetailsService();
+    log.debug("UserDetailsServiceConfig myUserDetailsService is {}", myUserDetailsService);
+
+    return myUserDetailsService;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/ZuulConfiguration.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/ZuulConfiguration.java
new file mode 100644
index 0000000..4c3cb9a
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/configuration/ZuulConfiguration.java
@@ -0,0 +1,75 @@
+package com.supwisdom.institute.backend.zuul.configuration;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ResourceUtils;
+
+import com.supwisdom.institute.backend.zuul.filters.pre.IdTokenPreFilter;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class ZuulConfiguration {
+
+  @Bean
+  public IdTokenPreFilter idTokenPreFilter() {
+    log.debug("-----IdTokenPreFilter");
+    return new IdTokenPreFilter();
+  }
+  
+  @Bean
+  public CloseableHttpClient httpClient(
+      @Value("${zuul-httpclient.client-auth.enabled:false}") boolean enabled,
+      @Value("${zuul-httpclient.client-auth.key-password:}") String keyPassword,
+      @Value("${zuul-httpclient.client-auth.key-store:}") String keyStore,
+      @Value("${zuul-httpclient.client-auth.key-store-password:}") String keyStorePassword) {
+    
+    if (!enabled) {
+      return HttpClients.custom().build();
+    }
+
+    SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+    
+    // 如果 服务端启用了 TLS 客户端验证,则需要指定 keyStore
+    if (keyStore == null || keyStore.isEmpty()) {
+    } else {
+      try {
+        sslContextBuilder
+          .loadKeyMaterial(
+              ResourceUtils.getFile(keyStore),
+              keyStorePassword.toCharArray(),
+              keyPassword.toCharArray());
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+
+    try {
+      SSLContext sslContext = sslContextBuilder.build();
+      
+      SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
+          sslContext,
+          SSLConnectionSocketFactory.getDefaultHostnameVerifier());
+      
+      CloseableHttpClient httpClient = HttpClients.custom()
+          .setSSLSocketFactory(sslsf)
+          .build();
+      
+      return httpClient;
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    
+    return HttpClients.custom().build();
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/filters/pre/IdTokenPreFilter.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/filters/pre/IdTokenPreFilter.java
new file mode 100644
index 0000000..7fb4e7b
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/filters/pre/IdTokenPreFilter.java
@@ -0,0 +1,63 @@
+package com.supwisdom.institute.backend.zuul.filters.pre;
+
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+import com.supwisdom.infras.security.authentication.JwtAuthenticationToken;
+
+import lombok.extern.slf4j.Slf4j;
+
+import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+@Slf4j
+public class IdTokenPreFilter extends ZuulFilter {
+
+  @Override
+  public boolean shouldFilter() {
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+    if (authentication == null) {
+      return false;
+    }
+    
+    if (authentication.isAuthenticated()) {
+      return true;
+    }
+    
+    return false;
+  }
+
+  @Override
+  public Object run() throws ZuulException {
+    RequestContext ctx = RequestContext.getCurrentContext();
+    
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+    if (JwtAuthenticationToken.class.isInstance(authentication)) {
+      JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
+      
+      String token = jwtAuthenticationToken.getToken();
+      log.debug("token is {}", token);
+
+      // ctx.addZuulRequestHeader("userToken", token);
+
+      ctx.addZuulRequestHeader("X-FORWARD-GATEWAY", "personal-security-zuul");
+      ctx.addZuulRequestHeader("X-FORWARD-ID-TOKEN", token);
+      log.debug("set to zuul header[X-FORWARD-ID-TOKEN]: ok");
+    }
+    
+    return null;
+  }
+
+  @Override
+  public String filterType() {
+    return PRE_TYPE;
+  }
+
+  @Override
+  public int filterOrder() {
+    return 100;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/route/RouteLocator.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/route/RouteLocator.java
new file mode 100644
index 0000000..040f13e
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/route/RouteLocator.java
@@ -0,0 +1,119 @@
+package com.supwisdom.institute.backend.zuul.route;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
+import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
+import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
+import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
+import org.springframework.util.StringUtils;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class RouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
+
+  private ZuulProperties properties;
+
+  public RouteLocator(String servletPath, ZuulProperties properties) {
+    super(servletPath, properties);
+    this.properties = properties;
+  }
+  
+  private AtomicReference<Map<String, ZuulRoute>> routesFromAuthn = new AtomicReference<>();
+  
+
+  @Override
+  public void refresh() {
+    doRefresh();
+  }
+  
+  @Override
+  protected void doRefresh() {
+    Map<String, ZuulProperties.ZuulRoute> routesMap = locateRoutesFromAuthn();
+    if (routesMap == null || routesMap.isEmpty()) {
+      log.debug("skip");
+      return;
+    }
+    
+    super.doRefresh();
+  }
+  
+//  @Override
+//  public Route getMatchingRoute(String path) {
+//    return super.getMatchingRoute(path); 
+//  }
+  
+  
+  @Override
+  protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
+    log.debug("locateRoutes");
+    LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>();
+    // 从application.properties中加载路由信息
+    routesMap.putAll(super.locateRoutes());
+    // 从db中加载路由信息
+    routesMap.putAll(this.routesFromAuthn.get()); // locateRoutesFromAuthn(),防止多次请求后端
+    // 优化一下配置
+    LinkedHashMap<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
+    for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : routesMap.entrySet()) {
+      String path = entry.getKey();
+      // Prepend with slash if not already present.
+      
+//      String verb = "GET";
+//      int idx = path.indexOf(" ");
+//      if (idx != -1) {
+//        verb = path.substring(0, idx);
+//        path = path.substring(idx + 1);
+//      }
+
+      if (!path.startsWith("/")) {
+        path = "/" + path;
+      }
+      if (StringUtils.hasText(this.properties.getPrefix())) {
+        path = this.properties.getPrefix() + path;
+        if (!path.startsWith("/")) {
+          path = "/" + path;
+        }
+      }
+
+      //values.put(verb.toUpperCase() + " " + path, entry.getValue());
+      values.put(path, entry.getValue());
+    }
+    
+    return values;
+  }
+
+  private Map<String, ZuulProperties.ZuulRoute> locateRoutesFromAuthn() {
+    Map<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<>();
+
+//    List<com.supwisdom.institute.personal.security.center.zuul.sa.authn.model.Route> routeList = authnService.routes();
+//    if (routeList != null) {
+//      for (com.supwisdom.institute.personal.security.center.zuul.sa.authn.model.Route r : routeList) {
+//        String id = r.getId();
+//        String path = r.getPathPrefix() + "/**";
+//        String serviceId = null;
+//        String url = r.getUrl();
+//        boolean stripPrefix = r.getStripPrefix();
+//        boolean retryable = true;
+//        Set<String> sensitiveHeaders = null;
+//  
+//        log.debug(path + " = " + url);
+//  
+//        ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute(id, path, serviceId, url, stripPrefix, retryable, sensitiveHeaders);
+//        routes.put(zuulRoute.getPath(), zuulRoute);
+//      }
+//    }
+    
+    if (routes != null && !routes.isEmpty()) {
+      this.routesFromAuthn.set(routes);
+    }
+    
+    return routes;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/route/listener/RouteRefreshListener.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/route/listener/RouteRefreshListener.java
new file mode 100644
index 0000000..b896b6a
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/route/listener/RouteRefreshListener.java
@@ -0,0 +1,62 @@
+package com.supwisdom.institute.backend.zuul.route.listener;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
+import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
+import org.springframework.context.ApplicationEventPublisher;
+
+public class RouteRefreshListener implements InitializingBean, DisposableBean {
+
+  @Autowired
+  ApplicationEventPublisher publisher;
+
+  @Autowired
+  RouteLocator routeLocator;
+
+  @Value("${admin-center-zuul.route.refresh.delay:5}")
+  private int delay = 5; // 启动后,延迟 5 秒
+  @Value("${admin-center-zuul.route.refresh.period:120}")
+  private int period = 120; // 定时,每隔 120 秒
+  
+  private Timer beanRefreshTimer;
+
+  @Override
+  public void destroy() throws Exception {
+    if (beanRefreshTimer != null) {
+      beanRefreshTimer.cancel();
+    }
+  }
+
+  @Override
+  public void afterPropertiesSet() throws Exception {
+    refresh();
+  }
+  
+  private void refresh() {
+    beanRefreshTimer = new Timer("定时刷新路由信息", true);
+    
+    beanRefreshTimer.scheduleAtFixedRate(new TimerTask() {
+      
+      @Override
+      public void run() {
+        try {
+          refreshRoute();
+        } catch (Exception e) {
+          
+        }
+      }
+    }, 1000 * delay, 1000 * period);
+  }
+
+  public void refreshRoute() {
+    RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(routeLocator);
+    publisher.publishEvent(routesRefreshedEvent);
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/InMemeryUserDetailsService.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/InMemeryUserDetailsService.java
new file mode 100644
index 0000000..22fc735
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/InMemeryUserDetailsService.java
@@ -0,0 +1,42 @@
+package com.supwisdom.institute.backend.zuul.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class InMemeryUserDetailsService implements UserDetailsService {
+
+  @Autowired
+  PasswordEncoder passwordEncoder;
+  
+  @Override
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+    
+    log.debug("InMemeryUserDetailsService.loadUserByUsername({})", username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
+    authorities.add(new SimpleGrantedAuthority("administrator"));
+    authorities.add(new SimpleGrantedAuthority("user"));
+
+    Map<String, Object> attributes = new HashMap<String, Object>();
+    
+    MyUser myUser = new MyUser(username, passwordEncoder.encode(username), authorities, attributes);
+    log.debug("myUser is {}", myUser);
+    
+    return myUser;
+  }
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/MyUser.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/MyUser.java
new file mode 100644
index 0000000..f8ba4d3
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/MyUser.java
@@ -0,0 +1,42 @@
+package com.supwisdom.institute.backend.zuul.security.core.userdetails;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.springframework.security.core.GrantedAuthority;
+
+import com.supwisdom.infras.security.core.userdetails.InfrasUser;
+
+public class MyUser extends InfrasUser {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = 3195151947212484499L;
+
+  public MyUser(String username, String password, 
+      Collection<? extends GrantedAuthority> authorities, 
+      Map<String, Object> attributes) {
+    this(username, password, true, true, true, true, authorities, attributes);
+  }
+
+  public MyUser(String username, String password, 
+      boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, 
+      Collection<? extends GrantedAuthority> authorities, 
+      Map<String, Object> attributes) {
+    super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, attributes);
+  }
+  
+  public Object getAttribute(String key) {
+    if (this.getAttributes() == null) {
+      return null;
+    }
+    
+    if (!this.getAttributes().containsKey(key)) {
+      return null;
+    }
+    
+    return this.getAttributes().get(key);
+  }
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/MyUserDetailsService.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/MyUserDetailsService.java
new file mode 100644
index 0000000..8aa5f28
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/MyUserDetailsService.java
@@ -0,0 +1,71 @@
+package com.supwisdom.institute.backend.zuul.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import com.supwisdom.institute.backend.zuul.authn.model.Account;
+import com.supwisdom.institute.backend.zuul.authn.model.Role;
+import com.supwisdom.institute.backend.zuul.security.core.userdetails.MyUser;
+import com.supwisdom.institute.backend.zuul.authn.service.AuthnService;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MyUserDetailsService implements UserDetailsService {
+  
+  @Autowired
+  PasswordEncoder passwordEncoder;
+  
+  @Autowired
+  private AuthnService authnAccountService;
+
+  @Override
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO: 
+
+    log.debug("MyUserDetailsService.loadUserByUsername({})", username);
+    
+    MyUser myUser = loadUser(username);
+
+    return myUser;
+  }
+
+
+  private MyUser loadUser(String username) {
+
+    Account account = authnAccountService.account(username);
+    if (account == null) {
+      throw new UsernameNotFoundException(String.format("%s not found", username));
+    }
+    
+    List<Role> roles = authnAccountService.accountRoles(username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    for (Role role : roles) {
+      authorities.add(new SimpleGrantedAuthority(role.getCode()));
+    }
+    
+    Map<String, Object> attributes = new HashMap<String, Object>();
+    attributes.put("accountId", account.getId());
+    attributes.put("accountName", account.getName());
+    //attributes.put("accountMobile", account.getMobile());
+    //attributes.put("accountEmail", account.getEmail());
+
+    MyUser myUser = new MyUser(account.getUsername(), passwordEncoder.encode(account.getPassword()), 
+        account.getEnabled(), account.getAccountNonExpired(), account.getCredentialsNonExpired(), account.getAccountNonLocked(), 
+        authorities, attributes);
+    log.debug("myUser is {}", myUser);
+
+    return myUser;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/PoaUserDetailsService.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/PoaUserDetailsService.java
new file mode 100644
index 0000000..e5779f2
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/core/userdetails/PoaUserDetailsService.java
@@ -0,0 +1,86 @@
+package com.supwisdom.institute.backend.zuul.security.core.userdetails;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import com.supwisdom.institute.backend.zuul.agent.poa.model.Role;
+import com.supwisdom.institute.backend.zuul.agent.poa.model.UserInfoModel;
+import com.supwisdom.institute.backend.zuul.agent.poa.service.AuthzService;
+import com.supwisdom.institute.backend.zuul.agent.poa.service.UserService;
+
+@Slf4j
+public class PoaUserDetailsService implements UserDetailsService {
+
+  @Autowired
+  PasswordEncoder passwordEncoder;
+  
+  @Autowired
+  UserService userService;
+  @Autowired
+  AuthzService authzService;
+
+  @Override
+  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+
+    log.debug("PoaUserDetailsService.loadUserByUsername({})", username);
+    
+    MyUser myUser = loadUser(username);
+
+    return myUser;
+  }
+
+  private MyUser loadUser(String username) {
+    
+    UserInfoModel userInfoModel = userService.loadUserInfoByAccountName(username);
+    if (userInfoModel == null) {
+      throw new UsernameNotFoundException(String.format("%s not found", username));
+    }
+    
+    List<Role> roles = authzService.loadAccountApplicationRoles(username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    for (Role role : roles) {
+      authorities.add(new SimpleGrantedAuthority(role.getCode()));
+    }
+    
+    Map<String, Object> attributes = new HashMap<String, Object>();
+    attributes.put("accountId", userInfoModel.getId());
+    attributes.put("accountName", userInfoModel.getAccountName());
+
+    attributes.put("identityTypeId", userInfoModel.getIdentityTypeId());
+    attributes.put("identityTypeCode", userInfoModel.getIdentityTypeCode());
+    attributes.put("identityTypeName", userInfoModel.getIdentityTypeName());
+
+    attributes.put("organizationId", userInfoModel.getOrganizationId());
+    attributes.put("organizationCode", userInfoModel.getOrganizationCode());
+    attributes.put("organizationName", userInfoModel.getOrganizationName());
+    
+    attributes.put("uid", userInfoModel.getUid());
+    attributes.put("name", userInfoModel.getName());
+    attributes.put("genderName", userInfoModel.getGenderName());
+    attributes.put("nationName", userInfoModel.getNationName());
+    attributes.put("countryName", userInfoModel.getCountryName());
+    attributes.put("addressName", userInfoModel.getAddressName());
+
+    attributes.put("imageUrl", userInfoModel.getImageUrl());
+
+    MyUser myUser = new MyUser(userInfoModel.getAccountName(), passwordEncoder.encode(StringUtils.reverse(userInfoModel.getAccountName())), 
+        authorities, attributes);
+    log.debug("myUser is {}", myUser);
+
+    return myUser;
+  }
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/listener/MyFilterInvocationSecurityMetadataSourceRefreshListener.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/listener/MyFilterInvocationSecurityMetadataSourceRefreshListener.java
new file mode 100644
index 0000000..2493a16
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/listener/MyFilterInvocationSecurityMetadataSourceRefreshListener.java
@@ -0,0 +1,66 @@
+package com.supwisdom.institute.backend.zuul.security.listener;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.servlet.ServletContextEvent;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.web.context.ContextLoaderListener;
+
+import com.supwisdom.institute.backend.zuul.security.web.access.intercept.MyFilterInvocationSecurityMetadataSource;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MyFilterInvocationSecurityMetadataSourceRefreshListener extends ContextLoaderListener {
+
+  private FilterInvocationSecurityMetadataSource securityMetadataSource;
+  public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
+    this.securityMetadataSource = securityMetadataSource;
+  }
+
+  private Timer timer = null;
+
+  @Value("${admin-center-zuul.resource.refresh.delay:5}")
+  private int delay = 5; // 启动后,延迟 5 秒
+  @Value("${admin-center-zuul.resource.refresh.period:20}")
+  private int period = 20; // 定时,每隔 20 秒
+
+  @Override
+  public void contextInitialized(ServletContextEvent event) {
+    // super.contextInitialized(event);
+    log.info("MyFilterInvocationSecurityMetadataSourceRefreshListener.contextInitialized");
+
+    if (securityMetadataSource instanceof MyFilterInvocationSecurityMetadataSource) {
+      timer = new Timer("定时刷新权限信息", true);
+
+      timer.scheduleAtFixedRate(new TimerTask() {
+
+        @Override
+        public void run() {
+          try {
+            ((MyFilterInvocationSecurityMetadataSource) securityMetadataSource).refreshRequestMap();
+          } catch (Exception e) {
+            
+          }
+        }
+
+      }, 1000 * delay, 1000 * period);
+
+    }
+
+  }
+
+  @Override
+  public void contextDestroyed(ServletContextEvent event) {
+    // super.contextDestroyed(event);
+    log.info("MyFilterInvocationSecurityMetadataSourceRefreshListener.contextDestroyed");
+
+    if (timer != null) {
+      timer.cancel();
+    }
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/MyAccessDecisionManager.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/MyAccessDecisionManager.java
new file mode 100644
index 0000000..c1d4796
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/MyAccessDecisionManager.java
@@ -0,0 +1,79 @@
+package com.supwisdom.institute.backend.zuul.security.web.access;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MyAccessDecisionManager implements AccessDecisionManager {
+
+  @Override
+  public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
+      throws AccessDeniedException, InsufficientAuthenticationException {
+
+    if (null == configAttributes || configAttributes.size() <= 0) {
+      return;
+    }
+    
+    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)) {
+          if (authentication.isAuthenticated()) {
+            throw new AccessDeniedException("no right");
+          }
+          
+          return;
+        } else if ("authenticate".equals(access)) {
+          if (!authentication.isAuthenticated()) {
+            throw new AccessDeniedException("no right");
+          }
+          
+          return;
+        } else if ("permitAll".equals(access)) {
+          return;
+        } else if ("denyAll".equals(access)) {
+          throw new AccessDeniedException("no right");
+        }
+        
+        throw new AccessDeniedException("no right");
+      }
+      
+      for (GrantedAuthority ga : authentication.getAuthorities()) { // authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
+        if (needRole.trim().equals(ga.getAuthority())) {
+          return;
+        }
+      }
+    }
+    
+    throw new AccessDeniedException("no right");
+  }
+
+  @Override
+  public boolean supports(ConfigAttribute attribute) {
+    return true;
+  }
+
+  @Override
+  public boolean supports(Class<?> clazz) {
+    return true;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/InMemeryFilterInvocationSecurityMetadataSource.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/InMemeryFilterInvocationSecurityMetadataSource.java
new file mode 100644
index 0000000..04e8541
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/InMemeryFilterInvocationSecurityMetadataSource.java
@@ -0,0 +1,75 @@
+package com.supwisdom.institute.backend.zuul.security.web.access.intercept;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+public class InMemeryFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
+  
+  private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = null;
+  
+  private void loadRequestMap() {
+    if (requestMap == null) {
+      requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
+      
+      AntPathRequestMatcher requestMatcher0 = new AntPathRequestMatcher("/api/**");
+      Collection<ConfigAttribute> attributes0 = new ArrayList<ConfigAttribute>();
+      attributes0.add(new SecurityConfig("user"));
+      requestMap.put(requestMatcher0, attributes0);
+
+//      AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/web/**");
+//      Collection<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
+//      attributes.add(new SecurityConfig("user"));
+//      requestMap.put(requestMatcher, attributes);
+    }
+  }
+
+  /**
+   * 获取当前请求关联的所有角色code {@link SecurityConfig} 
+   * 用于和用户拥有的角色code 进行比对
+   */
+  @Override
+  public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
+    
+    if (requestMap == null) {
+      loadRequestMap();
+    }
+
+    HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
+    
+    RequestMatcher requestMatcher;
+    for(Iterator<RequestMatcher> iter = requestMap.keySet().iterator(); iter.hasNext(); ) {
+      requestMatcher = iter.next();
+      
+      if(requestMatcher.matches(request)) {
+        return requestMap.get(requestMatcher);
+      }
+    }
+    
+    return null;
+  }
+
+  @Override
+  public Collection<ConfigAttribute> getAllConfigAttributes() {
+
+    return null;
+  }
+
+  @Override
+  public boolean supports(Class<?> clazz) {
+
+    return true;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/MyFilterInvocationSecurityMetadataSource.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/MyFilterInvocationSecurityMetadataSource.java
new file mode 100644
index 0000000..0248ad8
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/MyFilterInvocationSecurityMetadataSource.java
@@ -0,0 +1,114 @@
+package com.supwisdom.institute.backend.zuul.security.web.access.intercept;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
+  
+  private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = null;
+
+  public void refreshRequestMap() {
+    
+    log.info("MyFilterInvocationSecurityMetadataSource.refreshRequestMap");
+    
+    requestMap = null;
+    loadRequestMap();
+  }
+  
+  private void loadRequestMap() {
+    synchronized (MyFilterInvocationSecurityMetadataSource.class) {
+
+      if (requestMap == null) {
+        requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
+        
+//        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.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 {
+//              continue;
+//            }
+//            
+//            requestMap.put(requestMatcher, attributes);
+//          }
+//        }
+        
+      }
+      
+    }
+  }
+
+  /**
+   * 获取当前请求关联的所有角色code {@link SecurityConfig} 用于和用户拥有的角色code 进行比对
+   */
+  @Override
+  public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
+
+    if (requestMap == null) {
+      loadRequestMap();
+    }
+
+    HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
+
+    RequestMatcher requestMatcher;
+    for (Iterator<RequestMatcher> iter = requestMap.keySet().iterator(); iter.hasNext();) {
+      requestMatcher = iter.next();
+
+      if (requestMatcher.matches(request)) {
+        return requestMap.get(requestMatcher);
+      }
+    }
+
+    return null;
+  }
+
+  @Override
+  public Collection<ConfigAttribute> getAllConfigAttributes() {
+
+    return null;
+  }
+
+  @Override
+  public boolean supports(Class<?> clazz) {
+
+    return true;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/MyFilterSecurityInterceptor.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/MyFilterSecurityInterceptor.java
new file mode 100644
index 0000000..17188fe
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/security/web/access/intercept/MyFilterSecurityInterceptor.java
@@ -0,0 +1,77 @@
+package com.supwisdom.institute.backend.zuul.security.web.access.intercept;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.servlet.ServletException;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.AccessDecisionManager;
+import org.springframework.security.access.SecurityMetadataSource;
+import org.springframework.security.access.intercept.InterceptorStatusToken;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+
+import com.supwisdom.infras.security.web.access.intercept.InfrasFilterSecurityInterceptor;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MyFilterSecurityInterceptor extends InfrasFilterSecurityInterceptor {
+  
+  @Autowired
+  private FilterInvocationSecurityMetadataSource securityMetadataSource;
+
+  @Autowired
+  public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
+
+    super.setAccessDecisionManager(accessDecisionManager);
+  }
+
+  @Override
+  public void invoke(FilterInvocation fi) throws IOException, ServletException {
+    
+    Set<String> noneSecurityUrl = new HashSet<String>();  // FIXME: 对无须访问控制的url,支持可配置
+    noneSecurityUrl.add("/web/login");
+    noneSecurityUrl.add("/web/logout");
+    noneSecurityUrl.add("/web/index");
+
+    if (fi.getRequest() != null) {
+      String requestUrl = fi.getRequestUrl(); log.debug("MyFilterSecurityInterceptor invoke requestUrl: {}", requestUrl);
+      if (noneSecurityUrl.contains(requestUrl)) {
+        fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+        return;
+      }
+    }
+
+//    if (AuthenticationUtil.isAdministrator()) {
+//      fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+//      return;
+//    }
+
+    // fi里面有一个被拦截的url
+    // 里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
+    // 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
+    InterceptorStatusToken token = super.beforeInvocation(fi);
+    try {
+      // 执行下一个拦截器
+      fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+    } finally {
+      super.afterInvocation(token, null);
+    }
+  }
+
+  @Override
+  public Class<?> getSecureObjectClass() {
+
+    return FilterInvocation.class;
+  }
+
+  @Override
+  public SecurityMetadataSource obtainSecurityMetadataSource() {
+
+    return this.securityMetadataSource;
+  }
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/backend/zuul/utils/AuthenticationUtil.java b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/utils/AuthenticationUtil.java
new file mode 100644
index 0000000..4da5113
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/backend/zuul/utils/AuthenticationUtil.java
@@ -0,0 +1,91 @@
+package com.supwisdom.institute.backend.zuul.utils;
+
+import java.util.Collection;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.supwisdom.institute.backend.zuul.security.core.userdetails.MyUser;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class AuthenticationUtil {
+
+  public static String currentUsername() {
+
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+    if (authentication == null) {
+      log.error("authentication is null");
+      return null;
+    }
+
+    log.debug("authentication is {}", authentication.getPrincipal());
+
+    if (!authentication.isAuthenticated()) {
+      log.error("authentication is not authenticated");
+      return null;
+    }
+
+    if (authentication.getPrincipal() == null) {
+      log.error("authentication's principal is null");
+      return null;
+    }
+
+    log.debug("authentication's principal is {}", authentication.getPrincipal());
+
+    if (authentication.getPrincipal() instanceof MyUser) {
+      return ((MyUser) authentication.getPrincipal()).getUsername();
+    }
+
+    return null;
+  }
+
+  public static MyUser currentUser() {
+
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+    if (authentication == null) {
+      log.error("authentication is null");
+      return null;
+    }
+
+    log.debug("authentication is {}", authentication.getPrincipal());
+
+    if (!authentication.isAuthenticated()) {
+      log.error("authentication is not authenticated");
+      return null;
+    }
+
+    if (authentication.getPrincipal() == null) {
+      log.error("authentication's principal is null");
+      return null;
+    }
+
+    log.debug("authentication's principal is {}", authentication.getPrincipal());
+
+    if (authentication.getPrincipal() instanceof MyUser) {
+      return (MyUser) authentication.getPrincipal();
+    }
+
+    return null;
+  }
+
+  public static boolean isAdministrator() {
+    MyUser myUser = AuthenticationUtil.currentUser();
+    if (myUser != null) {
+      Collection<GrantedAuthority> grantedAuthoritys = myUser.getAuthorities();
+      if (grantedAuthoritys != null && grantedAuthoritys.size() > 0) {
+        for (GrantedAuthority grantedAuthority : grantedAuthoritys) {
+          if ("administrator".equals(grantedAuthority.getAuthority())) {
+            return true;
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/base/model/ABaseModel.java b/zuul/src/main/java/com/supwisdom/institute/base/model/ABaseModel.java
new file mode 100644
index 0000000..998877a
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/base/model/ABaseModel.java
@@ -0,0 +1,10 @@
+package com.supwisdom.institute.base.model;
+
+public abstract class ABaseModel implements IModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8906238738400652654L;
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/base/model/IModel.java b/zuul/src/main/java/com/supwisdom/institute/base/model/IModel.java
new file mode 100644
index 0000000..4eb447d
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/base/model/IModel.java
@@ -0,0 +1,7 @@
+package com.supwisdom.institute.base.model;
+
+import java.io.Serializable;
+
+public interface IModel extends Serializable {
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/base/transmit/annotation/EnableSimpleUserTransmitZuul.java b/zuul/src/main/java/com/supwisdom/institute/base/transmit/annotation/EnableSimpleUserTransmitZuul.java
new file mode 100644
index 0000000..78a32b3
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/base/transmit/annotation/EnableSimpleUserTransmitZuul.java
@@ -0,0 +1,19 @@
+package com.supwisdom.institute.base.transmit.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.context.annotation.Import;
+
+import com.supwisdom.institute.base.transmit.config.SimpleUserTransmitZuulConfiguration;
+
+@Documented
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Import({SimpleUserTransmitZuulConfiguration.class})
+public @interface EnableSimpleUserTransmitZuul {
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/base/transmit/config/SimpleUserTransmitZuulConfiguration.java b/zuul/src/main/java/com/supwisdom/institute/base/transmit/config/SimpleUserTransmitZuulConfiguration.java
new file mode 100644
index 0000000..b85badd
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/base/transmit/config/SimpleUserTransmitZuulConfiguration.java
@@ -0,0 +1,20 @@
+package com.supwisdom.institute.base.transmit.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.supwisdom.institute.base.transmit.zuul.SimpleUserTransmitPreFilter;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Configuration
+public class SimpleUserTransmitZuulConfiguration {
+  
+  @Bean
+  public SimpleUserTransmitPreFilter simpleUserTransmitPreFilter() {
+    log.debug("-----SimpleUserTransmitPreFilter");
+    return new SimpleUserTransmitPreFilter();
+  }
+  
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/base/transmit/user/User.java b/zuul/src/main/java/com/supwisdom/institute/base/transmit/user/User.java
new file mode 100644
index 0000000..1665bea
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/base/transmit/user/User.java
@@ -0,0 +1,26 @@
+package com.supwisdom.institute.base.transmit.user;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+@AllArgsConstructor
+public class User {
+
+  public static String KEY_USER_IN_HTTP_HEADER = "X-FORWARD-USER";
+
+  public static User ANONYMOUS = new User("anonymous", new ArrayList<String>(), new HashMap<String, Object>());
+  
+  
+  private String username;
+  
+  private List<String> roles;
+  
+  private Map<String, Object> attributes;
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/base/transmit/user/UserContext.java b/zuul/src/main/java/com/supwisdom/institute/base/transmit/user/UserContext.java
new file mode 100644
index 0000000..2d7eb21
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/base/transmit/user/UserContext.java
@@ -0,0 +1,31 @@
+package com.supwisdom.institute.base.transmit.user;
+
+public class UserContext {
+  
+  private static ThreadLocal<User> user = new InheritableThreadLocal<User>();
+  
+  private UserContext() {
+    
+  }
+  
+  public static User getUser() {
+    return user.get();
+  }
+  
+  public static void setUser(User value) {
+    user.set(value);
+  }
+  
+  
+  
+  public static String getUsername() {
+    User u = user.get();
+    
+    if (u == null) {
+      u = User.ANONYMOUS;
+    }
+    
+    return u.getUsername();
+  }
+
+}
diff --git a/zuul/src/main/java/com/supwisdom/institute/base/transmit/zuul/SimpleUserTransmitPreFilter.java b/zuul/src/main/java/com/supwisdom/institute/base/transmit/zuul/SimpleUserTransmitPreFilter.java
new file mode 100644
index 0000000..49604aa
--- /dev/null
+++ b/zuul/src/main/java/com/supwisdom/institute/base/transmit/zuul/SimpleUserTransmitPreFilter.java
@@ -0,0 +1,77 @@
+package com.supwisdom.institute.base.transmit.zuul;
+
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.alibaba.fastjson.JSONObject;
+import com.netflix.zuul.ZuulFilter;
+import com.netflix.zuul.context.RequestContext;
+import com.netflix.zuul.exception.ZuulException;
+import com.supwisdom.infras.security.authentication.converter.InfrasUserConverter;
+import com.supwisdom.infras.security.core.userdetails.InfrasUser;
+import com.supwisdom.institute.base.transmit.user.User;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class SimpleUserTransmitPreFilter extends ZuulFilter {
+  
+  @Autowired
+  private InfrasUserConverter infrasUserConverter;
+
+  @Override
+  public boolean shouldFilter() {
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+    if (authentication == null) {
+      return false;
+    }
+    
+    if (authentication.isAuthenticated()) {
+      return true;
+    }
+    
+    return false;
+  }
+
+  @Override
+  public Object run() throws ZuulException {
+    RequestContext ctx = RequestContext.getCurrentContext();
+    
+    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+    InfrasUser infrasUser = infrasUserConverter.convert(authentication);
+    
+    if (infrasUser != null) {
+      try {
+        User user = new User(infrasUser.getUsername(), infrasUser.getRoles(), infrasUser.getAttributes());
+        
+        String jsonUser = JSONObject.toJSONString(user);
+        log.debug(jsonUser);
+        
+        //String headerValue = URLEncoder.encode(jsonUser,"UTF-8");
+        String headerValue = Base64.encodeBase64URLSafeString(jsonUser.getBytes("UTF-8"));
+        log.debug(headerValue);
+        
+        ctx.addZuulRequestHeader(User.KEY_USER_IN_HTTP_HEADER, headerValue);
+        
+        log.debug("User set to zuul header: ok");
+      } catch (Exception e) {
+        log.warn("User set to zuul header: error", e);
+      }
+    }
+    
+    return null;
+  }
+
+  @Override
+  public String filterType() {
+    return "pre";
+  }
+
+  @Override
+  public int filterOrder() {
+    return 9999;
+  }
+
+}
diff --git a/zuul/src/main/resources/application-docker.yml b/zuul/src/main/resources/application-docker.yml
new file mode 100644
index 0000000..9c603b5
--- /dev/null
+++ b/zuul/src/main/resources/application-docker.yml
@@ -0,0 +1,129 @@
+server:
+  port: ${SERVER_PORT:8443}
+  ssl:
+    enabled: ${SSL_ENABLED:true}
+    clientAuth: NEED
+    key-store: ${SSL_KEYSTORE_FILE:file:/certs/server/server.keystore}
+    key-store-password: ${SSL_KEYSTORE_PASSWORD:}
+    trust-store: ${SSL_TRUSTSTORE_FILE:file:/certs/server/server.truststore}
+    trust-store-password: ${SSL_TRUSTSTORE_PASSWORD:}
+  tomcat: 
+    accept-count: ${SERVER_TOMCAT_ACCEPT_COUNT:100}
+    max-connections: ${SERVER_TOMCAT_MAX_CONNECTIONS:10000}
+    max-threads: ${SERVER_TOMCAT_MAX_THREADS:200}
+    min-spare-threads: ${SERVER_TOMCAT_MIN_SPARE_THREADS:10}
+    accesslog: 
+      enabled: ${TOMCAT_ACCESSLOG_ENABLED:false}
+      buffered: ${TOMCAT_ACCESSLOG_BUFFERED:true}
+      directory: ${TOMCAT_ACCESSLOG_DIR:log}
+      prefix: ${TOMCAT_ACCESSLOG_PREFIX:sa-api-accesslog}
+      suffix: ${TOMCAT_ACCESSLOG_SUFFIX:.log}
+      file-date-format: ${TOMCAT_ACCESSLOG_FILE_DATE_FORMAT:.yyyy-MM-dd}
+      rotate: ${TOMCAT_ACCESSLOG_ROTATE:true}
+
+
+## logging
+logging:
+  level:
+    root: INFO
+
+
+spring:
+  jackson:
+    time-zone: ${JACKSON_TIME_ZONE:Asia/Shanghai}
+
+
+zuul:
+  routes:
+    bff-api:
+      url: ${SW_BACKEND_BFF_API_URL:https://sw-backend-admin-bff}
+    base-api:
+      url: ${SW_BACKEND_BASE_API_URL:https://sw-backend-admin-sa}
+    system-api:
+      url: ${SW_BACKEND_BASE_API_URL:https://sw-backend-admin-sa}
+    biz-api:
+      url: ${SW_BACKEND_BIZ_API_URL:https://sw-backend-biz-sa}
+
+
+##
+# online-doc
+#
+infras.online-doc.enabled: ${INFRAS_ONLINE_DOC_ENABLED:false}
+infras.online-doc.md-docs.staitc.path: ${INFRAS_ONLINE_DOC_MD_DOCS_STATIC_PATH:/doc/}
+infras.online-doc.api-docs.staitc.path: ${INFRAS_ONLINE_DOC_API_DOCS_STATIC_PATH:/api-docs/}
+
+
+##
+# security basic
+#
+infras.security.basic.enabled: ${INFRAS_SECURITY_BASIC_ENABLED:true}
+
+
+##
+# security jwt
+#
+infras.security.jwt.enabled: ${INFRAS_SECURITY_JWT_ENABLED:false}
+
+#token过期时长,86400 秒(1天)
+infras.security.jwt.expiration: ${INFRAS_SECURITY_JWT_EXPIRATION:86400}
+
+infras.security.jwt.public-key-pem: ${INFRAS_SECURITY_JWT_PUBLIC_KEY_PEM:}
+infras.security.jwt.private-key-pem-pkcs8: ${INFRAS_SECURITY_JWT_PRIVATE_KEY_PEM_PKCS8:}
+
+
+# 
+infras.security.jwt.token.generate.type: ${INFRAS_SECURITY_JWT_TOKEN_GENERATE_TYPE:jwt}
+infras.security.jwt.token.decrypt.key.private-key-pem-pkcs8: ${INFRAS_SECURITY_JWT_TOKEN_DECRYPT_KEY_PRIVATE_KEY_PEM_PKCS8:}
+infras.security.jwt.token.signing.key.url: ${INFRAS_SECURITY_JWT_TOKEN_SIGNING_KEY_URL:}
+
+
+##
+# security cas
+#
+infras.security.cas.enabled: ${INFRAS_SECURITY_CAS_ENABLED:false}
+
+#应用访问地址
+app.server.host.url: ${APP_SERVER_HOST_URL:https://localhost:8443}
+#应用登录地址
+app.login.url: ${APP_LOGIN_URL:/cas/login}
+#应用登出地址
+app.logout.url: ${APP_LOGOUT_URL:/cas/logout}
+
+#CAS服务地址
+cas.server.host.url: ${CAS_SERVER_HOST_URL:}
+
+##
+# 认证时,用户信息服务实现
+# memery 内存,用户名密码一致即可登录,测试用,默认;
+# base 后端base服务;
+# poa 开放平台服务,建议和cas一起使用)
+sw-backend-gateway.security.core.userdetails.service.impl: ${SW_BACKEND_GATEWAY_SECURITY_CORE_USERDETAILS_SERVICE_IMPL:memery}
+
+
+sw-backend-base-api: 
+  uri: ${SW_BACKEND_BASE_API_URI:https://sw-backend-admin-sa}
+  client-auth:
+    enabled: ${SW_BACKEND_BASE_API_CLIENT_AUTH_ENABLED:false}
+    key-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_PASSWORD:}
+    trust-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore}
+    trust-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD:}
+
+sw-backend-agent-poa: 
+  uri: ${SW_BACKEND_AGENT_POA_URI:https://sw-backend-agent}
+  client-auth:
+    enabled: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_ENABLED:false}
+    key-password: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_KEYSTORE_PASSWORD:}
+    trust-store: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore}
+    trust-store-password: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_TRUSTSTORE_PASSWORD:}
+
+
+zuul-httpclient:
+  client-auth:
+    enabled: ${ZUUL_HTTPCLIENT_CLIENT_AUTH_ENABLED:false}
+    key-password: ${ZUUL_HTTPCLIENT_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${ZUUL_HTTPCLIENT_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${ZUUL_HTTPCLIENT_CLIENT_AUTH_KEYSTORE_PASSWORD:}
diff --git a/zuul/src/main/resources/application.yml b/zuul/src/main/resources/application.yml
new file mode 100644
index 0000000..a11b7ed
--- /dev/null
+++ b/zuul/src/main/resources/application.yml
@@ -0,0 +1,135 @@
+server:
+  port: 8080
+
+
+## logging
+logging:
+  level:
+    root: INFO
+#    com.supwisdom.institute.base: DEBUG
+#    com.supwisdom.institute.admin.center: DEBUG
+#    org.springframework.web: INFO
+#    org.springframework.cloud.openfeign: DEBUG
+
+
+spring:
+  jackson:
+    time-zone: Asia/Shanghai
+
+
+zuul:
+  #sensitiveHeaders: Cookie,Set-Cookie
+  ignored-headers: Access-Control-Allow-Origin,Vary
+  host:
+    socket-timeout-millis: 30000
+    connect-timeout-millis: 2000
+  routes:
+    bff-api:
+      path: /api/bff/**
+      url: http://localhost:8081
+      stripPrefix: true
+    base-api:
+      path: /api/base/**
+      url: http://localhost:8082
+      stripPrefix: true
+    system-api:
+      path: /api/system/**
+      url: http://localhost:8082
+      stripPrefix: true
+    biz-api:
+      path: /api/biz/**
+      url: http://localhost:8083
+      stripPrefix: true
+
+
+infras:
+  mvc:
+    # 自定义error输出的例子
+    custom-error:
+      enabled: true
+      error-map:
+        org.springframework.validation.BindException: Customized Bind Error Reason
+      include-message: true
+      include-errors: true
+      include-error: true
+      include-exception: true
+      include-path: true
+      include-timestamp: true
+      include-status: true
+
+
+##
+# online-doc
+#
+infras.online-doc.enabled: true
+infras.online-doc.md-docs.staitc.path: /Users/loie/c/work/git/institute/admin-center/doc/
+infras.online-doc.api-docs.staitc.path: /Users/loie/c/work/git/institute/admin-center/doc/api-docs/
+
+
+##
+# security basic
+#
+infras.security.basic.enabled: false
+
+
+##
+# security jwt
+#
+infras.security.jwt.enabled: true
+
+infras.security.jwt.expiration: 2592000
+
+infras.security.jwt.public-key-pem: |-
+  -----BEGIN PUBLIC KEY-----
+  MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgzXhvHLKypr+G+gJgOJNt8Lu8ygFFCU0eO4qJ4j2vDzpGwTOWKmD/u7dwIWKyHR43hUSN+FN4SSy1AmHjEKxz0btm7Cki+0YFw0BE4/mB/0wPD251wOS3w0CLsRTfoov9OaGaXApjVSMM74aIX8D46CbwHioLHdAj0/jlVU6gZQIDAQAB
+  -----END PUBLIC KEY-----
+infras.security.jwt.private-key-pem-pkcs8: |-
+  -----BEGIN PRIVATE KEY-----
+  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.jwt.token.generate.type: jwt
+#infras.security.jwt.token.signing.key.url: https://cas-dev.supwisdom.com/cas/jwt/publicKey
+infras.security.jwt.token.signing.key.url: ""
+#infras.security.jwt.token.decrypt.key.private-key-pem-pkcs8: MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDMubzaWCtdqk/38qaKd0fYCd+vfb9icw6kK2+GkDAw1Vgj9KpXLQSnAzgYz9SzuDibTo5GgR9vhrTSlJppDeA+Q1zeTkBvnkLBi1m5iCgCPoKgr7yqxJxuiazCyDVs3qx8Hf732LlHBcEv5+SHwPrVWkY2LWo5xb+8RvmFoaKo3Ksr6j5x87262n9KzplwFZET5ujvuWAM09o1cfrJWS0o2Cy4JSP7afndSMBIn6Qzi0dI3Ec3HlSk8E+3oQH01blBXnS6pPSb4bxYWdqYRG1cp394K60r42eWcep1amk+dTXxK/uRi7VB1HRKebcdZdDwdfE0LNTNqsIlMTn+HusrAgMBAAECggEBAI5slQatmhXCe3m6dMQVsYSJcfVrnO6HruLlWOQbgXsnoPb6qlqVdgwegDM6uvYArljVcMN55v22kCuDuFxni96lDIGXnNpKFpUBNf2NzI+rH0NcnvuKZm28F9U2ZXyE+Sgr0gpo2pSfW0PRproOtjIhaIEeXS0t9nKsScD+ruObyaZPAdJ1Ndb2Z6L1R/TFDsNUwIGQoc6zCidLxWVBd906CnkeFmVX4MCpPnrtGW2ozqDW4pB/aRxoUZZZ6bOjBmqiZ1YPtcHyAG/b1UgDYXSu6mUKrdhUVEkB6KHp92BKuU3DrMxroUtG8Rz2oY/7HBFXT6zZvmn7OneZRRSHKpkCgYEA9Xug3adh2xxtqHtZPb6u+H2SIdtXW7f/WDPKwV5tQc4X8H6uqh7KikLHbzgk5jgw6QHL4IT2+0W8EV5tieHWRY3bxyLAsR1uNQUnUNCr19vbw60fz/iSZb48e6F4wk5YPlHsbddsF3UdIWM1C5PSOJCrHB7uGexW9ZnOytTCHf8CgYEA1X8YsMoryEL1BPrJ/2OvVYPd0kSxU0ODvnXNq9UPi5JZpDviogXSyJ0MeEiRq96aeNDJ+4LQjPT8EnhJtZfy7MEzWdsS2fmh2uAc64kwN58l25t+TUpkhtkt06Zl7euGdOSupCWUOPqmdzoIg8Tu91YfD7OcYR86XGp7kXyQCtUCgYEAg/3TQxsKzKt+csbP9xkeL1IlTrsHP7OxQhWnAU3qZSWRTahv9dzUfn7liPGhNYAlHEPxAWm8+uJF+vjQ4QBjG8bo0yvme9UdOrjoqNVqcIgwpAfkQQigfsBI+RibO66wV+HoxC6+WeaIoTkcfnse33c56cbfs2SWZTwsKnc3YLUCgYEAt6c1Xh8LuqGelEIIMaFW2oEs+AwPXkjds6Ey43XMgYvLgPPi6O2JfPlcGLyUUvySdQtmNO066YZ0sI65GXU0i2VG/yzs8oVDLj1Lo3HIAJDuyBLieypbf4SjX0XsuNW6PCPb92g8MSesuzM4z+FAj5ON9LvU8dcjJQyUb3pvjmECgYEAlPvF5eA9zHGON8IKfMx9J+mDXWk6h3VyMGK9BCjLvR1CfLtXhTTOANX3LlERLV47D9+aCVah2sXUNUBB3cf8IHi9ExVmwmdgZu2ZkseeClgo6Sdyl26GkivvaNTfKZCt/Nkt5VKtU6BUuyp+kt5nWUkmrIIWSEiiQ+tVCB9Y+f8=
+
+
+##
+# security cas
+#
+infras.security.cas.enabled: true
+
+#应用访问地址
+app.server.host.url: http://localhost:8080
+#应用登录地址
+app.login.url: /cas/login
+#应用登出地址
+app.logout.url: /cas/logout
+
+#CAS服务地址
+cas.server.host.url: https://cas-dev.supwisdom.com/cas
+
+
+##
+# 认证时,用户信息服务实现
+# memery 内存,用户名密码一致即可登录,测试用,默认;
+# base 后端base服务;
+# poa 开放平台服务,建议和cas一起使用)
+sw-backend-gateway.security.core.userdetails.service.impl: memery
+
+
+sw-backend-base-api:
+  uri: http://localhost:8082
+
+sw-backend-agent-poa: 
+  uri: http://localhost:8090
+
+
+zuul-httpclient:
+  client-auth:
+    enabled: false
+    key-password: client
+    key-store: file:/Users/loie/c/work/git/institute/admin-center/certs/client/client.keystore
+    key-store-password: client
+
diff --git a/zuul/src/main/resources/bootstrap.yml b/zuul/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..10b3096
--- /dev/null
+++ b/zuul/src/main/resources/bootstrap.yml
@@ -0,0 +1,3 @@
+spring:
+  application:
+    name: sw-backend-zuul