chore: 调整项目结构
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