dubbo-security Jackson error (#11622)

This commit is contained in:
jojocodeX 2023-02-23 15:07:38 +08:00 committed by GitHub
parent e7925ceac7
commit 638b193c2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 314 additions and 74 deletions

View File

@ -1256,6 +1256,14 @@
META-INF/dubbo/internal/org.apache.dubbo.metrics.collector.MetricsCollector
</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>
META-INF/dubbo/internal/org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer
</resource>
</transformer>
</transformers>
<filters>
<filter>

View File

@ -57,6 +57,14 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<optional>true</optional>
<scope>test</scope>
</dependency>
<!-- spring security -->
<!-- jackson -->
@ -65,6 +73,11 @@
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId> com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>

View File

@ -26,7 +26,7 @@ import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import static org.apache.dubbo.rpc.RpcException.FORBIDDEN_EXCEPTION;
import static org.apache.dubbo.rpc.RpcException.AUTHORIZATION_EXCEPTION;
import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME;
@Activate(group = CommonConstants.PROVIDER, order =Integer.MAX_VALUE,onClass = SECURITY_CONTEXT_HOLDER_CLASS_NAME)
@ -43,7 +43,7 @@ public class AuthenticationExceptionTranslatorFilter implements Filter, Filter.L
if (this.isTranslate(result)) {
RpcException rpcException = new RpcException(result.getException().getMessage());
rpcException.setCode(FORBIDDEN_EXCEPTION);
rpcException.setCode(AUTHORIZATION_EXCEPTION);
result.setException(rpcException);
}

View File

@ -23,7 +23,8 @@ import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.filter.ClusterFilter;
import org.apache.dubbo.spring.security.utils.ObjectMapperCodec;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec;
import org.apache.dubbo.spring.security.utils.SecurityNames;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
@ -31,9 +32,13 @@ import org.springframework.security.core.context.SecurityContextHolder;
import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME;
@Activate(group = CommonConstants.CONSUMER, order = -10000,onClass = SECURITY_CONTEXT_HOLDER_CLASS_NAME)
public class ContextHolderAuthenticationPrepareFilter implements ClusterFilter {
public class ContextHolderAuthenticationPrepareFilter implements ClusterFilter{
private ObjectMapperCodec mapper = new ObjectMapperCodec();
private final ObjectMapperCodec mapper;
public ContextHolderAuthenticationPrepareFilter(ApplicationModel applicationModel) {
this.mapper = applicationModel.getBeanFactory().getBean(ObjectMapperCodec.class);
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

View File

@ -24,17 +24,21 @@ import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.spring.security.utils.ObjectMapperCodec;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec;
import org.apache.dubbo.spring.security.utils.SecurityNames;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME;
@Activate(group = CommonConstants.PROVIDER, order = -10000,onClass = SECURITY_CONTEXT_HOLDER_CLASS_NAME)
public class ContextHolderAuthenticationResolverFilter implements Filter {
private ObjectMapperCodec mapper = new ObjectMapperCodec();
private final ObjectMapperCodec mapper;
public ContextHolderAuthenticationResolverFilter(ApplicationModel applicationModel) {
this.mapper = applicationModel.getBeanFactory().getBean(ObjectMapperCodec.class);
}
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {

View File

@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.security.jackson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.springframework.security.jackson2.CoreJackson2Module;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class ObjectMapperCodec {
private ObjectMapper mapper = new ObjectMapper();
public ObjectMapperCodec() {
registerDefaultModule();
}
public <T> T deserialize(byte[] bytes, Class<T> clazz) {
try {
if (bytes == null || bytes.length == 0) {
return null;
}
return mapper.readValue(bytes, clazz);
} catch (Exception exception) {
throw new RuntimeException(
String.format("objectMapper! deserialize error %s", exception));
}
}
public <T> T deserialize(String content, Class<T> clazz) {
if (StringUtils.isBlank(content)) {
return null;
}
return deserialize(content.getBytes(), clazz);
}
public String serialize(Object object) {
try {
if (object == null) {
return null;
}
return mapper.writeValueAsString(object);
} catch (Exception ex) {
throw new RuntimeException(String.format("objectMapper! serialize error %s", ex));
}
}
public ObjectMapperCodec addModule(SimpleModule simpleModule) {
mapper.registerModule(simpleModule);
return this;
}
public ObjectMapperCodec configureMapper(Consumer<ObjectMapper> objectMapperConfigure) {
objectMapperConfigure.accept(this.mapper);
return this;
}
private void registerDefaultModule() {
mapper.registerModule(new CoreJackson2Module());
mapper.registerModule(new JavaTimeModule());
List<String> jacksonModuleClassNameList = new ArrayList<>();
jacksonModuleClassNameList.add("org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module");
jacksonModuleClassNameList.add("org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module");
jacksonModuleClassNameList.add("org.springframework.security.web.server.jackson2.WebServerJackson2Module");
jacksonModuleClassNameList.add("com.fasterxml.jackson.module.paramnames.ParameterNamesModule");
jacksonModuleClassNameList.add("org.springframework.security.web.jackson2.WebServletJackson2Module");
jacksonModuleClassNameList.add("org.springframework.security.web.jackson2.WebJackson2Module");
jacksonModuleClassNameList.add("org.springframework.boot.jackson.JsonMixinModule");
jacksonModuleClassNameList.add("org.springframework.security.ldap.jackson2.LdapJackson2Module");
loadModuleIfPresent(jacksonModuleClassNameList);
}
private void loadModuleIfPresent(List<String> jacksonModuleClassNameList) {
for (String moduleClassName : jacksonModuleClassNameList) {
try {
SimpleModule objectMapperModule = (SimpleModule) ClassUtils.forName(moduleClassName,
ObjectMapperCodec.class.getClassLoader()).newInstance();
mapper.registerModule(objectMapperModule);
} catch (Throwable ex) {
}
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.security.jackson;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
@SPI(scope = ExtensionScope.FRAMEWORK)
public interface ObjectMapperCodecCustomer {
void customize(ObjectMapperCodec objectMapperCodec);
}

View File

@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.security.model;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ModuleModel;
import org.apache.dubbo.rpc.model.ScopeModelInitializer;
import org.apache.dubbo.spring.security.jackson.ObjectMapperCodec;
import org.apache.dubbo.spring.security.jackson.ObjectMapperCodecCustomer;
import java.util.Set;
import static org.apache.dubbo.spring.security.utils.SecurityNames.SECURITY_CONTEXT_HOLDER_CLASS_NAME;
@Activate(onClass = SECURITY_CONTEXT_HOLDER_CLASS_NAME)
public class SecurityScopeModelInitializer implements ScopeModelInitializer {
@Override
public void initializeFrameworkModel(FrameworkModel frameworkModel) {
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
ObjectMapperCodec objectMapperCodec = beanFactory.getOrRegisterBean(ObjectMapperCodec.class);
Set<ObjectMapperCodecCustomer> objectMapperCodecCustomerList = frameworkModel.getExtensionLoader(ObjectMapperCodecCustomer.class).getSupportedExtensionInstances();
for (ObjectMapperCodecCustomer objectMapperCodecCustomer : objectMapperCodecCustomerList) {
objectMapperCodecCustomer.customize(objectMapperCodec);
}
}
@Override
public void initializeApplicationModel(ApplicationModel applicationModel) {
}
@Override
public void initializeModuleModel(ModuleModel moduleModel) {
}
}

View File

@ -1,66 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.security.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.dubbo.common.utils.StringUtils;
import org.springframework.security.jackson2.CoreJackson2Module;
public class ObjectMapperCodec {
private ObjectMapper mapper = new ObjectMapper();
public ObjectMapperCodec(){
mapper.registerModule(new CoreJackson2Module());
}
public <T> T deserialize(byte [] bytes,Class<T> clazz) {
try {
if (bytes == null || bytes.length == 0) {
return null;
}
return mapper.readValue(bytes, clazz);
} catch (Exception exception) {
throw new RuntimeException(
String.format("objectMapper! deserialize error %s", exception));
}
}
public <T> T deserialize(String content,Class<T> clazz) {
if (StringUtils.isBlank(content)) {
return null;
}
return deserialize(content.getBytes(), clazz);
}
public String serialize(Object object) {
try {
if (object == null) {
return null;
}
return mapper.writeValueAsString(object);
} catch (Exception ex) {
throw new RuntimeException(String.format("objectMapper! serialize error %s", ex));
}
}
}

View File

@ -0,0 +1 @@
securityApplicationScopeModelInitializer = org.apache.dubbo.spring.security.model.SecurityScopeModelInitializer

View File

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.spring.security.jackson;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import java.time.Duration;
import java.time.Instant;
public class ObjectMapperCodecTest {
private ObjectMapperCodec mapper = new ObjectMapperCodec();
@Test
public void testOAuth2AuthorizedClientCodec() {
ClientRegistration clientRegistration = clientRegistration().build();
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, "principal-name", noScopes());
String content = mapper.serialize(authorizedClient);
OAuth2AuthorizedClient deserialize = mapper.deserialize(content.getBytes(),
OAuth2AuthorizedClient.class);
Assertions.assertNotNull(deserialize);
}
public static ClientRegistration.Builder clientRegistration() {
// @formatter:off
return ClientRegistration.withRegistrationId("registration-id")
.redirectUri("http://localhost/uua/oauth2/code/{registrationId}")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.scope("read:user")
.authorizationUri("https://example.com/login/oauth/authorize")
.tokenUri("https://example.com/login/oauth/access_token")
.jwkSetUri("https://example.com/oauth2/jwk")
.issuerUri("https://example.com")
.userInfoUri("https://api.example.com/user")
.userNameAttributeName("id")
.clientName("Client Name")
.clientId("client-id")
.clientSecret("client-secret");
// @formatter:on
}
public static OAuth2AccessToken noScopes() {
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "no-scopes", Instant.now(),
Instant.now().plus(Duration.ofDays(1)));
}
}

View File

@ -40,6 +40,7 @@ public class RpcException extends RuntimeException {
public static final int ROUTER_CACHE_NOT_BUILD = 10;
public static final int METHOD_NOT_FOUND = 11;
public static final int VALIDATION_EXCEPTION = 12;
public static final int AUTHORIZATION_EXCEPTION = 13;
private static final long serialVersionUID = 7815426752583648734L;
/**
* RpcException cannot be extended, use error code for exception type to keep compatibility
@ -110,6 +111,10 @@ public class RpcException extends RuntimeException {
return code == SERIALIZATION_EXCEPTION;
}
public boolean isAuthorization(){
return code == AUTHORIZATION_EXCEPTION;
}
public boolean isNoInvokerAvailableAfterFilter() {
return code == NO_INVOKER_AVAILABLE_AFTER_FILTER;
}