From: 刘洪青 Date: Wed, 4 Dec 2019 06:01:36 +0000 (+0800) Subject: feat: 新增基于 zuul 的网关 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=a2e4efba3b08f761c3ab9267a76eb6b7a2aadf7c;p=institute%2Fsw-backend.git feat: 新增基于 zuul 的网关 --- diff --git a/pom.xml b/pom.xml index d3c0f58..f9cf939 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ admin-bff gateway + zuul 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 @@ + + + 4.0.0 + + + com.supwisdom.buildcommons + spring-cloud-parent + Finchley.RELEASE-1.1 + + + com.supwisdom.institute + sw-backend-zuul + 0.0.2-SNAPSHOT + jar + + Supwisdom Backend Framework Zuul + Supwisdom Backend Framework Zuul project + + + UTF-8 + 1.8 + + -Dfile.encoding=UTF-8 + + true + true + + ${java.version} + ${java.version} + + 1.4.8 + harbor.supwisdom.com + sw-admin-framework + + 0.1.2-SNAPSHOT + + 0.0.2-SNAPSHOT + + 2.9.2 + + com.supwisdom.institute.backend.zuul.Application + + + + + supwisdom-releases + internal release + https://app.supwisdom.com/nexus/content/repositories/releases + + + supwisdom-snapshots + internal snapshots + https://app.supwisdom.com/nexus/content/repositories/snapshots + + + + + + + true + + supwisdom + https://app.supwisdom.com/nexus/content/groups/public/ + + + + false + + central + http://repo.maven.apache.org/maven2 + + + + + + + + + com.supwisdom.infras + infras-bom + ${infras.version} + pom + import + + + + com.supwisdom.institute + sw-backend-common-core + ${sw-backend-common.version} + + + + com.supwisdom.institute + sw-backend-common-utils + ${sw-backend-common.version} + + + + com.supwisdom.institute + sw-backend-common-framework + ${sw-backend-common.version} + + + + + + com.alibaba + fastjson + 1.2.56 + + + + + + + + + + + org.projectlombok + lombok + provided + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + com.supwisdom.infras + infras-security + + + + io.jsonwebtoken + jjwt + 0.9.1 + + + + org.springframework.security + spring-security-cas + + + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + + + + + + commons-codec + commons-codec + + + + com.alibaba + fastjson + + + + + + org.springframework.boot + spring-boot-devtools + runtime + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + ${project.artifactId} + + + + + com.spotify + dockerfile-maven-plugin + ${dockerfile-maven-plugin.version} + + ${dockerfile.image.server}/${dockerfile.image.prefix}/${project.artifactId} + ${project.version} + true + + ${project.build.finalName}.${project.packaging} + ${project.version} + ${project.artifactId} + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + org.apache.maven.plugins + maven-release-plugin + + + org.jacoco + jacoco-maven-plugin + + + + org.springframework.boot + spring-boot-maven-plugin + + false + + + + + com.spotify + dockerfile-maven-plugin + + false + + + + + + + + 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 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 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 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 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 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 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 listener = new MyFilterInvocationSecurityMetadataSourceRefreshListener(); +// listener.setSecurityMetadataSource(securityMetadataSource()); +// +// ServletListenerRegistrationBean refreshListener = +// new ServletListenerRegistrationBean(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> routesFromAuthn = new AtomicReference<>(); + + + @Override + public void refresh() { + doRefresh(); + } + + @Override + protected void doRefresh() { + Map 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 locateRoutes() { + log.debug("locateRoutes"); + LinkedHashMap routesMap = new LinkedHashMap<>(); + // 从application.properties中加载路由信息 + routesMap.putAll(super.locateRoutes()); + // 从db中加载路由信息 + routesMap.putAll(this.routesFromAuthn.get()); // locateRoutesFromAuthn(),防止多次请求后端 + // 优化一下配置 + LinkedHashMap values = new LinkedHashMap<>(); + for (Map.Entry 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 locateRoutesFromAuthn() { + Map routes = new LinkedHashMap<>(); + +// List 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 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 authorities = new ArrayList(); + authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); + authorities.add(new SimpleGrantedAuthority("administrator")); + authorities.add(new SimpleGrantedAuthority("user")); + + Map attributes = new HashMap(); + + 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 authorities, + Map 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 authorities, + Map 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 roles = authnAccountService.accountRoles(username); + + List authorities = new ArrayList(); + for (Role role : roles) { + authorities.add(new SimpleGrantedAuthority(role.getCode())); + } + + Map attributes = new HashMap(); + 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 roles = authzService.loadAccountApplicationRoles(username); + + List authorities = new ArrayList(); + for (Role role : roles) { + authorities.add(new SimpleGrantedAuthority(role.getCode())); + } + + Map attributes = new HashMap(); + 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 configAttributes) + throws AccessDeniedException, InsufficientAuthenticationException { + + if (null == configAttributes || configAttributes.size() <= 0) { + return; + } + + ConfigAttribute ca; + String needRole; + for (Iterator 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> requestMap = null; + + private void loadRequestMap() { + if (requestMap == null) { + requestMap = new LinkedHashMap>(); + + AntPathRequestMatcher requestMatcher0 = new AntPathRequestMatcher("/api/**"); + Collection attributes0 = new ArrayList(); + attributes0.add(new SecurityConfig("user")); + requestMap.put(requestMatcher0, attributes0); + +// AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/web/**"); +// Collection attributes = new ArrayList(); +// attributes.add(new SecurityConfig("user")); +// requestMap.put(requestMatcher, attributes); + } + } + + /** + * 获取当前请求关联的所有角色code {@link SecurityConfig} + * 用于和用户拥有的角色code 进行比对 + */ + @Override + public Collection getAttributes(Object object) throws IllegalArgumentException { + + if (requestMap == null) { + loadRequestMap(); + } + + HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); + + RequestMatcher requestMatcher; + for(Iterator iter = requestMap.keySet().iterator(); iter.hasNext(); ) { + requestMatcher = iter.next(); + + if(requestMatcher.matches(request)) { + return requestMap.get(requestMatcher); + } + } + + return null; + } + + @Override + public Collection 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> requestMap = null; + + public void refreshRequestMap() { + + log.info("MyFilterInvocationSecurityMetadataSource.refreshRequestMap"); + + requestMap = null; + loadRequestMap(); + } + + private void loadRequestMap() { + synchronized (MyFilterInvocationSecurityMetadataSource.class) { + + if (requestMap == null) { + requestMap = new LinkedHashMap>(); + +// List 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 attributes = new ArrayList(); +// +// 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 getAttributes(Object object) throws IllegalArgumentException { + + if (requestMap == null) { + loadRequestMap(); + } + + HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); + + RequestMatcher requestMatcher; + for (Iterator iter = requestMap.keySet().iterator(); iter.hasNext();) { + requestMatcher = iter.next(); + + if (requestMatcher.matches(request)) { + return requestMap.get(requestMatcher); + } + } + + return null; + } + + @Override + public Collection 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 noneSecurityUrl = new HashSet(); // 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 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(), new HashMap()); + + + private String username; + + private List roles; + + private Map 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 = new InheritableThreadLocal(); + + 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