chore: 调整项目结构
diff --git a/thirdparty-agent/agent/.gitignore b/thirdparty-agent/agent/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/thirdparty-agent/agent/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/thirdparty-agent/agent/Dockerfile b/thirdparty-agent/agent/Dockerfile
new file mode 100644
index 0000000..f7bf86e
--- /dev/null
+++ b/thirdparty-agent/agent/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/thirdparty-agent/agent/pom.xml b/thirdparty-agent/agent/pom.xml
new file mode 100644
index 0000000..eecf40b
--- /dev/null
+++ b/thirdparty-agent/agent/pom.xml
@@ -0,0 +1,117 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-thirdparty-agent-parent</artifactId>
+    <version>0.0.2-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-thirdparty-agent</artifactId>
+  <version>0.0.2-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Third Party Agent</name>
+  <description>Supwisdom Backend Framework Third Party Agent project</description>
+
+  <properties>
+    <start-class>com.supwisdom.institute.backend.thirdparty.agent.Application</start-class>
+  </properties>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <!-- 微服务 健康监控 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-actuator</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-thirdparty-poa</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.springfox</groupId>
+      <artifactId>springfox-swagger-ui</artifactId>
+    </dependency>
+
+
+    <!-- 热部署,无需重启项目 -->
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-devtools</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <finalName>${project.artifactId}</finalName>
+
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-maven-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>false</skip>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/thirdparty-agent/agent/src/main/java/com/supwisdom/institute/backend/thirdparty/agent/Application.java b/thirdparty-agent/agent/src/main/java/com/supwisdom/institute/backend/thirdparty/agent/Application.java
new file mode 100644
index 0000000..089732a
--- /dev/null
+++ b/thirdparty-agent/agent/src/main/java/com/supwisdom/institute/backend/thirdparty/agent/Application.java
@@ -0,0 +1,40 @@
+package com.supwisdom.institute.backend.thirdparty.agent;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+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.institute.backend.common.core.transmit.annotation.EnableSimpleUserTransmit;
+import com.supwisdom.institute.backend.common.framework.exception.EnableCustomExceptionHandler;
+
+@SpringBootApplication
+
+@EnableScheduling
+
+@EnableSimpleUserTransmit
+@EnableCustomExceptionHandler
+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("/v2/api-docs", config);
+
+    return new CorsFilter(source);
+  }
+
+}
diff --git a/thirdparty-agent/agent/src/main/java/com/supwisdom/institute/backend/thirdparty/agent/configuration/Swagger2Config.java b/thirdparty-agent/agent/src/main/java/com/supwisdom/institute/backend/thirdparty/agent/configuration/Swagger2Config.java
new file mode 100644
index 0000000..a0a511c
--- /dev/null
+++ b/thirdparty-agent/agent/src/main/java/com/supwisdom/institute/backend/thirdparty/agent/configuration/Swagger2Config.java
@@ -0,0 +1,110 @@
+package com.supwisdom.institute.backend.thirdparty.agent.configuration;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.service.SecurityScheme;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger.web.UiConfiguration;
+import springfox.documentation.swagger.web.UiConfigurationBuilder;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+@Configuration
+@EnableSwagger2
+public class Swagger2Config {
+
+  @Value("${swagger2.apis.basePackage:com.supwisdom.institute}")
+  private String basePackage;
+
+  @Bean
+  public Docket createRestApi() {
+    return new Docket(DocumentationType.SWAGGER_2)
+        .securitySchemes(securitySchemes())
+        .securityContexts(securityContexts())
+        .apiInfo(apiInfo())
+        .select()
+        .apis(RequestHandlerSelectors.basePackage(basePackage))
+        .paths(PathSelectors.any())
+        .build()
+    ;
+  }
+
+  private ApiInfo apiInfo() {
+    Contact contact = new Contact("Backend Third Party Agent", "https://sw-backend-agent.supwisdom.com/swagger-ui.html", ""); // name, url, email
+    return new ApiInfoBuilder()
+        .title("Backend Third Party Agent APIs")
+        .description("管理后台 - 代理接口<br /><br />"
+            + "X-FORWARD-USER(测试用):<br /><br />"
+            + "明文:{\"attributes\":{\"accountId\":\"1\"},\"roles\":[\"ROLE_ADMIN\",\"administrator\",\"user\"],\"username\":\"swadmin\"}<br /><br />"
+            + "Base64:eyJhdHRyaWJ1dGVzIjp7ImFjY291bnRJZCI6IjEifSwicm9sZXMiOlsiUk9MRV9BRE1JTiIsImFkbWluaXN0cmF0b3IiLCJ1c2VyIl0sInVzZXJuYW1lIjoic3dhZG1pbiJ9<br /><br />"
+            + "使用 Base64字符串 进行 Authorize,然后进行接口测试<br /><br />"
+            + "若需要其他帐号,请自行拼接明文,再进行 Base64 编码<br /><br />"
+            + ""
+        )
+        .termsOfServiceUrl("http://www.supwisdom.com/")
+        .contact(contact)
+        .version("1.0")
+        .build();
+  }
+
+  private List<SecurityScheme> securitySchemes() {
+    //return newArrayList(new BasicAuth("sample"));
+    return newArrayList(
+        //new BasicAuth("Basic"),
+        //new ApiKey("JWTToken", "Authorization", "header"), 
+        new ApiKey("SimpleUserTransmit", "X-FORWARD-USER", "header"));
+  }
+  
+  private List<SecurityContext> securityContexts() {
+
+    List<SecurityReference> globalSecurityReference = newArrayList(
+        new SecurityReference("SimpleUserTransmit", new AuthorizationScope[]{new AuthorizationScope("global", "accessEverything")}));
+    
+//    AuthorizationScope[] authScopes = new AuthorizationScope[1];
+//    authScopes[0] = new AuthorizationScopeBuilder()
+//            .scope("read")
+//            .description("read access")
+//            .build();
+//    SecurityReference securityReference = SecurityReference.builder()
+//            .reference("sample")
+//            .scopes(authScopes)
+//            .build();
+
+    return newArrayList(
+        SecurityContext.builder()
+          .securityReferences(newArrayList(globalSecurityReference))
+          .build());
+  }
+
+  @Bean
+  UiConfiguration uiConfig() {
+    
+    return UiConfigurationBuilder.builder().build();
+    
+//    return new UiConfiguration(null, // url
+//        "none", // docExpansion => none | list
+//        "alpha", // apiSorter => alpha
+//        "schema", // defaultModelRendering => schema
+//        UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS,
+//        false, // enableJsonEditor => true || false
+//        true, // showRequestHeaders => true | false
+//        60000L); // requestTimeout => in milliseconds, defaults to null
+//    // (uses jquery xh timeout)
+  }
+
+}
diff --git a/thirdparty-agent/agent/src/main/resources/application-docker.yml b/thirdparty-agent/agent/src/main/resources/application-docker.yml
new file mode 100644
index 0000000..c48d4b2
--- /dev/null
+++ b/thirdparty-agent/agent/src/main/resources/application-docker.yml
@@ -0,0 +1,63 @@
+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: 
+    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
+    com.supwisdom: INFO
+
+
+spring:
+  jackson:
+    time-zone: ${JACKSON_TIME_ZONE:Asia/Shanghai}
+
+##
+# spring.redis
+#
+  redis: 
+    host: ${SPRING_REDIS_HOST:redis-server}
+    port: ${SPRING_REDIS_PORT:6379}
+    password: ${SPRING_REDIS_PASSWORD:}
+
+
+##
+# 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/}
+
+
+## 平台OpenAPI
+poa.server.url: ${POA_SERVER_URL:https://poa.supwisdom.com}
+
+poa.client.id: ${POA_CLIENT_ID:id}
+poa.client.secret: ${POA_CLIENT_SECRET:secret}
+poa.scopes: ${POA_SCOPES:read,write,trust}
+
+## 开放接口地址
+user-data-service.server.url: ${USER_DATA_SERVICE_SERVER_URL:${poa.server.url}/apis/user/v1}
+user-authorization-service.server.url: ${USER_AUTHORIZATION_SERVICE_SERVER_URL:${poa.server.url}/apis/authz/v1}
+
+## 用户授权服务
+user-authorization-service.applicationId: ${USER_AUTHORIZATION_SERVICE_APPLICATION_ID:}
diff --git a/thirdparty-agent/agent/src/main/resources/application.yml b/thirdparty-agent/agent/src/main/resources/application.yml
new file mode 100644
index 0000000..001a0bd
--- /dev/null
+++ b/thirdparty-agent/agent/src/main/resources/application.yml
@@ -0,0 +1,63 @@
+server:
+  port: 8090
+  ssl:
+    enabled: false
+
+
+##
+# logging
+#
+logging:
+  level:
+    root: INFO
+    com.supwisdom: DEBUG
+#    org.springframework.web: INFO
+#    org.springframework.cloud.openfeign: INFO
+
+
+swagger2.apis.basePackage: com.supwisdom.institute
+
+
+spring:
+  jackson:
+    time-zone: Asia/Shanghai
+
+##
+# spring.redis
+#
+  redis: 
+    database: 0
+    host: localhost
+    port: 6379
+    password: 
+    timeout: 10000
+    
+    jedis:
+      pool: 
+        maxIdle: 8
+        minIdle: 0
+        maxActive: 8
+        maxWait: -1
+
+
+##
+# infras.online-doc
+#
+infras.online-doc.enabled: true
+infras.online-doc.md-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/doc/
+infras.online-doc.api-docs.staitc.path: /Users/loie/c/work/git/institute/sw-backend/api-docs/
+
+
+## 平台OpenAPI
+poa.server.url: https://poa.supwisdom.com
+
+poa.client.id: nV8US9uAdFQ0ovuYpFOloXtFkME=
+poa.client.secret: dDgZAzuNnOjfsbm8iDohyVCXBU1GwImeMsmkJzjyGh8=
+poa.scopes: user:v1:readUser,user:v1:readOrganization,user:v1:readGroup,user:v1:readLabel,authz:v1:readRole
+
+## 开放接口地址
+user-data-service.server.url: ${poa.server.url}/apis/user/v1
+user-authorization-service.server.url: ${poa.server.url}/apis/authz/v1
+
+## 用户授权服务
+user-authorization-service.applicationId: 2
diff --git a/thirdparty-agent/agent/src/main/resources/bootstrap.yml b/thirdparty-agent/agent/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..92df83b
--- /dev/null
+++ b/thirdparty-agent/agent/src/main/resources/bootstrap.yml
@@ -0,0 +1,3 @@
+spring:
+  application:
+    name: sw-backend-thirdparty-agent
diff --git a/thirdparty-agent/pom.xml b/thirdparty-agent/pom.xml
new file mode 100644
index 0000000..ba5b8bb
--- /dev/null
+++ b/thirdparty-agent/pom.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.buildcommons</groupId>
+    <artifactId>spring-cloud-parent</artifactId>
+    <version>Finchley.RELEASE-1.1</version>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-thirdparty-agent-parent</artifactId>
+  <version>0.0.2-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <name>Supwisdom Backend Framework Third Party Agent Parent</name>
+  <description>Supwisdom Backend Framework Third Party Agent Parent project</description>
+
+  <modules>
+    <module>thirdparty-poa</module>
+    <module>agent</module>
+  </modules>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <java.version>1.8</java.version>
+
+    <argLine>-Dfile.encoding=UTF-8</argLine>
+
+    <downloadSources>true</downloadSources>
+    <downloadJavadocs>true</downloadJavadocs>
+
+    <maven.compiler.source>${java.version}</maven.compiler.source>
+    <maven.compiler.target>${java.version}</maven.compiler.target>
+
+    <dockerfile-maven-plugin.version>1.4.8</dockerfile-maven-plugin.version>
+    <dockerfile.image.server>harbor.supwisdom.com</dockerfile.image.server>
+    <dockerfile.image.prefix>sw-admin-framework</dockerfile.image.prefix>
+
+    <infras.version>0.1.1-SNAPSHOT</infras.version>
+
+    <io.springfox.version>2.9.2</io.springfox.version>
+
+  </properties>
+
+  <distributionManagement>
+    <repository>
+      <id>supwisdom-releases</id>
+      <name>internal release</name>
+      <url>https://app.supwisdom.com/nexus/content/repositories/releases</url>
+    </repository>
+    <snapshotRepository>
+      <id>supwisdom-snapshots</id>
+      <name>internal snapshots</name>
+      <url>https://app.supwisdom.com/nexus/content/repositories/snapshots</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <repositories>
+    <repository>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+      <id>supwisdom</id>
+      <url>https://app.supwisdom.com/nexus/content/groups/public/</url>
+    </repository>
+    <repository>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+      <id>central</id>
+      <url>http://repo.maven.apache.org/maven2</url>
+    </repository>
+  </repositories>
+
+  <dependencyManagement>
+    <dependencies>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-core</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-utils</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-common-framework</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+
+      <dependency>
+        <groupId>com.supwisdom.institute</groupId>
+        <artifactId>sw-backend-thirdparty-poa</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
+
+      <dependency>
+        <groupId>com.alibaba</groupId>
+        <artifactId>fastjson</artifactId>
+        <version>1.2.61</version>
+      </dependency>
+
+      <dependency>
+        <groupId>io.springfox</groupId>
+        <artifactId>springfox-swagger2</artifactId>
+        <version>${io.springfox.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>io.springfox</groupId>
+        <artifactId>springfox-swagger-ui</artifactId>
+        <version>${io.springfox.version}</version>
+      </dependency>
+
+    </dependencies>
+  </dependencyManagement>
+
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>com.spotify</groupId>
+          <artifactId>dockerfile-maven-plugin</artifactId>
+          <version>${dockerfile-maven-plugin.version}</version>
+          <configuration>
+            <repository>${dockerfile.image.server}/${dockerfile.image.prefix}/${project.artifactId}</repository>
+            <tag>${project.version}</tag>
+            <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+            <buildArgs>
+              <JAR_FILE>${project.build.finalName}.${project.packaging}</JAR_FILE>
+              <VERSION>${project.version}</VERSION>
+              <NAME>${project.artifactId}</NAME>
+            </buildArgs>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+
+    <plugins>
+      <plugin>
+        <groupId>com.spotify</groupId>
+        <artifactId>dockerfile-maven-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+
+  </build>
+
+</project>
diff --git a/thirdparty-agent/thirdparty-poa/.gitignore b/thirdparty-agent/thirdparty-poa/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/thirdparty-agent/thirdparty-poa/pom.xml b/thirdparty-agent/thirdparty-poa/pom.xml
new file mode 100644
index 0000000..c252ac9
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/pom.xml
@@ -0,0 +1,132 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.supwisdom.institute</groupId>
+    <artifactId>sw-backend-thirdparty-agent-parent</artifactId>
+    <version>0.0.2-SNAPSHOT</version>
+  </parent>
+
+  <groupId>com.supwisdom.institute</groupId>
+  <artifactId>sw-backend-thirdparty-poa</artifactId>
+  <version>0.0.2-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>Supwisdom Backend Framework Third Party POA</name>
+  <description>Supwisdom Backend Framework Third Party POA project</description>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.projectlombok</groupId>
+      <artifactId>lombok</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-core</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-utils</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.supwisdom.institute</groupId>
+      <artifactId>sw-backend-common-framework</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.data</groupId>
+      <artifactId>spring-data-redis</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>redis.clients</groupId>
+      <artifactId>jedis</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.alibaba</groupId>
+      <artifactId>fastjson</artifactId>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.jacoco</groupId>
+        <artifactId>jacoco-maven-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/HttpUtils.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/HttpUtils.java
new file mode 100644
index 0000000..d6e18d6
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/HttpUtils.java
@@ -0,0 +1,384 @@
+package com.supwisdom.institute.backend.thirdparty.poa;
+
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * This is {@link HttpUtils}.
+ *
+ * @author Misagh Moayyed
+ * @since 5.2.0
+ */
+@Slf4j
+@UtilityClass
+public class HttpUtils {
+
+    private static final int MAX_CONNECTIONS = 200;
+    private static final int MAX_CONNECTIONS_PER_ROUTE = 20;
+
+    private static final HttpClient HTTP_CLIENT = HttpClientBuilder.create().setMaxConnTotal(MAX_CONNECTIONS)
+            .setMaxConnPerRoute(MAX_CONNECTIONS_PER_ROUTE).build();
+
+    /**
+     * Execute http response.
+     *
+     * @param url               the url
+     * @param method            the method
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @return the http response
+     */
+    public static HttpResponse execute(final String url, final String method,
+                                       final String basicAuthUsername,
+                                       final String basicAuthPassword) {
+        return execute(url, method, basicAuthUsername, basicAuthPassword, new HashMap<>(), new HashMap<>());
+    }
+
+    /**
+     * Execute http response.
+     *
+     * @param url               the url
+     * @param method            the method
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @param headers           the headers
+     * @return the http response
+     */
+    public static HttpResponse execute(final String url, final String method,
+                                       final String basicAuthUsername, final String basicAuthPassword,
+                                       final Map<String, Object> headers) {
+        return execute(url, method, basicAuthUsername, basicAuthPassword, new HashMap<>(), headers);
+    }
+
+    /**
+     * Execute http response.
+     *
+     * @param url    the url
+     * @param method the method
+     * @return the http response
+     */
+    public static HttpResponse execute(final String url, final String method) {
+        return execute(url, method, null, null, new HashMap<>(), new HashMap<>());
+    }
+    
+    /**
+     * Execute http response.
+     *
+     * @param url    the url
+     * @param method the method
+     * @return the http response
+     */
+    public static HttpResponse execute(final String url, final String method, final Map<String, Object> parameters, final Map<String, Object> headers) {
+        return execute(url, method, null, null, parameters, headers);
+    }
+
+    /**
+     * Execute http response.
+     *
+     * @param url               the url
+     * @param method            the method
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @param parameters        the parameters
+     * @param headers           the headers
+     * @return the http response
+     */
+    public static HttpResponse execute(final String url, final String method,
+                                       final String basicAuthUsername,
+                                       final String basicAuthPassword,
+                                       final Map<String, Object> parameters,
+                                       final Map<String, Object> headers) {
+        return execute(url, method, basicAuthUsername, basicAuthPassword, parameters, headers, null);
+    }
+
+    /**
+     * Execute http request and produce a response.
+     *
+     * @param url               the url
+     * @param method            the method
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @param parameters        the parameters
+     * @param headers           the headers
+     * @param entity            the entity
+     * @return the http response
+     */
+    public static HttpResponse execute(final String url, final String method,
+                                       final String basicAuthUsername,
+                                       final String basicAuthPassword,
+                                       final Map<String, Object> parameters,
+                                       final Map<String, Object> headers,
+                                       final String entity) {
+        try {
+            final URI uri = buildHttpUri(url, parameters);
+            final HttpUriRequest request;
+            switch (method.toLowerCase()) {
+                case "post":
+                case "put":
+                    request = new HttpPost(uri);
+                    if (StringUtils.isNotBlank(entity)) {
+                        final StringEntity stringEntity = new StringEntity(entity);
+                        ((HttpPost) request).setEntity(stringEntity);
+                    }
+                    break;
+                case "delete":
+                    request = new HttpDelete(uri);
+                    break;
+                case "get":
+                default:
+                    request = new HttpGet(uri);
+                    break;
+            }
+            headers.forEach((k, v) -> request.addHeader(k, v.toString()));
+            prepareHttpRequest(request, basicAuthUsername, basicAuthPassword);
+            return HTTP_CLIENT.execute(request);
+        } catch (final Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * Close the response.
+     *
+     * @param response the response to close
+     */
+    public static void close(final HttpResponse response) {
+        if (response != null) {
+            final CloseableHttpResponse closeableHttpResponse = (CloseableHttpResponse) response;
+            try {
+                closeableHttpResponse.close();
+            } catch (final IOException e) {
+                log.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    /**
+     * Execute get http response.
+     *
+     * @param url               the url
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @param parameters        the parameters
+     * @return the http response
+     */
+    public static HttpResponse executeGet(final String url,
+                                          final String basicAuthUsername,
+                                          final String basicAuthPassword,
+                                          final Map<String, Object> parameters) {
+        try {
+            return executeGet(url, basicAuthUsername, basicAuthPassword, parameters, new HashMap<>());
+        } catch (final Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * Execute get http response.
+     *
+     * @param url               the url
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @param parameters        the parameters
+     * @param headers           the headers
+     * @return the http response
+     */
+    public static HttpResponse executeGet(final String url,
+                                          final String basicAuthUsername,
+                                          final String basicAuthPassword,
+                                          final Map<String, Object> parameters,
+                                          final Map<String, Object> headers) {
+        try {
+            return execute(url, "GET", basicAuthUsername, basicAuthPassword, parameters, headers);
+        } catch (final Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * Execute get http response.
+     *
+     * @param url        the url
+     * @param parameters the parameters
+     * @return the http response
+     */
+    public static HttpResponse executeGet(final String url,
+                                          final Map<String, Object> parameters) {
+        try {
+            return executeGet(url, null, null, parameters);
+        } catch (final Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * Execute get http response.
+     *
+     * @param url the url
+     * @return the http response
+     */
+    public static HttpResponse executeGet(final String url) {
+        try {
+            return executeGet(url, null, null, new LinkedHashMap<>());
+        } catch (final Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * Execute get http response.
+     *
+     * @param url               the url
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @return the http response
+     */
+    public static HttpResponse executeGet(final String url,
+                                          final String basicAuthUsername,
+                                          final String basicAuthPassword) {
+        try {
+            return executeGet(url, basicAuthUsername, basicAuthPassword, new HashMap<>());
+        } catch (final Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * Execute post http response.
+     *
+     * @param url               the url
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @param jsonEntity        the json entity
+     * @return the http response
+     */
+    public static HttpResponse executePost(final String url,
+                                           final String basicAuthUsername,
+                                           final String basicAuthPassword,
+                                           final String jsonEntity) {
+        return executePost(url, basicAuthUsername, basicAuthPassword, jsonEntity, new HashMap<>());
+    }
+
+    /**
+     * Execute post http response.
+     *
+     * @param url        the url
+     * @param parameters the parameters
+     * @return the http response
+     */
+    public static HttpResponse executePost(final String url,
+                                           final Map<String, Object> parameters) {
+        return executePost(url, null, null, null, parameters);
+    }
+    
+    /**
+     * Execute post http response.
+     *
+     * @param url        the url
+     * @param jsonEntity the json entity
+     * @param parameters the parameters
+     * @return the http response
+     */
+    public static HttpResponse executePost(final String url,
+                                           final String jsonEntity,
+                                           final Map<String, Object> parameters) {
+        return executePost(url, null, null, jsonEntity, parameters);
+    }
+
+    /**
+     * Execute post http response.
+     *
+     * @param url               the url
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     * @param jsonEntity        the json entity
+     * @param parameters        the parameters
+     * @return the http response
+     */
+    public static HttpResponse executePost(final String url,
+                                           final String basicAuthUsername,
+                                           final String basicAuthPassword,
+                                           final String jsonEntity,
+                                           final Map<String, Object> parameters) {
+        try {
+            return execute(url, "POST", basicAuthUsername, basicAuthPassword, parameters, new HashMap<>(), jsonEntity);
+        } catch (final Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    /**
+     * Prepare http request. Tries to set the authorization header
+     * in cases where the URL endpoint does not actually produce the header
+     * on its own.
+     *
+     * @param request           the request
+     * @param basicAuthUsername the basic auth username
+     * @param basicAuthPassword the basic auth password
+     */
+    private static void prepareHttpRequest(final HttpUriRequest request, final String basicAuthUsername,
+                                           final String basicAuthPassword) {
+        if (StringUtils.isNotBlank(basicAuthUsername) && StringUtils.isNotBlank(basicAuthPassword)) {
+            final String auth = encodeBase64(basicAuthUsername + ":" + basicAuthPassword);
+            request.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + auth);
+        }
+    }
+
+    private static URI buildHttpUri(final String url, final Map<String, Object> parameters) throws URISyntaxException {
+        final URIBuilder uriBuilder = new URIBuilder(url);
+        parameters.forEach((k, v) -> uriBuilder.addParameter(k, v.toString()));
+        return uriBuilder.build();
+    }
+
+    /**
+     * Create headers org . springframework . http . http headers.
+     *
+     * @param basicAuthUser     the basic auth user
+     * @param basicAuthPassword the basic auth password
+     * @return the org . springframework . http . http headers
+     */
+//    public static org.springframework.http.HttpHeaders createBasicAuthHeaders(final String basicAuthUser, final String basicAuthPassword) {
+//        final org.springframework.http.HttpHeaders acceptHeaders = new org.springframework.http.HttpHeaders();
+//        acceptHeaders.setAccept(CollectionUtils.wrap(MediaType.APPLICATION_JSON));
+//        if (StringUtils.isNotBlank(basicAuthUser) && StringUtils.isNotBlank(basicAuthPassword)) {
+//            final String authorization = basicAuthUser + ':' + basicAuthPassword;
+//            final String basic = encodeBase64(authorization.getBytes(Charset.forName("US-ASCII")));
+//            acceptHeaders.set("Authorization", "Basic " + basic);
+//        }
+//        return acceptHeaders;
+//    }
+    
+    
+    private static String encodeBase64(final String data) {
+      return Base64.encodeBase64String(data.getBytes(StandardCharsets.UTF_8));
+    }
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/PoaUtil.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/PoaUtil.java
new file mode 100644
index 0000000..41f44d6
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/PoaUtil.java
@@ -0,0 +1,321 @@
+package com.supwisdom.institute.backend.thirdparty.poa;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.common.core.distributedlock.DistributedLockHandler;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class PoaUtil {
+  
+  public static void main(String[] args) {
+    
+    PoaUtil.poaServerUrl = "https://poa.supwisdom.com";
+    PoaUtil.clientId = "nV8US9uAdFQ0ovuYpFOloXtFkME=";
+    PoaUtil.clientSecret = "dDgZAzuNnOjfsbm8iDohyVCXBU1GwImeMsmkJzjyGh8=";
+    PoaUtil.scope = "user:v1:readUser,user:v1:readOrganization,user:v1:readGroup,user:v1:readLabel,authz:v1:readRole";
+    
+    //JSONObject accessTokenObject = PoaUtil.getAccessToken("https://poa.supwisdom.com", "tikgr8E6vfcGDXVSG5Vs-00XJoQ=", "wZugRIydkt9lQqJyi7lco-x8wZyyreb41WC_ioj8g-I=", "personalSecurityCenterSa:v1:admin");
+    JSONObject accessTokenObject = PoaUtil.getAccessTokenObject();
+    System.out.println(accessTokenObject.toJSONString());
+    
+    String accessToken = accessTokenObject.getString("access_token");
+    PoaUtil.accessToken = accessToken;
+    
+    JSONObject jsonObject = PoaUtil.loadUserInfoByAccountName("https://poa.supwisdom.com/apis/user/v1", "smartadmin");
+    System.out.println(jsonObject.toJSONString());
+  }
+  
+  
+
+  @Autowired(required = false)
+  private DistributedLockHandler distributedLockHandler;
+  
+  //每隔 3000 秒执行一次
+  @Scheduled(fixedRate = 3000000)
+  public void refresh() {
+    if (distributedLockHandler == null) {
+      PoaUtil.clearAccessToken();
+      
+      try {
+        JSONObject accessTokenObject = PoaUtil.getAccessTokenObject();
+        if (PoaUtil.saveAccessToken(accessTokenObject)) {
+          return;
+        }
+        
+        try {
+          Thread.sleep(500L);
+        } catch (InterruptedException e) {
+          e.printStackTrace();
+        }
+        log.debug("AccessTokenRefresh.refresh retry");
+        
+        refresh();
+      } finally {
+        
+      }
+    }
+    
+    final String LOCK_KEY = "poa:ACCESS_TOKEN_REFRESH:LOCK";
+    
+    boolean lock = distributedLockHandler.lock(LOCK_KEY, 5000L);  log.debug("AccessTokenRefresh.refresh lock: {}", lock);
+    if(!lock) {
+      return;
+    }
+
+    PoaUtil.clearAccessToken();
+
+    try {
+      JSONObject accessTokenObject = PoaUtil.getAccessTokenObject();
+      
+      if (PoaUtil.saveAccessToken(accessTokenObject)) {
+        return;
+      }
+      
+      // 如果 token 获取失败,则 先释放锁,随后再次尝试获取
+      boolean releaseLock = distributedLockHandler.releaseLock(LOCK_KEY);  log.debug("AccessTokenRefresh.refresh releaseLock: {}", releaseLock);
+      
+      try {
+        Thread.sleep(500L);
+      } catch (InterruptedException e) {
+        e.printStackTrace();
+      }
+      log.debug("AccessTokenRefresh.refresh retry");
+      
+      refresh();
+      
+    } finally {
+      boolean releaseLock = distributedLockHandler.releaseLock(LOCK_KEY);  log.debug("AccessTokenRefresh.refresh finally releaseLock: {}", releaseLock);
+    }
+    
+  }
+
+  
+  private static String accessToken = null;
+  
+  
+  private static String poaServerUrl;
+
+  private static String clientId;
+
+  private static String clientSecret;
+
+  private static String scope;
+  
+  private static StringRedisTemplate stringRedisTemplate;
+
+  @Value("${poa.server.url}")
+  public void setPoaServerUrl(String poaServerUrl) {
+    PoaUtil.poaServerUrl = poaServerUrl;
+  }
+  @Value("${poa.client.id}")
+  public void setClientId(String clientId) {
+    PoaUtil.clientId = clientId;
+  }
+  @Value("${poa.client.secret}")
+  public void setClientSecret(String clientSecret) {
+    PoaUtil.clientSecret = clientSecret;
+  }
+  @Value("${poa.scopes}")
+  public void setScopes(String[] scopes) {
+    PoaUtil.scope = StringUtils.join(scopes, ","); log.debug("{}", PoaUtil.scope);
+  }
+  
+  @Autowired(required = false)
+  public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
+    PoaUtil.stringRedisTemplate = stringRedisTemplate;
+  }
+  
+  public static boolean saveAccessToken(JSONObject accessTokenObject) {
+    
+    if (accessTokenObject != null) {
+      String accessToken = accessTokenObject.getString("access_token");
+      Long expiresIn = accessTokenObject.getLong("expires_in");
+      
+      PoaUtil.accessToken = accessToken;
+      
+      if (stringRedisTemplate != null) {
+        final String ACCESS_TOKEN_KEY = "poa:"+clientId+":ACCESS_TOKEN";
+        RedisUtils.redisTemplate(stringRedisTemplate).setValue(ACCESS_TOKEN_KEY, expiresIn, accessToken);
+      }
+      
+      return true;
+    }
+    
+    return false;
+  }
+  
+  public static void clearAccessToken() {
+    PoaUtil.accessToken = null;
+
+    if (stringRedisTemplate != null) {
+      final String ACCESS_TOKEN_KEY = "poa:"+clientId+":ACCESS_TOKEN";
+      RedisUtils.redisTemplate(stringRedisTemplate).expireValue(ACCESS_TOKEN_KEY);
+    }
+  }
+  
+  public static String getAccessToken() {
+    if (PoaUtil.accessToken != null) {
+      return PoaUtil.accessToken;
+    }
+    
+    if (stringRedisTemplate != null) {
+      final String ACCESS_TOKEN_KEY = "poa:"+clientId+":ACCESS_TOKEN";
+      PoaUtil.accessToken = RedisUtils.redisTemplate(stringRedisTemplate).getValue(ACCESS_TOKEN_KEY);
+    }
+    
+    return PoaUtil.accessToken;
+  }
+  
+  public static JSONObject getAccessTokenObject() {
+    
+    String tokenUrl = poaServerUrl + "/oauth2/token";
+    
+    String grantType = "client_credentials";
+    
+    Map<String, Object> headers = new HashMap<String, Object>();
+    headers.put("Content-Type", "application/x-www-form-urlencoded");
+    
+    String formData = String.format("grant_type=%s&client_id=%s&client_secret=%s&scope=%s", grantType, clientId, clientSecret, scope);
+    //log.debug("Post formData [{}]", formData);
+
+    int retry = 0;
+    while(retry < 3) {
+      try{
+        HttpResponse httpResponse = HttpUtils.execute(tokenUrl, "POST", null, null, new HashMap<String, Object>(), headers, formData);
+        /**
+         * {
+         *   "access_token": "0loVdVN4AqPIbStZmkvtkw==",
+         *   "token_type": "bearer",
+         *   "expires_in": 3600
+         * }
+         */
+        
+        /**
+         * {
+         *   "error": "invalid_client",
+         *   "error_description": ""
+         * }
+         */
+        
+        JSONObject resultJsonObject = parseJSONObject(httpResponse);
+        if (resultJsonObject != null) {
+          if (!resultJsonObject.containsKey("error")) {
+            return resultJsonObject;
+          }
+          
+          log.error("Get access_token by [{}] from poa error: {}", clientId, resultJsonObject.getString("error"));
+          break;
+        }
+        
+        retry ++;
+        Thread.sleep(retry * retry * 500L);
+        log.debug("Retry {}", retry);
+      } catch (Exception e) {
+        // 未知异常时,重试
+        log.error("Get access_token by [{}] from poa excption: ", clientId, e);
+      }
+
+    }
+    
+    return null;
+  }
+  
+  
+
+  private static JSONObject parseJSONObject(HttpResponse httpResponse) {
+    try {
+      if (httpResponse != null) {
+        StringBuilder entityStringBuilder = new StringBuilder();
+  
+        BufferedReader b = new BufferedReader(new InputStreamReader(httpResponse.getEntity().getContent(), "UTF-8"), 8*1024);
+  
+        String line=null;
+        while ((line=b.readLine())!=null) {
+          entityStringBuilder.append(line);
+        }
+        log.debug("Fetch response [{}]", entityStringBuilder.toString());
+        
+        JSONObject resultJsonObject = JSONObject.parseObject(entityStringBuilder.toString());
+        
+        return resultJsonObject;
+      }
+    } catch (UnsupportedEncodingException e) {
+      e.printStackTrace();
+    } catch (UnsupportedOperationException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    
+    return null;
+  }
+  
+  
+  public static JSONObject loadUserInfoByAccountName(String baseUrl, String accountName) {
+    
+    String url = baseUrl + "/users/accountName/" + accountName;
+
+    Map<String, Object> parameters = new HashMap<String, Object>();
+
+    Map<String, Object> headers = new HashMap<String, Object>();
+    headers.put(HttpHeaders.AUTHORIZATION, "Bearer "+PoaUtil.getAccessToken());
+    log.debug("{}", headers);
+    
+    HttpResponse httpResponse = HttpUtils.execute(url, "GET", parameters, headers);
+    
+    JSONObject resultJsonObject = parseJSONObject(httpResponse);
+    if (resultJsonObject != null) {
+      if (!resultJsonObject.containsKey("error")) {
+        // XXX: 根据API响应数据,须修改为 只返回实际的 data 数据
+        return resultJsonObject;
+      }
+    }
+    
+    return null;
+  }
+  
+  
+  public static JSONObject loadAccountApplicationRoles(String baseUrl, String applicationId, String username) {
+    
+    String url = baseUrl + "/application/"+applicationId+"/account/"+username+"/roles";
+    log.debug(url);
+    
+    Map<String, Object> parameters = new HashMap<String, Object>();
+    log.debug("{}", parameters);
+
+    Map<String, Object> headers = new HashMap<String, Object>();
+    headers.put(HttpHeaders.AUTHORIZATION, "Bearer "+PoaUtil.getAccessToken());
+    log.debug("{}", headers);
+    
+    HttpResponse httpResponse = HttpUtils.execute(url, "GET", parameters, headers);
+    
+    JSONObject resultJsonObject = parseJSONObject(httpResponse);
+    if (resultJsonObject != null) {
+      if (!resultJsonObject.containsKey("error")) {
+        // XXX: 根据API响应数据,须修改为 只返回实际的 data 数据
+        return resultJsonObject;
+      }
+    }
+    
+    return null;
+  }
+  
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/RedisUtils.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/RedisUtils.java
new file mode 100644
index 0000000..26b7dee
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/RedisUtils.java
@@ -0,0 +1,205 @@
+package com.supwisdom.institute.backend.thirdparty.poa;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import org.springframework.data.redis.core.BoundHashOperations;
+import org.springframework.data.redis.core.BoundSetOperations;
+import org.springframework.data.redis.core.BoundValueOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+
+public class RedisUtils<T> {
+  
+  private RedisUtils() {
+    this.redisTemplate = null;
+  }
+  
+  private RedisUtils(RedisTemplate<String, T> redisTemplate) {
+    this.redisTemplate = redisTemplate;
+  }
+  
+  private final RedisTemplate<String, T> redisTemplate;
+  
+  public static <T> RedisUtils<T> redisTemplate(RedisTemplate<String, T> redisTemplate) {
+    RedisUtils<T> redisUtils = new RedisUtils<T>(redisTemplate);
+    
+    return redisUtils;
+  }
+  
+
+
+  /**
+   * Set the value of a key
+   * @param redisKey
+   * @param expireTime, time in seconds
+   * @param t
+   */
+  public void setValue(String redisKey, Long expireTime, T t) {
+    BoundValueOperations<String, T> boundValue = this.redisTemplate.boundValueOps(redisKey);
+    boundValue.set(t);
+    
+    if (expireTime != null) {
+      boundValue.expire(expireTime, TimeUnit.SECONDS);
+    }
+  }
+  
+  /**
+   * Get the value of a key
+   * @param redisKey
+   * @return
+   */
+  public T getValue(String redisKey) {
+    BoundValueOperations<String, T> boundValue = this.redisTemplate.boundValueOps(redisKey);
+    if (boundValue == null) {
+      return null;
+    }
+    
+    return boundValue.get();
+  }
+  
+  /**
+   * Expire the value of a key
+   * @param redisKey
+   */
+  public void expireValue(String redisKey) {
+    BoundValueOperations<String, T> boundValue = this.redisTemplate.boundValueOps(redisKey);
+    if (boundValue == null) {
+      return;
+    }
+    
+    boundValue.expire(-1L, TimeUnit.SECONDS);
+  }
+  
+  public List<T> getAllValue(String patternRedisKey) {
+    return this.redisTemplate.keys(patternRedisKey)
+        .stream()
+        .map(redisKey -> this.redisTemplate.boundValueOps(redisKey).get())
+        .filter(Objects::nonNull).collect(Collectors.toList());
+  }
+  
+  
+  /**
+   * SADD key member
+   * Add a member ({@link T}) to a set
+   * @param redisKey
+   * @param expireTime
+   * @param member
+   */
+  public void addToSet(String redisKey, Long expireTime, T member) {
+    BoundSetOperations<String, T> boundSet = this.redisTemplate.boundSetOps(redisKey);
+    boundSet.add(member);
+    
+    if (expireTime != null) {
+      boundSet.expire(expireTime, TimeUnit.SECONDS);
+    }
+  }
+  
+  /**
+   * SREM key member
+   * Remove a member ({@link T}) from a set
+   * @param redisKey
+   * @param member
+   */
+  public void remFromSet(String redisKey, T member) {
+    BoundSetOperations<String, T> boundSet = this.redisTemplate.boundSetOps(redisKey);
+    if (boundSet == null) {
+      return;
+    }
+    
+    boundSet.remove(member);
+    
+    if (boundSet.size() == 0) {
+      boundSet.expire(-1L, TimeUnit.SECONDS);
+    }
+  }
+  
+  public void expireSet(String redisKey) {
+    BoundSetOperations<String, T> boundSet = this.redisTemplate.boundSetOps(redisKey);
+    if (boundSet == null) {
+      return;
+    }
+    
+    boundSet.expire(-1L, TimeUnit.SECONDS);
+  }
+  
+  
+  /**
+   * HSET key field value
+   * Set the string value of a hash field
+   * @param redisKey
+   * @param expireTime
+   * @param field
+   * @param value
+   */
+  public void setToHash(String redisKey, Long expireTime, String field, T value) {
+    BoundHashOperations<String, String, T> boundHash = this.redisTemplate.boundHashOps(redisKey);
+    boundHash.put(field, value);
+    
+    if (expireTime != null) {
+      boundHash.expire(expireTime, TimeUnit.SECONDS);
+    }
+  }
+  
+  /**
+   * HGETALL key
+   * Get all the fields and values in a hash
+   * @param redisKey
+   * @return
+   */
+  public Map<String, T> getAllFromHash(String redisKey) {
+    BoundHashOperations<String, String, T> boundHash = this.redisTemplate.boundHashOps(redisKey);
+    if (boundHash == null) {
+      return null;
+    }
+    
+    return boundHash.entries();
+  }
+  
+  /**
+   * HGET key field
+   * summary: Get the value of a hash field
+   * @param redisKey
+   * @param field
+   * @return
+   */
+  public T getFromHash(String redisKey, String field) {
+    BoundHashOperations<String, String, T> boundHash = this.redisTemplate.boundHashOps(redisKey);
+    if (boundHash == null) {
+      return null;
+    }
+    
+    return boundHash.get(field);
+  }
+  
+  /**
+   * HDEL key field
+   * summary: Delete a hash field
+   * @param redisKey
+   * @param field
+   */
+  public void delFromHash(String redisKey, String field) {
+    BoundHashOperations<String, String, T> boundHash = this.redisTemplate.boundHashOps(redisKey);
+    if (boundHash == null) {
+      return;
+    }
+    
+    boundHash.delete(field);
+    
+    if (boundHash.size() == 0) {
+      boundHash.expire(-1L, TimeUnit.SECONDS);
+    }
+  }
+  
+  public void expireHash(String redisKey) {
+    BoundHashOperations<String, String, T> boundHash = this.redisTemplate.boundHashOps(redisKey);
+    if (boundHash == null) {
+      return;
+    }
+    
+    boundHash.expire(-1L, TimeUnit.SECONDS);
+  }
+  
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/controller/AuthzController.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/controller/AuthzController.java
new file mode 100644
index 0000000..b98f10a
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/controller/AuthzController.java
@@ -0,0 +1,53 @@
+package com.supwisdom.institute.backend.thirdparty.poa.api.v1.controller;
+
+import java.util.List;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+import com.supwisdom.institute.backend.thirdparty.poa.api.v1.vo.response.RoleListResponseData;
+import com.supwisdom.institute.backend.thirdparty.poa.model.Role;
+import com.supwisdom.institute.backend.thirdparty.poa.service.AuthzService;
+
+@Api(value = "AgentPoaAuthz", tags = { "AgentPoaAuthz" }, description = "POA授权服务代理接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/poa/authz")
+public class AuthzController {
+  
+  @Autowired
+  private AuthzService authzService;
+
+  @GetMapping(path = "roles/account/{username}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<RoleListResponseData> loadAccountApplicationRoles(
+      @PathVariable("username") String username) {
+    
+    if (username == null || username.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+    
+    List<Role> roles = authzService.loadAccountRoles(username);
+    
+    if (roles == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    RoleListResponseData data = RoleListResponseData.of(roles);
+
+    return new DefaultApiResponse<RoleListResponseData>(data);
+  }
+
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/controller/UserController.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/controller/UserController.java
new file mode 100644
index 0000000..b82b77b
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/controller/UserController.java
@@ -0,0 +1,51 @@
+package com.supwisdom.institute.backend.thirdparty.poa.api.v1.controller;
+
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.MimeTypeUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.DefaultApiResponse;
+import com.supwisdom.institute.backend.thirdparty.poa.api.v1.vo.response.UserInfoResponseData;
+import com.supwisdom.institute.backend.thirdparty.poa.model.UserInfoModel;
+import com.supwisdom.institute.backend.thirdparty.poa.service.UserService;
+
+@Api(value = "AgentPoaUser", tags = { "AgentPoaUser" }, description = "POA用户服务代理接口")
+@Slf4j
+@RestController
+@RequestMapping("/v1/poa/user")
+public class UserController {
+  
+  @Autowired
+  private UserService userService;
+  
+  @GetMapping(path = "/users/accountName/{accountName}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
+  @ResponseStatus(value = HttpStatus.OK)
+  @ResponseBody
+  public DefaultApiResponse<UserInfoResponseData> loadUserInfoByAccountName(
+      @PathVariable("accountName") String accountName) {
+    
+    if (accountName == null || accountName.length() == 0) {
+      throw new RuntimeException("exception.get.username.must.not.empty");
+    }
+    
+    UserInfoModel userInfoModel = userService.loadUserInfoByAccountName(accountName);
+    
+    if (userInfoModel == null) {
+      throw new RuntimeException("exception.get.account.not.exist");
+    }
+    
+    UserInfoResponseData data = UserInfoResponseData.of(userInfoModel);
+
+    return new DefaultApiResponse<UserInfoResponseData>(data);
+  }
+
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/vo/response/RoleListResponseData.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/vo/response/RoleListResponseData.java
new file mode 100644
index 0000000..eb5002b
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/vo/response/RoleListResponseData.java
@@ -0,0 +1,30 @@
+package com.supwisdom.institute.backend.thirdparty.poa.api.v1.vo.response;
+
+import java.util.List;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+import com.supwisdom.institute.backend.thirdparty.poa.model.Role;
+
+public class RoleListResponseData implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4227215295471065273L;
+
+  @Getter
+  @Setter
+  public List<Role> roles;
+  
+  public static RoleListResponseData of(List<Role> roles) {
+    RoleListResponseData data = new RoleListResponseData();
+    
+    data.setRoles(roles);
+    
+    return data;
+  }
+
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/vo/response/UserInfoResponseData.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/vo/response/UserInfoResponseData.java
new file mode 100644
index 0000000..73ad872
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/api/v1/vo/response/UserInfoResponseData.java
@@ -0,0 +1,19 @@
+package com.supwisdom.institute.backend.thirdparty.poa.api.v1.vo.response;
+
+import com.supwisdom.institute.backend.common.framework.entity.EntityUtils;
+import com.supwisdom.institute.backend.common.framework.vo.response.data.IApiResponseData;
+import com.supwisdom.institute.backend.thirdparty.poa.model.UserInfoModel;
+
+public class UserInfoResponseData extends UserInfoModel implements IApiResponseData {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -6563198124742058531L;
+
+  public static UserInfoResponseData of(UserInfoModel entity) {
+    UserInfoResponseData data = new UserInfoResponseData();
+    return EntityUtils.copy(entity, data);
+  }
+  
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/autoconfigure/PoaAutoConfiguration.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/autoconfigure/PoaAutoConfiguration.java
new file mode 100644
index 0000000..a480ee3
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/autoconfigure/PoaAutoConfiguration.java
@@ -0,0 +1,10 @@
+package com.supwisdom.institute.backend.thirdparty.poa.autoconfigure;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ComponentScan(basePackages = {"com.supwisdom.institute.backend.thirdparty.poa"})
+public class PoaAutoConfiguration {
+
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/model/Role.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/model/Role.java
new file mode 100644
index 0000000..cdcc151
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/model/Role.java
@@ -0,0 +1,41 @@
+package com.supwisdom.institute.backend.thirdparty.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/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/model/UserInfoModel.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/model/UserInfoModel.java
new file mode 100644
index 0000000..9b16312
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/model/UserInfoModel.java
@@ -0,0 +1,124 @@
+package com.supwisdom.institute.backend.thirdparty.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/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/service/AuthzService.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/service/AuthzService.java
new file mode 100644
index 0000000..9780b20
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/service/AuthzService.java
@@ -0,0 +1,41 @@
+package com.supwisdom.institute.backend.thirdparty.poa.service;
+
+import java.util.List;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.thirdparty.poa.PoaUtil;
+import com.supwisdom.institute.backend.thirdparty.poa.model.Role;
+
+@Slf4j
+@Service
+public class AuthzService {
+
+  @Value("${user-authorization-service.applicationId}")
+  private String applicationId;
+
+  @Value("${user-authorization-service.server.url}")
+  private String userAuthorizationServiceServerUrl;
+  
+  public List<Role> loadAccountRoles(String username) {
+
+    JSONObject jsonObject = PoaUtil.loadAccountApplicationRoles(userAuthorizationServiceServerUrl, applicationId, username);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    JSONObject data = jsonObject.getJSONObject("data");
+    
+    JSONArray roleArray = data.getJSONArray("roles");
+    
+    List<Role> roles = roleArray.toJavaList(Role.class);
+    log.debug("{}", roles);
+    
+    return roles;
+  }
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/service/UserService.java b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/service/UserService.java
new file mode 100644
index 0000000..ba2527f
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/java/com/supwisdom/institute/backend/thirdparty/poa/service/UserService.java
@@ -0,0 +1,34 @@
+package com.supwisdom.institute.backend.thirdparty.poa.service;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.thirdparty.poa.PoaUtil;
+import com.supwisdom.institute.backend.thirdparty.poa.model.UserInfoModel;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+public class UserService {
+
+  @Value("${user-data-service.server.url}")
+  private String userDataServiceServerUrl;
+  
+  public UserInfoModel loadUserInfoByAccountName(String accountName) {
+    
+    JSONObject jsonObject = PoaUtil.loadUserInfoByAccountName(userDataServiceServerUrl, accountName);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    JSONObject data = jsonObject.getJSONObject("data");
+    
+    UserInfoModel userInfoModel = data.toJavaObject(UserInfoModel.class);
+    log.debug("userInfoModel: [{}]", userInfoModel);
+    
+    return userInfoModel;
+  }
+
+}
diff --git a/thirdparty-agent/thirdparty-poa/src/main/resources/META-INF/spring.factories b/thirdparty-agent/thirdparty-poa/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..b4f81c0
--- /dev/null
+++ b/thirdparty-agent/thirdparty-poa/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  com.supwisdom.institute.backend.thirdparty.poa.autoconfigure.PoaAutoConfiguration