feat: 增加用户登录时,从后端服务接口获取帐号、角色等信息
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/Application.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/Application.java
index f837894..adc1582 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/Application.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/Application.java
@@ -25,12 +25,6 @@
 
 @EnableCustomExceptionHandler
 
-//@EnableInfrasOnlineDoc
-
-//@EnableInfrasCasSecurity
-
-//@EnableInfrasBasicApi
-
 @EnableInfrasJWTWebFluxApi
 @EnableInfrasBasicWebFluxApi
 public class Application {
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Account.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Account.java
new file mode 100644
index 0000000..38f38d5
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Account.java
@@ -0,0 +1,70 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Account extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -4889952442290543101L;
+
+  private String id;
+
+  /**
+   * 用户名
+   */
+  private String username;
+  
+  /**
+   * 密码
+   */
+  private String password;
+
+  /**
+   * 是否可用,1 可用,0 不可用,默认:1
+   */
+  private Boolean enabled;
+  /**
+   * 账号未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean accountNonExpired;
+  /**
+   * 账号未锁定,1 未锁定,0 锁定,默认:1
+   */
+  private Boolean accountNonLocked;
+  /**
+   * 密码未过期,1 未过期,0 过期,默认:1
+   */
+  private Boolean credentialsNonExpired;
+
+  /**
+   * 姓名
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 登录手机
+   */
+  private String mobile;
+  /**
+   * 登录邮箱
+   */
+  private String email;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java
new file mode 100644
index 0000000..0d5c72d
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Permission.java
@@ -0,0 +1,64 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Permission extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -3042842657207449148L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+  /**
+   * 类型(1 应用,2 菜单,3 操作)
+   */
+  private String type;
+
+  /**
+   * URL地址
+   */
+  private String url;
+
+  /**
+   * 系统ID
+   */
+  private String applicationId;
+
+  /**
+   * 父级ID
+   */
+  private String parentId;
+
+  /**
+   * 排序
+   */
+  private Integer order;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Role.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Role.java
new file mode 100644
index 0000000..034ff84
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/model/Role.java
@@ -0,0 +1,39 @@
+package com.supwisdom.institute.backend.gateway.authn.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import com.supwisdom.institute.backend.common.framework.model.ABaseModel;
+
+@Getter
+@Setter
+public class Role extends ABaseModel {
+
+  /**
+   * 
+   */
+  private static final long serialVersionUID = -8551951601186240995L;
+
+  private String id;
+
+  /**
+   * 代码
+   */
+  private String code;
+
+  /**
+   * 名称
+   */
+  private String name;
+
+  /**
+   * 备注
+   */
+  private String memo;
+
+  /**
+   * 状态(1 启用,0 停用)
+   */
+  private String status;
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnAccountRestTemplateConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnAccountRestTemplateConfig.java
new file mode 100644
index 0000000..5e98c4a
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/configuration/AuthnAccountRestTemplateConfig.java
@@ -0,0 +1,92 @@
+package com.supwisdom.institute.backend.gateway.authn.remote.configuration;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.apache.http.ssl.SSLContexts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.util.ResourceUtils;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class AuthnAccountRestTemplateConfig {
+
+  @Bean
+  public ClientHttpRequestFactory simpleClientHttpRequestFactory(
+      @Value("${sw-backend-base-api.client-auth.enabled:false}") boolean enabled,
+      @Value("${sw-backend-base-api.client-auth.key-password:}") String keyPassword,
+      @Value("${sw-backend-base-api.client-auth.key-store:}") String keyStore,
+      @Value("${sw-backend-base-api.client-auth.key-store-password:}") String keyStorePassword,
+      @Value("${sw-backend-base-api.client-auth.trust-store:}") String trustStore,
+      @Value("${sw-backend-base-api.client-auth.trust-store-password:}") String trustStorePassword
+  ) {
+    if (!enabled) {
+      SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    }
+    
+    if (keyStore == null || keyStore.isEmpty()) {
+      SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+      factory.setReadTimeout(5000);// 单位为ms
+      factory.setConnectTimeout(5000);// 单位为ms
+      return factory;
+    } else {
+      try {
+        SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+        if (trustStore == null || trustStore.isEmpty()) {
+        } else {
+          sslContextBuilder
+//            .loadTrustMaterial(TrustAllStrategy.INSTANCE)
+            .loadTrustMaterial(
+                ResourceUtils.getFile(trustStore),
+                trustStorePassword.toCharArray()
+            );
+        }
+        
+        sslContextBuilder
+          .loadKeyMaterial(
+              ResourceUtils.getFile(keyStore),
+              keyStorePassword.toCharArray(),
+              keyPassword.toCharArray());
+        
+        SSLContext sslContext = sslContextBuilder.build();
+    
+        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
+            sslContext,
+            SSLConnectionSocketFactory.getDefaultHostnameVerifier());
+        
+        CloseableHttpClient httpClient = HttpClients.custom()
+            .setSSLSocketFactory(sslsf)
+            .build();
+        
+        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
+        factory.setReadTimeout(5000);// 单位为ms
+        factory.setConnectTimeout(5000);// 单位为ms
+        return factory;
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    
+    SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+    factory.setReadTimeout(5000);// 单位为ms
+    factory.setConnectTimeout(5000);// 单位为ms
+    return factory;
+  }
+  
+  @Bean(name = "authnAccountRestTemplate")
+  public RestTemplate authnAccountRestTemplate(ClientHttpRequestFactory requestFactory) {
+    return new RestTemplate(requestFactory);
+  }
+  
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnAccountRemoteRestTemplate.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnAccountRemoteRestTemplate.java
new file mode 100644
index 0000000..65cf89f
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/remote/web/client/AuthnAccountRemoteRestTemplate.java
@@ -0,0 +1,123 @@
+package com.supwisdom.institute.backend.gateway.authn.remote.web.client;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import com.alibaba.fastjson.JSONObject;
+
+@Slf4j
+@Component
+public class AuthnAccountRemoteRestTemplate {
+  
+  @Autowired
+  private RestTemplate authnAccountRestTemplate;
+  
+  @Value(value = "${sw-backend-base-api.uri}/v1/authn")
+  private String url;
+  
+  private JSONObject defaultErrorJson(Throwable cause) {
+    JSONObject error = new JSONObject();
+    
+    error.put("code", -1);
+    error.put("message", cause.getMessage());
+    error.put("error", cause.getMessage());
+    
+    return error;
+  }
+
+  public JSONObject account(String username) {
+    
+    try {
+      final String path = "/{username}/account";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnAccountRestTemplate.getForObject(this.url + path, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject roles(String username) {
+
+    try {
+      final String path = "/{username}/roles";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnAccountRestTemplate.getForObject(url, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject applications(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/applications";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnAccountRestTemplate.getForObject(this.url + path, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject menus(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/menus";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnAccountRestTemplate.getForObject(this.url + path, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject operations(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/operations";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnAccountRestTemplate.getForObject(this.url + path, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+  public JSONObject resources(String username, String applicationId) {
+
+    try {
+      final String path = "/{username}/resources";
+      final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username});
+      log.debug(url);
+      
+      return authnAccountRestTemplate.getForObject(this.url + path, JSONObject.class);
+    } catch (Exception e) {
+      e.printStackTrace();
+      
+      return defaultErrorJson(e);
+    }
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnAccountService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnAccountService.java
new file mode 100644
index 0000000..beeb74a
--- /dev/null
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/authn/service/AuthnAccountService.java
@@ -0,0 +1,71 @@
+package com.supwisdom.institute.backend.gateway.authn.service;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.supwisdom.institute.backend.gateway.authn.model.Account;
+import com.supwisdom.institute.backend.gateway.authn.model.Permission;
+import com.supwisdom.institute.backend.gateway.authn.model.Role;
+import com.supwisdom.institute.backend.gateway.authn.remote.web.client.AuthnAccountRemoteRestTemplate;
+
+@Service
+public class AuthnAccountService {
+  
+//  @Autowired
+//  private AuthnAccountRemoteFeignClient authnAccountRemote;
+  
+  @Autowired
+  private AuthnAccountRemoteRestTemplate authnAccountRemote;
+  
+  public Account account(String username) {
+    
+    JSONObject jsonObject = authnAccountRemote.account(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.toJavaObject(Account.class);
+    }
+    
+    return null;
+  }
+
+  public List<Role> roles(String username) {
+    
+    JSONObject jsonObject = authnAccountRemote.roles(username);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("roles").toJavaList(Role.class);
+    }
+    
+    return null;
+  }
+
+  public List<Permission> menus(String username, String applicationId) {
+    
+    JSONObject jsonObject = authnAccountRemote.menus(username, applicationId);
+    if (jsonObject == null) {
+      return null;
+    }
+    
+    if (jsonObject.getIntValue("code") == 0) {
+      JSONObject data = jsonObject.getJSONObject("data");
+      
+      return data.getJSONArray("permissions").toJavaList(Permission.class);
+    }
+    
+    return null;
+  }
+
+}
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/UserDetailsServiceConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/UserDetailsServiceConfig.java
index cafbc17..6f8be84 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/UserDetailsServiceConfig.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/configuration/UserDetailsServiceConfig.java
@@ -12,20 +12,20 @@
 @Configuration
 public class UserDetailsServiceConfig {
 
-//  @Bean
-//  public MyUserDetailsService userDetailsService() throws Exception {
-//    MyUserDetailsService myUserDetailsService = new MyUserDetailsService();
-//    log.debug("UserDetailsServiceConfig myUserDetailsService is {}", myUserDetailsService);
-//
-//    return myUserDetailsService;
-//  }
-
   @Bean
-  public InMemeryUserDetailsService userDetailsService() throws Exception {
-    InMemeryUserDetailsService inMemeryUserDetailsService = new InMemeryUserDetailsService();
-    log.debug("UserDetailsServiceConfig inMemeryUserDetailsService is {}", inMemeryUserDetailsService);
+  public MyUserDetailsService userDetailsService() throws Exception {
+    MyUserDetailsService myUserDetailsService = new MyUserDetailsService();
+    log.debug("UserDetailsServiceConfig myUserDetailsService is {}", myUserDetailsService);
 
-    return inMemeryUserDetailsService;
+    return myUserDetailsService;
   }
 
+//  @Bean
+//  public InMemeryUserDetailsService userDetailsService() throws Exception {
+//    InMemeryUserDetailsService inMemeryUserDetailsService = new InMemeryUserDetailsService();
+//    log.debug("UserDetailsServiceConfig inMemeryUserDetailsService is {}", inMemeryUserDetailsService);
+//
+//    return inMemeryUserDetailsService;
+//  }
+
 }
diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
index e5bfec8..171f2cf 100644
--- a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
+++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/MyUserDetailsService.java
@@ -15,6 +15,10 @@
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.crypto.password.PasswordEncoder;
 
+import com.supwisdom.institute.backend.gateway.authn.model.Account;
+import com.supwisdom.institute.backend.gateway.authn.model.Role;
+import com.supwisdom.institute.backend.gateway.authn.service.AuthnAccountService;
+
 import reactor.core.publisher.Mono;
 import lombok.extern.slf4j.Slf4j;
 
@@ -24,46 +28,16 @@
   @Autowired
   PasswordEncoder passwordEncoder;
 
-//  @Autowired
-//  AccountService accountService;
+  @Autowired
+  AuthnAccountService authnAccountService;
   
   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO: 
 
     log.debug("MyUserDetailsService.loadUserByUsername({})", username);
     
-//    Account account = accountService.loadByUsername(username);
-//    if (account == null) {
-//      throw new UsernameNotFoundException(String.format("%s not found", username));
-//    }
-//    
-//    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
-////    for (Role role : securityUser.getRoles()) {
-////      authorities.add(new SimpleGrantedAuthority(role.getCode()));
-////    }
-//    // FIXME: 
-//    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
-//    authorities.add(new SimpleGrantedAuthority("administrator"));
-//    authorities.add(new SimpleGrantedAuthority("user"));
-//    
-//    Map<String, Object> attributes = new HashMap<String, Object>();
-//    attributes.put("userId", account.getUser().getId());
-//    attributes.put("userUid", account.getUser().getUid());
-//    
-//    MyUser myUser = new MyUser(
-//        account.getAccountName(), 
-//        account.getUser().getPassWord(), 
-//        account.getActivation() && "Normal".equals(account.getState()) ? true : false, 
-//        account.getAccountExpiryDate() == null || Calendar.getInstance().getTime().before(account.getAccountExpiryDate()) ? true : false,
-//        true, 
-//        true, 
-//        authorities,
-//        attributes);
-//    
-//    log.debug("myUser is {}", myUser);
-    
-    MyUser myUser = null;
-    
+    MyUser myUser = loadUser(username);
+
     return myUser;
   }
 
@@ -71,9 +45,37 @@
   public Mono<UserDetails> findByUsername(String username) {
     log.debug("MyUserDetailsService.findByUsername({})", username);
     
-    MyUser myUser = null;
-    
+    MyUser myUser = loadUser(username);
+
     return Mono.just(myUser);
   }
+  
+  private MyUser loadUser(String username) {
+
+    Account account = authnAccountService.account(username);
+    if (account == null) {
+      throw new UsernameNotFoundException(String.format("%s not found", username));
+    }
+    
+    List<Role> roles = authnAccountService.roles(username);
+    
+    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+    for (Role role : roles) {
+      authorities.add(new SimpleGrantedAuthority(role.getCode()));
+    }
+    
+    Map<String, Object> attributes = new HashMap<String, Object>();
+    attributes.put("accountId", account.getId());
+    attributes.put("accountName", account.getName());
+    //attributes.put("accountMobile", account.getMobile());
+    //attributes.put("accountEmail", account.getEmail());
+    
+    MyUser myUser = new MyUser(account.getUsername(), passwordEncoder.encode(account.getPassword()), 
+        account.getEnabled(), account.getAccountNonExpired(), account.getCredentialsNonExpired(), account.getAccountNonLocked(), 
+        authorities, attributes);
+    log.debug("myUser is {}", myUser);
+
+    return myUser;
+  }
 
 }
diff --git a/gateway/src/main/resources/application-docker.yml b/gateway/src/main/resources/application-docker.yml
index b5ea861..219f4f7 100644
--- a/gateway/src/main/resources/application-docker.yml
+++ b/gateway/src/main/resources/application-docker.yml
@@ -103,3 +103,15 @@
 
 #CAS服务地址
 cas.server.host.url: ${CAS_SERVER_HOST_URL:https://cas-server/cas}
+
+
+sw-backend-base-api: 
+  uri: ${SW_BACKEND_BASE_API_URI:https://sw-backend-admin-sa}
+  client-auth:
+    enabled: ${SW_BACKEND_BASE_API_CLIENT_AUTH_ENABLED:true}
+    key-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEY_PASSWORD:}
+    key-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore}
+    key-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_KEYSTORE_PASSWORD:}
+    trust-store: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore}
+    trust-store-password: ${SW_BACKEND_BASE_API_CLIENT_AUTH_TRUSTSTORE_PASSWORD:}
+  
\ No newline at end of file
diff --git a/gateway/src/main/resources/application.yml b/gateway/src/main/resources/application.yml
index 7e88467..c72cbc7 100644
--- a/gateway/src/main/resources/application.yml
+++ b/gateway/src/main/resources/application.yml
@@ -94,3 +94,7 @@
 
 #CAS服务地址
 cas.server.host.url: https://cas.supwisdom.com/cas
+
+
+sw-backend-base-api:
+  uri: http://localhost:8082