fix: normalize channel login open ids
This commit is contained in:
parent
2a7be2ca92
commit
916ce628be
@ -224,10 +224,19 @@ public class EndpointRestController {
|
||||
*/
|
||||
@PostMapping("/account/login/channel")
|
||||
public TokenCredentialCO login(@RequestBody @Validated UserChannelCredentialCmd cmd, HttpServletRequest request) {
|
||||
TokenCredentialCO tokenCredentialCO = processToken.createUserCredential(ResponseAssert.requiredSuccess(
|
||||
appUserAccountClient.channelCredential(cmd)));
|
||||
return submitLoginAccessValidation(tokenCredentialCO, cmd.requireReqSysOrigin(), cmd.getReqZoneId(), request);
|
||||
}
|
||||
try {
|
||||
TokenCredentialCO tokenCredentialCO = processToken.createUserCredential(ResponseAssert.requiredSuccess(
|
||||
appUserAccountClient.channelCredential(cmd)));
|
||||
return submitLoginAccessValidation(tokenCredentialCO, cmd.requireReqSysOrigin(), cmd.getReqZoneId(), request);
|
||||
} catch (ResponseException ex) {
|
||||
if (isChannelCredentialDecodeError(ex)) {
|
||||
log.warn("channel login credential decode error, return USER_NOT_REGISTERED. authType={}",
|
||||
cmd.getAuthType());
|
||||
throw new ResponseException(UserErrorCode.USER_NOT_REGISTERED);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 账号登录.
|
||||
@ -287,6 +296,13 @@ public class EndpointRestController {
|
||||
|| Objects.equals(ex.getErrorCode(), UserErrorCode.SOURCE_REGISTERED.getCode());
|
||||
}
|
||||
|
||||
private boolean isChannelCredentialDecodeError(ResponseException ex) {
|
||||
return Objects.equals(ex.getErrorCode(), ResponseErrorCode.NOT_ACCEPTABLE.getCode())
|
||||
&& Objects.equals(ex.getErrorCodeName(), "ERROR_INNER_DECODER")
|
||||
&& Objects.toString(ex.getExtValues(), "")
|
||||
.contains("/app-user/account/channel/credential");
|
||||
}
|
||||
|
||||
private TokenCredentialCO createTokenCredential(
|
||||
UserAccountDTO account,
|
||||
String reqSysOrigin,
|
||||
|
||||
@ -12,6 +12,7 @@ import com.red.circle.tool.core.text.StringUtils;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
@ -69,11 +70,14 @@ public class FeignErrorDecoder implements ErrorDecoder {
|
||||
String errorCodeName = Objects.equals(response.status(), 429)
|
||||
? "REQ_LIMIT_INNER_DECODER"
|
||||
: "ERROR_INNER_DECODER";
|
||||
return new ResponseException(response.status(), errorCodeName, getErrorMsg(methodKey, response));
|
||||
Map<String, Object> extValues = new HashMap<>();
|
||||
extValues.put("innerRequest", reqMapping(response));
|
||||
extValues.put("innerMethodKey", methodKey);
|
||||
return new ResponseException(response.status(), errorCodeName, getErrorMsg(response), extValues);
|
||||
}
|
||||
|
||||
private String getErrorMsg(String methodKey, Response response) {
|
||||
return StringUtils.isBlankOrElse(response.reason(), "") + reqMapping(response);
|
||||
private String getErrorMsg(Response response) {
|
||||
return StringUtils.isBlankOrElse(response.reason(), "Inner service error");
|
||||
}
|
||||
|
||||
private ResponseException parseResponseException(String methodKey, Response response) {
|
||||
@ -103,15 +107,44 @@ public class FeignErrorDecoder implements ErrorDecoder {
|
||||
}
|
||||
|
||||
try (InputStream inputStream = response.body().asInputStream()) {
|
||||
String responseCompression = ApplicationContextUtils.getApplicationContext()
|
||||
.getEnvironment()
|
||||
.getProperty("spring.cloud.openfeign.compression.response.enabled");
|
||||
return JacksonUtils.readValue(inputStream, new TypeReference<>() {
|
||||
}, Objects.equals(responseCompression, "true"));
|
||||
byte[] bodyBytes = inputStream.readAllBytes();
|
||||
if (bodyBytes.length == 0) {
|
||||
log.warn("Feign error response body is empty, status={}, reason={}, request={}",
|
||||
response.status(), response.reason(), reqMapping(response));
|
||||
return null;
|
||||
}
|
||||
boolean gzipEncoded = isGzipEncoded(response);
|
||||
ResultResponse<Void> responseBody = parseResponseBytes(response, bodyBytes, gzipEncoded);
|
||||
if (Objects.nonNull(responseBody)) {
|
||||
return responseBody;
|
||||
}
|
||||
return parseResponseBytes(response, bodyBytes, !gzipEncoded);
|
||||
} catch (IOException | RuntimeException ex) {
|
||||
log.warn("Parse feign error response body failed, status={}, reason={}, request={}",
|
||||
log.warn("Read feign error response body failed, status={}, reason={}, request={}",
|
||||
response.status(), response.reason(), reqMapping(response), ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ResultResponse<Void> parseResponseBytes(
|
||||
Response response,
|
||||
byte[] bodyBytes,
|
||||
boolean compression) {
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bodyBytes)) {
|
||||
return JacksonUtils.readValue(inputStream, new TypeReference<>() {
|
||||
}, compression);
|
||||
} catch (IOException | RuntimeException ex) {
|
||||
log.warn("Parse feign error response body failed, status={}, reason={}, request={}, compression={}",
|
||||
response.status(), response.reason(), reqMapping(response), compression, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGzipEncoded(Response response) {
|
||||
return response.headers().entrySet().stream()
|
||||
.filter(entry -> Objects.nonNull(entry.getKey()))
|
||||
.filter(entry -> "Content-Encoding".equalsIgnoreCase(entry.getKey()))
|
||||
.flatMap(entry -> entry.getValue().stream())
|
||||
.anyMatch(value -> "gzip".equalsIgnoreCase(value));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
package com.red.circle.other.inner.model.cmd.user;
|
||||
|
||||
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||
import com.red.circle.other.inner.enums.user.AuthTypeEnum;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import com.red.circle.common.business.dto.cmd.AppExtCommand;
|
||||
import com.red.circle.other.inner.enums.user.AuthTypeEnum;
|
||||
import com.red.circle.tool.core.text.StringUtils;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 用户认证检测.
|
||||
@ -31,12 +32,16 @@ public class UserChannelCredentialCmd extends AppExtCommand {
|
||||
*/
|
||||
@NotBlank(message = "openId required.")
|
||||
private String openId;
|
||||
|
||||
public String openId() {
|
||||
if (openId.contains("_") && !AuthTypeEnum.HUAWEI.name().equals(authType.name())) {
|
||||
return openId;
|
||||
}
|
||||
return this.getOpenId().concat("_").concat(this.requireReqSysOrigin());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String openId() {
|
||||
if (StringUtils.isBlank(openId)) {
|
||||
return openId;
|
||||
}
|
||||
String suffix = "_".concat(requireReqSysOrigin());
|
||||
if (openId.endsWith(suffix)) {
|
||||
return openId;
|
||||
}
|
||||
return openId.concat(suffix);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -99,17 +99,25 @@ public class CreateAccountCmd extends AppExtCommand {
|
||||
&& Objects.equals(getMobile().getPhonePrefix(), 86);
|
||||
}
|
||||
|
||||
public void changeOpenId() {
|
||||
if (!this.checkTypeMobile()) {
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
StringUtils.isNotBlank(this.openId)
|
||||
&& !Objects.equals(this.openId, "null"));
|
||||
}
|
||||
setOpenId(this.openId.concat("_").concat(requireReqSysOrigin()));
|
||||
}
|
||||
|
||||
public String registerOpenId() {
|
||||
return checkTypeMobile() ? this.mobile.getFullPhoneNumber() : this.openId;
|
||||
}
|
||||
|
||||
}
|
||||
public void changeOpenId() {
|
||||
if (!this.checkTypeMobile()) {
|
||||
ResponseAssert.isTrue(ResponseErrorCode.REQUEST_PARAMETER_ERROR,
|
||||
StringUtils.isNotBlank(this.openId)
|
||||
&& !Objects.equals(this.openId, "null"));
|
||||
}
|
||||
setOpenId(openIdWithReqSysOrigin(this.openId));
|
||||
}
|
||||
|
||||
public String registerOpenId() {
|
||||
return checkTypeMobile() ? this.mobile.getFullPhoneNumber() : this.openId;
|
||||
}
|
||||
|
||||
private String openIdWithReqSysOrigin(String sourceOpenId) {
|
||||
String suffix = "_".concat(requireReqSysOrigin());
|
||||
if (sourceOpenId.endsWith(suffix)) {
|
||||
return sourceOpenId;
|
||||
}
|
||||
return sourceOpenId.concat(suffix);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
package com.red.circle.other.inner.model.cmd.user;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import com.red.circle.framework.core.dto.ReqSysOrigin;
|
||||
import com.red.circle.other.inner.enums.user.AuthTypeEnum;
|
||||
import com.red.circle.other.inner.model.cmd.user.account.CreateAccountCmd;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UserOpenIdNormalizeTest {
|
||||
|
||||
@Test
|
||||
public void channelLoginAppendsOriginWhenGoogleOpenIdContainsUnderscore() {
|
||||
UserChannelCredentialCmd cmd = new UserChannelCredentialCmd();
|
||||
cmd.setAuthType(AuthTypeEnum.GOOGLE);
|
||||
cmd.setOpenId("google_open_id");
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
|
||||
|
||||
assertEquals("google_open_id_LIKEI", cmd.openId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void channelLoginDoesNotAppendOriginTwice() {
|
||||
UserChannelCredentialCmd cmd = new UserChannelCredentialCmd();
|
||||
cmd.setAuthType(AuthTypeEnum.GOOGLE);
|
||||
cmd.setOpenId("google_open_id_LIKEI");
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
|
||||
|
||||
assertEquals("google_open_id_LIKEI", cmd.openId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountCreateDoesNotAppendOriginTwice() {
|
||||
CreateAccountCmd cmd = new CreateAccountCmd();
|
||||
cmd.setType(AuthTypeEnum.GOOGLE.name());
|
||||
cmd.setOpenId("google_open_id_LIKEI");
|
||||
cmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
|
||||
cmd.changeOpenId();
|
||||
|
||||
assertEquals("google_open_id_LIKEI", cmd.getOpenId());
|
||||
}
|
||||
}
|
||||
@ -40,15 +40,23 @@ public class BindUserAuthTypeCmd extends AppExtCommand {
|
||||
return Objects.equals(type, AuthTypeEnum.MOBILE);
|
||||
}
|
||||
|
||||
public String getActualOpenId() {
|
||||
|
||||
if (!isTypeMobile() && StringUtils.isBlank(openId)) {
|
||||
return "";
|
||||
public String getActualOpenId() {
|
||||
|
||||
if (!isTypeMobile() && StringUtils.isBlank(openId)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return isTypeMobile()
|
||||
? mobile.getFullPhoneNumber()
|
||||
: openId.concat("_").concat(requireReqSysOrigin());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return isTypeMobile()
|
||||
? mobile.getFullPhoneNumber()
|
||||
: openIdWithReqSysOrigin();
|
||||
}
|
||||
|
||||
private String openIdWithReqSysOrigin() {
|
||||
String suffix = "_".concat(requireReqSysOrigin());
|
||||
if (openId.endsWith(suffix)) {
|
||||
return openId;
|
||||
}
|
||||
return openId.concat(suffix);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import com.red.circle.tool.core.text.StringUtils;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
@ -69,11 +70,14 @@ public class FeignErrorDecoder implements ErrorDecoder {
|
||||
String errorCodeName = Objects.equals(response.status(), 429)
|
||||
? "REQ_LIMIT_INNER_DECODER"
|
||||
: "ERROR_INNER_DECODER";
|
||||
return new ResponseException(response.status(), errorCodeName, getErrorMsg(methodKey, response));
|
||||
Map<String, Object> extValues = new HashMap<>();
|
||||
extValues.put("innerRequest", reqMapping(response));
|
||||
extValues.put("innerMethodKey", methodKey);
|
||||
return new ResponseException(response.status(), errorCodeName, getErrorMsg(response), extValues);
|
||||
}
|
||||
|
||||
private String getErrorMsg(String methodKey, Response response) {
|
||||
return StringUtils.isBlankOrElse(response.reason(), "") + reqMapping(response);
|
||||
private String getErrorMsg(Response response) {
|
||||
return StringUtils.isBlankOrElse(response.reason(), "Inner service error");
|
||||
}
|
||||
|
||||
private ResponseException parseResponseException(String methodKey, Response response) {
|
||||
@ -103,15 +107,44 @@ public class FeignErrorDecoder implements ErrorDecoder {
|
||||
}
|
||||
|
||||
try (InputStream inputStream = response.body().asInputStream()) {
|
||||
String responseCompression = ApplicationContextUtils.getApplicationContext()
|
||||
.getEnvironment()
|
||||
.getProperty("spring.cloud.openfeign.compression.response.enabled");
|
||||
return JacksonUtils.readValue(inputStream, new TypeReference<>() {
|
||||
}, Objects.equals(responseCompression, "true"));
|
||||
byte[] bodyBytes = inputStream.readAllBytes();
|
||||
if (bodyBytes.length == 0) {
|
||||
log.warn("Feign error response body is empty, status={}, reason={}, request={}",
|
||||
response.status(), response.reason(), reqMapping(response));
|
||||
return null;
|
||||
}
|
||||
boolean gzipEncoded = isGzipEncoded(response);
|
||||
ResultResponse<Void> responseBody = parseResponseBytes(response, bodyBytes, gzipEncoded);
|
||||
if (Objects.nonNull(responseBody)) {
|
||||
return responseBody;
|
||||
}
|
||||
return parseResponseBytes(response, bodyBytes, !gzipEncoded);
|
||||
} catch (IOException | RuntimeException ex) {
|
||||
log.warn("Parse feign error response body failed, status={}, reason={}, request={}",
|
||||
log.warn("Read feign error response body failed, status={}, reason={}, request={}",
|
||||
response.status(), response.reason(), reqMapping(response), ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ResultResponse<Void> parseResponseBytes(
|
||||
Response response,
|
||||
byte[] bodyBytes,
|
||||
boolean compression) {
|
||||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bodyBytes)) {
|
||||
return JacksonUtils.readValue(inputStream, new TypeReference<>() {
|
||||
}, compression);
|
||||
} catch (IOException | RuntimeException ex) {
|
||||
log.warn("Parse feign error response body failed, status={}, reason={}, request={}, compression={}",
|
||||
response.status(), response.reason(), reqMapping(response), compression, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGzipEncoded(Response response) {
|
||||
return response.headers().entrySet().stream()
|
||||
.filter(entry -> Objects.nonNull(entry.getKey()))
|
||||
.filter(entry -> "Content-Encoding".equalsIgnoreCase(entry.getKey()))
|
||||
.flatMap(entry -> entry.getValue().stream())
|
||||
.anyMatch(value -> "gzip".equalsIgnoreCase(value));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user