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