dubbo-security Jackson error (#11622)
This commit is contained in:
parent
e7925ceac7
commit
638b193c2c
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
securityApplicationScopeModelInitializer = org.apache.dubbo.spring.security.model.SecurityScopeModelInitializer
|
||||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue