From: 刘洪青 Date: Fri, 11 Oct 2019 05:41:46 +0000 (+0800) Subject: feat: 对接POA的用户服务、授权服务完成用户登录 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=773d4ab851bce936e9bbd37342ddccb8478db4d8;p=institute%2Fsw-backend.git feat: 对接POA的用户服务、授权服务完成用户登录 --- diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/model/Role.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/model/Role.java new file mode 100644 index 0000000..f8bd6e7 --- /dev/null +++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/model/Role.java @@ -0,0 +1,41 @@ +package com.supwisdom.institute.backend.gateway.agent.poa.model; + +import java.io.Serializable; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +public class Role implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -273297347381636597L; + + @Getter + @Setter + private String id; + + @Getter + @Setter + private String code; + + @Getter + @Setter + private String name; + + @Getter + @Setter + private String description; + + @Getter + @Setter + private Boolean enabled; + + @Getter + @Setter + private String externalId; + +} diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/model/UserInfoModel.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/model/UserInfoModel.java new file mode 100644 index 0000000..f9c23af --- /dev/null +++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/model/UserInfoModel.java @@ -0,0 +1,124 @@ +package com.supwisdom.institute.backend.gateway.agent.poa.model; + +import java.io.Serializable; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@ToString +public class UserInfoModel implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -822608981040243176L; + + @Getter + @Setter + private String id; + + @Getter + @Setter + private String accountName; + + @Getter + @Setter + private String identityTypeId; + @Getter + @Setter + private String identityTypeCode; + @Getter + @Setter + private String identityTypeName; + + @Getter + @Setter + private String organizationId; + @Getter + @Setter + private String organizationCode; + @Getter + @Setter + private String organizationName; + + @Getter + @Setter + private String uid; + + @Getter + @Setter + private String name; + + @Getter + @Setter + private String fullNameSpelling; + @Getter + @Setter + private String nameSpelling; + + @Getter + @Setter + private String certificateTypeId; + @Getter + @Setter + private String certificateTypeCode; + @Getter + @Setter + private String certificateTypeName; + @Getter + @Setter + private String certificateNumber; + + @Getter + @Setter + private String genderId; + @Getter + @Setter + private String genderCode; + @Getter + @Setter + private String genderName; + + @Getter + @Setter + private String nationId; + @Getter + @Setter + private String nationCode; + @Getter + @Setter + private String nationName; + + @Getter + @Setter + private String countryId; + @Getter + @Setter + private String countryCode; + @Getter + @Setter + private String countryName; + + @Getter + @Setter + private String addressId; + @Getter + @Setter + private String addressCode; + @Getter + @Setter + private String addressName; + + @Getter + @Setter + private String phoneNumber; + @Getter + @Setter + private String email; + + @Getter + @Setter + private String imageUrl; + +} diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/remote/configuration/AgentPoaRestTemplateConfig.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/remote/configuration/AgentPoaRestTemplateConfig.java new file mode 100644 index 0000000..f26b518 --- /dev/null +++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/remote/configuration/AgentPoaRestTemplateConfig.java @@ -0,0 +1,100 @@ +package com.supwisdom.institute.backend.gateway.agent.poa.remote.configuration; + +import javax.net.ssl.SSLContext; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.util.ResourceUtils; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AgentPoaRestTemplateConfig { + + @Bean + public ClientHttpRequestFactory simpleClientHttpRequestFactory( + @Value("${sw-backend-agent-poa.client-auth.enabled:false}") boolean enabled, + @Value("${sw-backend-agent-poa.client-auth.key-password:}") String keyPassword, + @Value("${sw-backend-agent-poa.client-auth.key-store:}") String keyStore, + @Value("${sw-backend-agent-poa.client-auth.key-store-password:}") String keyStorePassword, + @Value("${sw-backend-agent-poa.client-auth.trust-store:}") String trustStore, + @Value("${sw-backend-agent-poa.client-auth.trust-store-password:}") String trustStorePassword + ) { + if (!enabled) { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setReadTimeout(5000);// 单位为ms + factory.setConnectTimeout(5000);// 单位为ms + return factory; + } + + SSLContextBuilder sslContextBuilder = SSLContexts.custom(); + + if (trustStore == null || trustStore.isEmpty()) { + } else { + try { + sslContextBuilder + .loadTrustMaterial( + ResourceUtils.getFile(trustStore), + trustStorePassword.toCharArray() + ); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (keyStore == null || keyStore.isEmpty()) { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setReadTimeout(5000);// 单位为ms + factory.setConnectTimeout(5000);// 单位为ms + return factory; + } else { + try { + sslContextBuilder + .loadKeyMaterial( + ResourceUtils.getFile(keyStore), + keyStorePassword.toCharArray(), + keyPassword.toCharArray()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + try { + SSLContext sslContext = sslContextBuilder.build(); + + SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( + sslContext, + SSLConnectionSocketFactory.getDefaultHostnameVerifier()); + + CloseableHttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(sslsf) + .build(); + + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); + factory.setReadTimeout(5000);// 单位为ms + factory.setConnectTimeout(5000);// 单位为ms + return factory; + } catch (Exception e) { + e.printStackTrace(); + } + + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setReadTimeout(5000);// 单位为ms + factory.setConnectTimeout(5000);// 单位为ms + return factory; + } + + @Bean(name = "agentPoaRestTemplate") + public RestTemplate agentPoaRestTemplate(ClientHttpRequestFactory requestFactory) { + return new RestTemplate(requestFactory); + } + +} diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/remote/web/client/AgentPoaRemoteClient.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/remote/web/client/AgentPoaRemoteClient.java new file mode 100644 index 0000000..aaa98bf --- /dev/null +++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/remote/web/client/AgentPoaRemoteClient.java @@ -0,0 +1,61 @@ +package com.supwisdom.institute.backend.gateway.agent.poa.remote.web.client; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import com.alibaba.fastjson.JSONObject; + +@Slf4j +@Component +public class AgentPoaRemoteClient { + + @Autowired + private RestTemplate agentPoaRestTemplate; + + @Value(value = "${sw-backend-agent-poa.uri}/v1/poa") + private String url; + + private JSONObject defaultErrorJson(Throwable cause) { + JSONObject error = new JSONObject(); + + error.put("code", -1); + error.put("message", cause.getMessage()); + error.put("error", cause.getMessage()); + + return error; + } + + public JSONObject loadUserInfoByAccountName(String accountName) { + try { + final String path = "/user/users/accountName/{accountName}"; + final String url = this.url + StringUtils.replaceEach(path, new String[] {"{accountName}"}, new String[] {accountName}); + log.debug(url); + + return agentPoaRestTemplate.getForObject(url, JSONObject.class); + } catch (Exception e) { + //e.printStackTrace(); + + return defaultErrorJson(e); + } + } + + public JSONObject loadAccountApplicationRoles(String username) { + try { + final String path = "/authz/roles/account/{username}"; + final String url = this.url + StringUtils.replaceEach(path, new String[] {"{username}"}, new String[] {username}); + log.debug(url); + + return agentPoaRestTemplate.getForObject(url, JSONObject.class); + } catch (Exception e) { + //e.printStackTrace(); + + return defaultErrorJson(e); + } + } + +} diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/service/AuthzService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/service/AuthzService.java new file mode 100644 index 0000000..1d040b6 --- /dev/null +++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/service/AuthzService.java @@ -0,0 +1,38 @@ +package com.supwisdom.institute.backend.gateway.agent.poa.service; + +import java.util.List; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.supwisdom.institute.backend.gateway.agent.poa.model.Role; +import com.supwisdom.institute.backend.gateway.agent.poa.remote.web.client.AgentPoaRemoteClient; + +@Slf4j +@Service +public class AuthzService { + + @Autowired + private AgentPoaRemoteClient agentPoaRemoteClient; + + public List loadAccountApplicationRoles(String username) { + + JSONObject jsonObject = agentPoaRemoteClient.loadAccountApplicationRoles(username); + if (jsonObject == null) { + return null; + } + + JSONObject data = jsonObject.getJSONObject("data"); + + JSONArray roleArray = data.getJSONArray("roles"); + + List roles = roleArray.toJavaList(Role.class); + log.debug("{}", roles); + + return roles; + } +} diff --git a/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/service/UserService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/service/UserService.java new file mode 100644 index 0000000..777c596 --- /dev/null +++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/agent/poa/service/UserService.java @@ -0,0 +1,34 @@ +package com.supwisdom.institute.backend.gateway.agent.poa.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.alibaba.fastjson.JSONObject; +import com.supwisdom.institute.backend.gateway.agent.poa.model.UserInfoModel; +import com.supwisdom.institute.backend.gateway.agent.poa.remote.web.client.AgentPoaRemoteClient; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class UserService { + + @Autowired + private AgentPoaRemoteClient agentPoaRemoteClient; + + public UserInfoModel loadUserInfoByAccountName(String accountName) { + + JSONObject jsonObject = agentPoaRemoteClient.loadUserInfoByAccountName(accountName); + if (jsonObject == null) { + return null; + } + + JSONObject data = jsonObject.getJSONObject("data"); + + UserInfoModel userInfoModel = data.toJavaObject(UserInfoModel.class); + log.debug("userInfoModel: [{}]", userInfoModel); + + return userInfoModel; + } + +} 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 6f8be84..5bc7d6b 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 @@ -1,10 +1,12 @@ package com.supwisdom.institute.backend.gateway.configuration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.supwisdom.institute.backend.gateway.security.core.userdetails.InMemeryUserDetailsService; import com.supwisdom.institute.backend.gateway.security.core.userdetails.MyUserDetailsService; +import com.supwisdom.institute.backend.gateway.security.core.userdetails.PoaUserDetailsService; import lombok.extern.slf4j.Slf4j; @@ -13,19 +15,30 @@ import lombok.extern.slf4j.Slf4j; public class UserDetailsServiceConfig { @Bean - public MyUserDetailsService userDetailsService() throws Exception { + @ConditionalOnProperty(name = "sw-backend-gateway.security.core.userdetails.service.impl", havingValue = "memery", matchIfMissing = true) + public InMemeryUserDetailsService inMemeryUserDetailsService() throws Exception { + InMemeryUserDetailsService inMemeryUserDetailsService = new InMemeryUserDetailsService(); + log.debug("UserDetailsServiceConfig inMemeryUserDetailsService is {}", inMemeryUserDetailsService); + + return inMemeryUserDetailsService; + } + + @Bean + @ConditionalOnProperty(name = "sw-backend-gateway.security.core.userdetails.service.impl", havingValue = "poa", matchIfMissing = false) + public PoaUserDetailsService poaUserDetailsService() throws Exception { + PoaUserDetailsService poaUserDetailsService = new PoaUserDetailsService(); + log.debug("UserDetailsServiceConfig poaUserDetailsService is {}", poaUserDetailsService); + + return poaUserDetailsService; + } + + @Bean + @ConditionalOnProperty(name = "sw-backend-gateway.security.core.userdetails.service.impl", havingValue = "base", matchIfMissing = false) + public MyUserDetailsService myUserDetailsService() throws Exception { MyUserDetailsService myUserDetailsService = new MyUserDetailsService(); log.debug("UserDetailsServiceConfig myUserDetailsService is {}", myUserDetailsService); return myUserDetailsService; } -// @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/PoaUserDetailsService.java b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/PoaUserDetailsService.java new file mode 100644 index 0000000..4479be4 --- /dev/null +++ b/gateway/src/main/java/com/supwisdom/institute/backend/gateway/security/core/userdetails/PoaUserDetailsService.java @@ -0,0 +1,99 @@ +package com.supwisdom.institute.backend.gateway.security.core.userdetails; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.ReactiveUserDetailsService; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.password.PasswordEncoder; + +import com.supwisdom.institute.backend.gateway.agent.poa.model.Role; +import com.supwisdom.institute.backend.gateway.agent.poa.model.UserInfoModel; +import com.supwisdom.institute.backend.gateway.agent.poa.service.AuthzService; +import com.supwisdom.institute.backend.gateway.agent.poa.service.UserService; + +import reactor.core.publisher.Mono; + +@Slf4j +public class PoaUserDetailsService implements UserDetailsService, ReactiveUserDetailsService { + + @Autowired + PasswordEncoder passwordEncoder; + + @Autowired + UserService userService; + @Autowired + AuthzService authzService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + + log.debug("PoaUserDetailsService.loadUserByUsername({})", username); + + MyUser myUser = loadUser(username); + + return myUser; + } + + @Override + public Mono findByUsername(String username) { + + log.debug("PoaUserDetailsService.findByUsername({})", username); + + MyUser myUser = loadUser(username); + + return Mono.just(myUser); + } + + private MyUser loadUser(String username) { + + UserInfoModel userInfoModel = userService.loadUserInfoByAccountName(username); + if (userInfoModel == null) { + throw new UsernameNotFoundException(String.format("%s not found", username)); + } + + List roles = authzService.loadAccountApplicationRoles(username); + + List authorities = new ArrayList(); + for (Role role : roles) { + authorities.add(new SimpleGrantedAuthority(role.getCode())); + } + + Map attributes = new HashMap(); + attributes.put("accountId", userInfoModel.getId()); + attributes.put("accountName", userInfoModel.getAccountName()); + + attributes.put("identityTypeId", userInfoModel.getIdentityTypeId()); + attributes.put("identityTypeCode", userInfoModel.getIdentityTypeCode()); + attributes.put("identityTypeName", userInfoModel.getIdentityTypeName()); + + attributes.put("organizationId", userInfoModel.getOrganizationId()); + attributes.put("organizationCode", userInfoModel.getOrganizationCode()); + attributes.put("organizationName", userInfoModel.getOrganizationName()); + + attributes.put("uid", userInfoModel.getUid()); + attributes.put("name", userInfoModel.getName()); + attributes.put("genderName", userInfoModel.getGenderName()); + attributes.put("nationName", userInfoModel.getNationName()); + attributes.put("countryName", userInfoModel.getCountryName()); + attributes.put("addressName", userInfoModel.getAddressName()); + + attributes.put("imageUrl", userInfoModel.getImageUrl()); + + MyUser myUser = new MyUser(userInfoModel.getAccountName(), passwordEncoder.encode(StringUtils.reverse(userInfoModel.getAccountName())), + authorities, attributes); + log.debug("myUser is {}", myUser); + + return myUser; + } +} diff --git a/gateway/src/main/resources/application-docker.yml b/gateway/src/main/resources/application-docker.yml index 661b919..4a25bab 100644 --- a/gateway/src/main/resources/application-docker.yml +++ b/gateway/src/main/resources/application-docker.yml @@ -31,7 +31,6 @@ spring: jackson: time-zone: ${JACKSON_TIME_ZONE:Asia/Shanghai} - ## # spring cloud gateway # @@ -105,6 +104,14 @@ app.logout.url: ${APP_LOGOUT_URL:/cas/logout} cas.server.host.url: ${CAS_SERVER_HOST_URL:https://cas-server/cas} +## +# 认证时,用户信息服务实现 +# memery 内存,用户名密码一致即可登录,测试用,默认; +# base 后端base服务; +# poa 开放平台服务,建议和cas一起使用) +sw-backend-gateway.security.core.userdetails.service.impl: ${SW_BACKEND_GATEWAY_SECURITY_CORE_USERDETAILS_SERVICE_IMPL:base} + + sw-backend-base-api: uri: ${SW_BACKEND_BASE_API_URI:https://sw-backend-admin-sa} client-auth: @@ -114,4 +121,13 @@ sw-backend-base-api: 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 + +sw-backend-agent-poa: + uri: ${SW_BACKEND_AGENT_POA_URI:https://sw-backend-agent} + client-auth: + enabled: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_ENABLED:true} + key-password: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_KEY_PASSWORD:} + key-store: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_KEYSTORE_FILE:file:/certs/common/common.keystore} + key-store-password: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_KEYSTORE_PASSWORD:} + trust-store: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_TRUSTSTORE_FILE:file:/certs/common/common.truststore} + trust-store-password: ${SW_BACKEND_AGENT_POA_CLIENT_AUTH_TRUSTSTORE_PASSWORD:} diff --git a/gateway/src/main/resources/application.yml b/gateway/src/main/resources/application.yml index 1f3be47..2c4fde8 100644 --- a/gateway/src/main/resources/application.yml +++ b/gateway/src/main/resources/application.yml @@ -95,5 +95,16 @@ app.logout.url: /cas/logout cas.server.host.url: https://cas.supwisdom.com/cas +## +# 认证时,用户信息服务实现 +# memery 内存,用户名密码一致即可登录,测试用,默认; +# base 后端base服务; +# poa 开放平台服务,建议和cas一起使用) +sw-backend-gateway.security.core.userdetails.service.impl: memery + + sw-backend-base-api: uri: http://localhost:8082 + +sw-backend-agent-poa: + uri: http://localhost:8090