mirror of
https://github.com/vran-dev/databasir.git
synced 2025-09-18 01:37:12 +08:00
support wework oauth login (#267)
* feat: jooq generate oauth_app_property * feat: jooq generate -> remove oauth_app unused columns * feat: support wework oauth login * feat: update oauth property validator
This commit is contained in:
@@ -1,26 +1,33 @@
|
||||
package com.databasir.core.domain.app;
|
||||
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.app.converter.OauthAppConverter;
|
||||
import com.databasir.core.domain.app.converter.OAuthAppResponseConverter;
|
||||
import com.databasir.core.domain.app.converter.OauthAppConverter;
|
||||
import com.databasir.core.domain.app.data.*;
|
||||
import com.databasir.core.domain.app.handler.OAuthProcessResult;
|
||||
import com.databasir.core.domain.app.handler.OpenAuthHandlers;
|
||||
import com.databasir.core.domain.app.validator.OauthPropertiesValidator;
|
||||
import com.databasir.core.domain.user.data.UserCreateRequest;
|
||||
import com.databasir.core.domain.user.data.UserDetailResponse;
|
||||
import com.databasir.core.domain.user.service.UserService;
|
||||
import com.databasir.dao.impl.OauthAppDao;
|
||||
import com.databasir.dao.impl.OauthAppPropertyDao;
|
||||
import com.databasir.dao.tables.pojos.OauthApp;
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@@ -31,12 +38,20 @@ public class OpenAuthAppService {
|
||||
|
||||
private final OauthAppDao oauthAppDao;
|
||||
|
||||
private final OauthAppPropertyDao oauthAppPropertyDao;
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final OAuthAppResponseConverter oauthAppResponseConverter;
|
||||
|
||||
private final OauthAppConverter oauthAppConverter;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final OauthPropertiesValidator oauthPropertiesValidator;
|
||||
|
||||
private List<OAuthAppPlatformResponse> platforms = new ArrayList<>();
|
||||
|
||||
public UserDetailResponse oauthCallback(String registrationId, Map<String, String[]> params) {
|
||||
// process by handler
|
||||
OAuthProcessResult result = openAuthHandlers.process(registrationId, params);
|
||||
@@ -63,25 +78,37 @@ public class OpenAuthAppService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteById(Integer id) {
|
||||
if (oauthAppDao.existsById(id)) {
|
||||
oauthAppDao.deleteById(id);
|
||||
oauthAppPropertyDao.deleteByOauthAppId(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateById(OAuthAppUpdateRequest request) {
|
||||
oauthPropertiesValidator.validate(request, listPlatforms());
|
||||
OauthApp pojo = oauthAppConverter.of(request);
|
||||
try {
|
||||
oauthAppDao.updateById(pojo);
|
||||
oauthAppPropertyDao.deleteByOauthAppId(pojo.getId());
|
||||
List<OauthAppProperty> properties = oauthAppConverter.toProperty(pojo.getId(), request.getProperties());
|
||||
oauthAppPropertyDao.batchInsert(properties);
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw DomainErrors.REGISTRATION_ID_DUPLICATE.exception();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Integer create(OAuthAppCreateRequest request) {
|
||||
oauthPropertiesValidator.validate(request, listPlatforms());
|
||||
OauthApp pojo = oauthAppConverter.of(request);
|
||||
try {
|
||||
return oauthAppDao.insertAndReturnId(pojo);
|
||||
Integer oauthAppId = oauthAppDao.insertAndReturnId(pojo);
|
||||
List<OauthAppProperty> properties = oauthAppConverter.toProperty(oauthAppId, request.getProperties());
|
||||
oauthAppPropertyDao.batchInsert(properties);
|
||||
return oauthAppId;
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw DomainErrors.REGISTRATION_ID_DUPLICATE.exception();
|
||||
}
|
||||
@@ -92,6 +119,25 @@ public class OpenAuthAppService {
|
||||
}
|
||||
|
||||
public Optional<OAuthAppDetailResponse> getOne(Integer id) {
|
||||
return oauthAppDao.selectOptionalById(id).map(oauthAppConverter::toDetailResponse);
|
||||
return oauthAppDao.selectOptionalById(id).map(oauthApp -> {
|
||||
List<OauthAppProperty> properties = oauthAppPropertyDao.selectByOauthAppId(id);
|
||||
return oauthAppConverter.toDetailResponse(oauthApp, properties);
|
||||
});
|
||||
}
|
||||
|
||||
public List<OAuthAppPlatformResponse> listPlatforms() {
|
||||
if (platforms == null || platforms.isEmpty()) {
|
||||
String schemaPath = "classpath:/oauth/platform-properties-schema.json";
|
||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
Resource resource = resourceLoader.getResource(schemaPath);
|
||||
try (InputStream inputStream = resource.getInputStream()) {
|
||||
return objectMapper.readValue(inputStream, new TypeReference<List<OAuthAppPlatformResponse>>() {
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
return platforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,31 @@
|
||||
package com.databasir.core.domain.app.common;
|
||||
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CommonProperties {
|
||||
|
||||
String AUTH_HOST = "auth_host";
|
||||
|
||||
String RESOURCE_HOST = "resource_host";
|
||||
|
||||
CommonProperties INSTANCE = new CommonProperties() {
|
||||
};
|
||||
|
||||
default String get(List<OauthAppProperty> properties, String key) {
|
||||
return properties.stream()
|
||||
.filter(p -> p.getName().equals(key))
|
||||
.map(OauthAppProperty::getValue)
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
default String getAuthHost(List<OauthAppProperty> properties) {
|
||||
return get(properties, AUTH_HOST);
|
||||
}
|
||||
|
||||
default String getResourceHost(List<OauthAppProperty> properties) {
|
||||
return get(properties, RESOURCE_HOST);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package com.databasir.core.domain.app.common;
|
||||
|
||||
public interface GithubProperties extends CommonProperties {
|
||||
|
||||
String CLIENT_ID = "client_id";
|
||||
|
||||
String CLIENT_SECRET = "client_secret";
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package com.databasir.core.domain.app.common;
|
||||
|
||||
public interface GitlabProperties extends CommonProperties {
|
||||
|
||||
String CLIENT_ID = "client_id";
|
||||
|
||||
String CLIENT_SECRET = "client_secret";
|
||||
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.databasir.core.domain.app.common;
|
||||
|
||||
public interface WeWorkProperties extends CommonProperties {
|
||||
|
||||
String APP_ID = "appid";
|
||||
|
||||
String AGENT_ID = "agentid";
|
||||
|
||||
String SECRET = "secret";
|
||||
|
||||
String REDIRECT_URL = "redirect_uri";
|
||||
}
|
@@ -1,13 +1,15 @@
|
||||
package com.databasir.core.domain.app.converter;
|
||||
|
||||
import com.databasir.core.domain.app.data.OAuthAppCreateRequest;
|
||||
import com.databasir.core.domain.app.data.OAuthAppDetailResponse;
|
||||
import com.databasir.core.domain.app.data.OAuthAppPageResponse;
|
||||
import com.databasir.core.domain.app.data.OAuthAppUpdateRequest;
|
||||
import com.databasir.core.domain.app.data.*;
|
||||
import com.databasir.dao.tables.pojos.OauthApp;
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface OauthAppConverter {
|
||||
|
||||
@@ -22,5 +24,17 @@ public interface OauthAppConverter {
|
||||
|
||||
OAuthAppPageResponse toPageResponse(OauthApp pojo);
|
||||
|
||||
OAuthAppDetailResponse toDetailResponse(OauthApp pojo);
|
||||
OAuthAppDetailResponse toDetailResponse(OauthApp pojo, List<OauthAppProperty> properties);
|
||||
|
||||
@Mapping(target = "createAt", ignore = true)
|
||||
@Mapping(target = "id", ignore = true)
|
||||
OauthAppProperty toProperty(Integer oauthAppId, OauthAppPropertyData property);
|
||||
|
||||
default List<OauthAppProperty> toProperty(Integer oauthAppId, List<OauthAppPropertyData> properties) {
|
||||
if (properties == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return properties.stream().map(prop -> toProperty(oauthAppId, prop)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@ import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OAuthAppCreateRequest {
|
||||
@@ -20,17 +22,6 @@ public class OAuthAppCreateRequest {
|
||||
|
||||
private String appIcon;
|
||||
|
||||
@NotBlank
|
||||
private String authUrl;
|
||||
private List<OauthAppPropertyData> properties = new ArrayList<>();
|
||||
|
||||
@NotBlank
|
||||
private String resourceUrl;
|
||||
|
||||
@NotBlank
|
||||
private String clientId;
|
||||
|
||||
@NotBlank
|
||||
private String clientSecret;
|
||||
|
||||
private String scope;
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@ import com.databasir.dao.enums.OAuthAppType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OAuthAppDetailResponse {
|
||||
@@ -18,13 +20,7 @@ public class OAuthAppDetailResponse {
|
||||
|
||||
private String registrationId;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private String authUrl;
|
||||
|
||||
private String resourceUrl;
|
||||
private List<OauthAppPropertyData> properties = new ArrayList<>();
|
||||
|
||||
private LocalDateTime updateAt;
|
||||
|
||||
|
@@ -18,12 +18,6 @@ public class OAuthAppPageResponse {
|
||||
|
||||
private String registrationId;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String authUrl;
|
||||
|
||||
private String resourceUrl;
|
||||
|
||||
private LocalDateTime updateAt;
|
||||
|
||||
private LocalDateTime createAt;
|
||||
|
@@ -0,0 +1,42 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import com.databasir.dao.enums.OAuthAppType;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class OAuthAppPlatformResponse {
|
||||
|
||||
private OAuthAppType authAppType;
|
||||
|
||||
private String authAppName;
|
||||
|
||||
@Builder.Default
|
||||
private List<OAuthAppPlatformProperty> properties = new ArrayList<>();
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class OAuthAppPlatformProperty {
|
||||
|
||||
private String name;
|
||||
|
||||
private String label;
|
||||
|
||||
private String description;
|
||||
|
||||
private Boolean required;
|
||||
|
||||
private String defaultValue;
|
||||
|
||||
}
|
||||
}
|
@@ -5,6 +5,8 @@ import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class OAuthAppUpdateRequest {
|
||||
@@ -23,18 +25,6 @@ public class OAuthAppUpdateRequest {
|
||||
|
||||
private String appIcon;
|
||||
|
||||
@NotBlank
|
||||
private String authUrl;
|
||||
|
||||
@NotBlank
|
||||
private String resourceUrl;
|
||||
|
||||
@NotBlank
|
||||
private String clientId;
|
||||
|
||||
@NotBlank
|
||||
private String clientSecret;
|
||||
|
||||
private String scope;
|
||||
private List<OauthAppPropertyData> properties = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,18 @@
|
||||
package com.databasir.core.domain.app.data;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class OauthAppPropertyData {
|
||||
|
||||
private String name;
|
||||
|
||||
private String value;
|
||||
|
||||
}
|
@@ -1,10 +1,13 @@
|
||||
package com.databasir.core.domain.app.handler;
|
||||
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.app.common.CommonProperties;
|
||||
import com.databasir.core.domain.app.common.GithubProperties;
|
||||
import com.databasir.core.domain.app.exception.DatabasirAuthenticationException;
|
||||
import com.databasir.core.infrastructure.remote.github.GithubRemoteService;
|
||||
import com.databasir.dao.enums.OAuthAppType;
|
||||
import com.databasir.dao.tables.pojos.OauthApp;
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jooq.tools.StringUtils;
|
||||
@@ -12,6 +15,7 @@ import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@@ -26,13 +30,16 @@ public class GithubOpenAuthHandler implements OpenAuthHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorizationUrl(OauthApp app, Map<String, String[]> requestParams) {
|
||||
String authUrl = app.getAuthUrl();
|
||||
String clientId = app.getClientId();
|
||||
public String authorizationUrl(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> requestParams) {
|
||||
String authUrl = CommonProperties.INSTANCE.getAuthHost(properties);
|
||||
String clientId = CommonProperties.INSTANCE.get(properties, GithubProperties.CLIENT_ID);
|
||||
String authorizeUrl = authUrl + "/login/oauth/authorize";
|
||||
String url = UriComponentsBuilder.fromUriString(authorizeUrl)
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(authorizeUrl)
|
||||
.queryParam("client_id", clientId)
|
||||
.queryParam("scope", "read:user user:email")
|
||||
.queryParam("scope", "read:user user:email");
|
||||
String url = builder
|
||||
.encode()
|
||||
.build()
|
||||
.toUriString();
|
||||
@@ -40,10 +47,12 @@ public class GithubOpenAuthHandler implements OpenAuthHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthProcessResult process(OauthApp app, Map<String, String[]> requestParams) {
|
||||
String clientId = app.getClientId();
|
||||
String clientSecret = app.getClientSecret();
|
||||
String authUrl = app.getAuthUrl();
|
||||
public OAuthProcessResult process(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> requestParams) {
|
||||
String authUrl = CommonProperties.INSTANCE.getAuthHost(properties);
|
||||
String clientId = CommonProperties.INSTANCE.get(properties, GithubProperties.CLIENT_ID);
|
||||
String clientSecret = CommonProperties.INSTANCE.get(properties, GithubProperties.CLIENT_SECRET);
|
||||
|
||||
String code = requestParams.get("code")[0];
|
||||
JsonNode tokenNode = githubRemoteService.getToken(authUrl, clientId, clientSecret, code)
|
||||
@@ -55,7 +64,8 @@ public class GithubOpenAuthHandler implements OpenAuthHandler {
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
throw new CredentialsExpiredException("授权失效,请重新登陆");
|
||||
}
|
||||
String resourceUrl = app.getResourceUrl();
|
||||
|
||||
String resourceUrl = CommonProperties.INSTANCE.get(properties, GithubProperties.RESOURCE_HOST);
|
||||
String email = null;
|
||||
for (JsonNode node : githubRemoteService.getEmail(resourceUrl, accessToken)) {
|
||||
if (node.get("primary").asBoolean()) {
|
||||
|
@@ -1,15 +1,19 @@
|
||||
package com.databasir.core.domain.app.handler;
|
||||
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.app.common.CommonProperties;
|
||||
import com.databasir.core.domain.app.common.GitlabProperties;
|
||||
import com.databasir.core.domain.app.exception.DatabasirAuthenticationException;
|
||||
import com.databasir.core.infrastructure.remote.gitlab.GitlabRemoteService;
|
||||
import com.databasir.dao.enums.OAuthAppType;
|
||||
import com.databasir.dao.tables.pojos.OauthApp;
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@@ -24,42 +28,52 @@ public class GitlabOpenAuthHandler implements OpenAuthHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorizationUrl(OauthApp app, Map<String, String[]> params) {
|
||||
public String authorizationUrl(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> params) {
|
||||
if (!params.containsKey("redirect_uri")) {
|
||||
throw DomainErrors.MISS_REQUIRED_PARAMETERS.exception("缺少参数 redirect_uri", null);
|
||||
}
|
||||
String authUrl = app.getAuthUrl();
|
||||
String clientId = app.getClientId();
|
||||
|
||||
String authUrl = CommonProperties.INSTANCE.getAuthHost(properties);
|
||||
String clientId = CommonProperties.INSTANCE.get(properties, GitlabProperties.CLIENT_ID);
|
||||
String authorizeUrl = authUrl + "/oauth/authorize";
|
||||
String redirectUri = params.get("redirect_uri")[0];
|
||||
String url = UriComponentsBuilder.fromUriString(authorizeUrl)
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(authorizeUrl)
|
||||
.queryParam("client_id", clientId)
|
||||
.queryParam("redirect_uri", redirectUri)
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("state", redirectUri)
|
||||
.queryParam("scope", "read_user")
|
||||
.queryParam("response_type", "code")
|
||||
.queryParam("scope", "read_user");
|
||||
return builder
|
||||
.encode()
|
||||
.build()
|
||||
.toUriString();
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthProcessResult process(OauthApp app, Map<String, String[]> requestParams) {
|
||||
public OAuthProcessResult process(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> requestParams) {
|
||||
if (!requestParams.containsKey("redirect_uri")) {
|
||||
throw DomainErrors.MISS_REQUIRED_PARAMETERS.exception("缺少参数 redirect_uri", null);
|
||||
}
|
||||
String url = app.getAuthUrl();
|
||||
|
||||
String url = CommonProperties.INSTANCE.getAuthHost(properties);
|
||||
String code = requestParams.get("code")[0];
|
||||
String state = requestParams.get("state")[0];
|
||||
String redirectUri = requestParams.get("redirect_uri")[0];
|
||||
String clientId = CommonProperties.INSTANCE.get(properties, GitlabProperties.CLIENT_ID);
|
||||
String secret = CommonProperties.INSTANCE.get(properties, GitlabProperties.CLIENT_SECRET);
|
||||
JsonNode accessTokenData =
|
||||
gitlabRemoteService.getAccessToken(url, code, app.getClientId(), app.getClientSecret(), redirectUri);
|
||||
gitlabRemoteService.getAccessToken(url, code, clientId, secret, redirectUri);
|
||||
if (accessTokenData == null) {
|
||||
throw new DatabasirAuthenticationException(DomainErrors.NETWORK_ERROR.exception());
|
||||
}
|
||||
String accessToken = accessTokenData.get("access_token").asText();
|
||||
JsonNode userData = gitlabRemoteService.getUser(app.getResourceUrl(), accessToken);
|
||||
|
||||
String resourceUrl = CommonProperties.INSTANCE.getResourceHost(properties);
|
||||
JsonNode userData = gitlabRemoteService.getUser(resourceUrl, accessToken);
|
||||
if (userData == null) {
|
||||
throw new DatabasirAuthenticationException(DomainErrors.NETWORK_ERROR.exception());
|
||||
}
|
||||
|
@@ -2,14 +2,20 @@ package com.databasir.core.domain.app.handler;
|
||||
|
||||
import com.databasir.dao.enums.OAuthAppType;
|
||||
import com.databasir.dao.tables.pojos.OauthApp;
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface OpenAuthHandler {
|
||||
|
||||
boolean support(OAuthAppType oauthAppType);
|
||||
|
||||
String authorizationUrl(OauthApp app, Map<String, String[]> requestParams);
|
||||
String authorizationUrl(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> requestParams);
|
||||
|
||||
OAuthProcessResult process(OauthApp app, Map<String, String[]> requestParams);
|
||||
OAuthProcessResult process(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> requestParams);
|
||||
}
|
||||
|
@@ -2,7 +2,9 @@ package com.databasir.core.domain.app.handler;
|
||||
|
||||
import com.databasir.core.domain.app.exception.DatabasirAuthenticationException;
|
||||
import com.databasir.dao.impl.OauthAppDao;
|
||||
import com.databasir.dao.impl.OauthAppPropertyDao;
|
||||
import com.databasir.dao.tables.pojos.OauthApp;
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -19,12 +21,15 @@ public class OpenAuthHandlers {
|
||||
|
||||
private final OauthAppDao oauthAppDao;
|
||||
|
||||
private final OauthAppPropertyDao oauthAppPropertyDao;
|
||||
|
||||
public String authorizeUrl(String registrationId, Map<String, String[]> parameters) {
|
||||
OauthApp app = oauthAppDao.selectByRegistrationId(registrationId);
|
||||
List<OauthAppProperty> properties = oauthAppPropertyDao.selectByOauthAppId(app.getId());
|
||||
return handlers.stream()
|
||||
.filter(handler -> handler.support(app.getAppType()))
|
||||
.findFirst()
|
||||
.map(handler -> handler.authorizationUrl(app, parameters))
|
||||
.map(handler -> handler.authorizationUrl(app, properties, parameters))
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
@@ -34,10 +39,11 @@ public class OpenAuthHandlers {
|
||||
var bizErr = REGISTRATION_ID_NOT_FOUND.exception("应用 ID [" + registrationId + "] 不存在");
|
||||
return new DatabasirAuthenticationException(bizErr);
|
||||
});
|
||||
List<OauthAppProperty> properties = oauthAppPropertyDao.selectByOauthAppId(app.getId());
|
||||
return handlers.stream()
|
||||
.filter(handler -> handler.support(app.getAppType()))
|
||||
.findFirst()
|
||||
.map(handler -> handler.process(app, parameters))
|
||||
.map(handler -> handler.process(app, properties, parameters))
|
||||
.orElseThrow();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,77 @@
|
||||
package com.databasir.core.domain.app.handler;
|
||||
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.app.common.WeWorkProperties;
|
||||
import com.databasir.core.infrastructure.remote.wework.WeWorkRemoteService;
|
||||
import com.databasir.dao.enums.OAuthAppType;
|
||||
import com.databasir.dao.tables.pojos.OauthApp;
|
||||
import com.databasir.dao.tables.pojos.OauthAppProperty;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.databasir.core.domain.app.common.CommonProperties.INSTANCE;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class WeWorkOpenAuthHandler implements OpenAuthHandler {
|
||||
|
||||
private final WeWorkRemoteService weWorkRemoteService;
|
||||
|
||||
@Override
|
||||
public boolean support(OAuthAppType oauthAppType) {
|
||||
return oauthAppType == OAuthAppType.WE_WORK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String authorizationUrl(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> requestParams) {
|
||||
String authUrl = INSTANCE.getAuthHost(properties);
|
||||
String authorizeUrl = authUrl + "/wwopen/sso/qrConnect";
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(authorizeUrl)
|
||||
.queryParam("appid", INSTANCE.get(properties, WeWorkProperties.APP_ID))
|
||||
.queryParam("agentid", INSTANCE.get(properties, WeWorkProperties.AGENT_ID))
|
||||
.queryParam("redirect_uri", INSTANCE.get(properties, WeWorkProperties.REDIRECT_URL));
|
||||
String url = builder
|
||||
.encode()
|
||||
.build()
|
||||
.toUriString();
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuthProcessResult process(OauthApp app,
|
||||
List<OauthAppProperty> properties,
|
||||
Map<String, String[]> requestParams) {
|
||||
if (!requestParams.containsKey("redirect_uri")) {
|
||||
throw DomainErrors.MISS_REQUIRED_PARAMETERS.exception("缺少参数 redirect_uri", null);
|
||||
}
|
||||
String code = requestParams.get("code")[0];
|
||||
|
||||
String resourceUrl = INSTANCE.getResourceHost(properties);
|
||||
String clientId = INSTANCE.get(properties, WeWorkProperties.APP_ID);
|
||||
String secret = INSTANCE.get(properties, WeWorkProperties.SECRET);
|
||||
String tokenUrl = resourceUrl + "/cgi-bin/gettoken";
|
||||
String token = weWorkRemoteService.getToken(tokenUrl, clientId, secret);
|
||||
|
||||
String userIdUrl = resourceUrl + "/cgi-bin/auth/getuserinfo";
|
||||
String userId = weWorkRemoteService.getUserId(userIdUrl, token, code);
|
||||
|
||||
String userInfoUrl = resourceUrl + "/cgi-bin/user/get";
|
||||
JsonNode userInfo = weWorkRemoteService.getUserInfo(userInfoUrl, token, userId);
|
||||
|
||||
OAuthProcessResult result = new OAuthProcessResult();
|
||||
result.setAvatar(userInfo.get("avatar").asText());
|
||||
result.setEmail(userInfo.get("biz_mail").asText());
|
||||
result.setNickname(userInfo.get("name").asText());
|
||||
result.setUsername(userInfo.get("biz_mail").asText());
|
||||
return result;
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package com.databasir.core.domain.app.validator;
|
||||
|
||||
import com.databasir.core.domain.DomainErrors;
|
||||
import com.databasir.core.domain.app.data.OAuthAppCreateRequest;
|
||||
import com.databasir.core.domain.app.data.OAuthAppPlatformResponse;
|
||||
import com.databasir.core.domain.app.data.OAuthAppPlatformResponse.OAuthAppPlatformProperty;
|
||||
import com.databasir.core.domain.app.data.OAuthAppUpdateRequest;
|
||||
import com.databasir.core.domain.app.data.OauthAppPropertyData;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class OauthPropertiesValidator {
|
||||
|
||||
public void validate(OAuthAppCreateRequest request, List<OAuthAppPlatformResponse> platforms) {
|
||||
Map<String, OauthAppPropertyData> propertyMapByName = request.getProperties()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(OauthAppPropertyData::getName, i -> i));
|
||||
platforms.stream()
|
||||
.filter(platform -> platform.getAuthAppType() == request.getAppType())
|
||||
.forEach(platform -> {
|
||||
List<OAuthAppPlatformProperty> properties = platform.getProperties();
|
||||
properties.forEach(property -> {
|
||||
if (Objects.equals(true, property.getRequired())) {
|
||||
if (!propertyMapByName.containsKey(property.getName())) {
|
||||
throw DomainErrors.MISS_REQUIRED_PARAMETERS.exception(
|
||||
property.getLabel() + " 不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(propertyMapByName.get(property.getName()).getValue())) {
|
||||
throw DomainErrors.MISS_REQUIRED_PARAMETERS.exception(
|
||||
property.getLabel() + " 不能为空");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public void validate(OAuthAppUpdateRequest request, List<OAuthAppPlatformResponse> platforms) {
|
||||
Map<String, OauthAppPropertyData> propertyMapByName = request.getProperties()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(OauthAppPropertyData::getName, i -> i));
|
||||
platforms.stream()
|
||||
.filter(platform -> platform.getAuthAppType() == request.getAppType())
|
||||
.forEach(platform -> {
|
||||
List<OAuthAppPlatformProperty> properties = platform.getProperties();
|
||||
properties.forEach(property -> {
|
||||
if (Objects.equals(true, property.getRequired())) {
|
||||
if (!propertyMapByName.containsKey(property.getName())) {
|
||||
throw DomainErrors.MISS_REQUIRED_PARAMETERS.exception(
|
||||
property.getLabel() + " 不能为空");
|
||||
}
|
||||
if (StringUtils.isBlank(propertyMapByName.get(property.getName()).getValue())) {
|
||||
throw DomainErrors.MISS_REQUIRED_PARAMETERS.exception(
|
||||
property.getLabel() + " 不能为空");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@@ -50,13 +50,17 @@ public class OperationLogService {
|
||||
}
|
||||
|
||||
public void saveLoginFailedLog(String username, String msg) {
|
||||
saveLoginFailedLog(username, "登录", msg);
|
||||
}
|
||||
|
||||
public void saveLoginFailedLog(String username, String name, String msg) {
|
||||
try {
|
||||
JsonData result = JsonData.error("-1", Objects.requireNonNullElse(msg, "登录失败"));
|
||||
OperationLogRequest log = OperationLogRequest.builder()
|
||||
.isSuccess(false)
|
||||
.operationCode("login")
|
||||
.operationModule(AuditLog.Modules.LOGIN)
|
||||
.operationName("登录")
|
||||
.operationName(name)
|
||||
.operatorNickname(username)
|
||||
.operatorUsername(username)
|
||||
.operatorUserId(-1)
|
||||
@@ -70,6 +74,10 @@ public class OperationLogService {
|
||||
}
|
||||
|
||||
public void saveLoginLog(User user, Boolean success, String msg) {
|
||||
this.saveLoginLog(user, success, "登录", msg);
|
||||
}
|
||||
|
||||
public void saveLoginLog(User user, Boolean success, String name, String msg) {
|
||||
try {
|
||||
JsonData result;
|
||||
if (success) {
|
||||
@@ -82,7 +90,7 @@ public class OperationLogService {
|
||||
.involvedUserId(user.getId())
|
||||
.operationCode("login")
|
||||
.operationModule(AuditLog.Modules.LOGIN)
|
||||
.operationName("登录")
|
||||
.operationName(name)
|
||||
.operatorNickname(user.getNickname())
|
||||
.operatorUsername(user.getUsername())
|
||||
.operatorUserId(user.getId())
|
||||
|
@@ -3,6 +3,7 @@ package com.databasir.core.infrastructure.remote;
|
||||
import com.databasir.core.infrastructure.remote.github.GithubApiClient;
|
||||
import com.databasir.core.infrastructure.remote.github.GithubOauthClient;
|
||||
import com.databasir.core.infrastructure.remote.gitlab.GitlabApiClient;
|
||||
import com.databasir.core.infrastructure.remote.wework.WeWorkApiClient;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -41,4 +42,13 @@ public class ClientConfig {
|
||||
.build();
|
||||
return retrofit.create(GitlabApiClient.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WeWorkApiClient weWorkApiClient() {
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl("https://qyapi.weixin.qq.com")
|
||||
.addConverterFactory(JacksonConverterFactory.create())
|
||||
.build();
|
||||
return retrofit.create(WeWorkApiClient.class);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,25 @@
|
||||
package com.databasir.core.infrastructure.remote.wework;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Headers;
|
||||
import retrofit2.http.QueryMap;
|
||||
import retrofit2.http.Url;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface WeWorkApiClient {
|
||||
|
||||
@GET
|
||||
@Headers(value = {
|
||||
"Accept: application/json"
|
||||
})
|
||||
Call<JsonNode> getUserInfo(@Url String url, @QueryMap Map<String, String> request);
|
||||
|
||||
@GET
|
||||
@Headers(value = {
|
||||
"Accept: application/json"
|
||||
})
|
||||
Call<JsonNode> getAccessToken(@Url String url, @QueryMap Map<String, String> request);
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package com.databasir.core.infrastructure.remote.wework;
|
||||
|
||||
import com.databasir.common.SystemException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class WeWorkRemoteService {
|
||||
|
||||
private final WeWorkApiClient weWorkApiClient;
|
||||
|
||||
public String getToken(String url,
|
||||
String corpId,
|
||||
String secret) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("corpid", corpId);
|
||||
map.put("corpsecret", secret);
|
||||
JsonNode data = execute(weWorkApiClient.getAccessToken(url, map));
|
||||
return data.get("access_token").asText();
|
||||
}
|
||||
|
||||
public String getUserId(String url,
|
||||
String accessToken,
|
||||
String code) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("access_token", accessToken);
|
||||
map.put("code", code);
|
||||
return execute(weWorkApiClient.getUserInfo(url, map)).get("userid").asText();
|
||||
}
|
||||
|
||||
public JsonNode getUserInfo(String url,
|
||||
String accessToken,
|
||||
String userId) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("access_token", accessToken);
|
||||
map.put("userid", userId);
|
||||
return execute(weWorkApiClient.getUserInfo(url, map));
|
||||
}
|
||||
|
||||
private <T> T execute(Call<T> call) {
|
||||
try {
|
||||
Response<T> response = call.execute();
|
||||
if (!response.isSuccessful()) {
|
||||
log.error("request error: " + call.request() + ", response = " + response);
|
||||
throw new SystemException("Call Remote Error");
|
||||
} else {
|
||||
log.info("response " + response);
|
||||
T body = response.body();
|
||||
return body;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SystemException("System Error", e);
|
||||
}
|
||||
}
|
||||
}
|
108
core/src/main/resources/oauth/platform-properties-schema.json
Normal file
108
core/src/main/resources/oauth/platform-properties-schema.json
Normal file
@@ -0,0 +1,108 @@
|
||||
[
|
||||
{
|
||||
"authAppType": "GITLAB",
|
||||
"authAppName": "Gitlab",
|
||||
"properties": [
|
||||
{
|
||||
"name": "auth_host",
|
||||
"label": "认证服务地址",
|
||||
"description": "用于登录认证",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "resource_host",
|
||||
"label": "资源服务地址",
|
||||
"description": "用于用户信息等资源查询",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "client_id",
|
||||
"label": "Client ID",
|
||||
"description": "",
|
||||
"required": true,
|
||||
"stage": "AUTHORIZATION"
|
||||
},
|
||||
{
|
||||
"name": "client_secret",
|
||||
"label": "Client Secret",
|
||||
"description": "",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"authAppType": "GITHUB",
|
||||
"authAppName": "Github",
|
||||
"properties": [
|
||||
{
|
||||
"name": "auth_host",
|
||||
"label": "认证服务地址",
|
||||
"description": "用于登录认证",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "resource_host",
|
||||
"label": "资源服务地址",
|
||||
"description": "用于用户信息等资源查询",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "client_id",
|
||||
"label": "Client ID",
|
||||
"description": "",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "client_secret",
|
||||
"label": "Client Secret",
|
||||
"description": "",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"authAppType": "WE_WORK",
|
||||
"authAppName": "企业微信",
|
||||
"properties": [
|
||||
{
|
||||
"name": "auth_host",
|
||||
"label": "认证服务地址",
|
||||
"description": "用于登录认证",
|
||||
"required": true,
|
||||
"defaultValue": "https://open.work.weixin.qq.com"
|
||||
},
|
||||
{
|
||||
"name": "resource_host",
|
||||
"label": "资源服务地址",
|
||||
"description": "用于用户信息等资源查询",
|
||||
"required": true,
|
||||
"defaultValue": "https://qyapi.weixin.qq.com"
|
||||
},
|
||||
{
|
||||
"name": "redirect_uri",
|
||||
"label": "登录成功回跳地址",
|
||||
"description": "",
|
||||
"defaultValue": "javaScript:window.location.protocol + '//' +window.location.host+'/login/oauth2/'+formData.registrationId",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "appid",
|
||||
"label": "企业 ID",
|
||||
"description": "",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "agentid",
|
||||
"label": "应用 agentID",
|
||||
"description": "",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "secret",
|
||||
"label": "应用 secret",
|
||||
"description": "应用密钥",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
Reference in New Issue
Block a user