Skip to content

Commit 04f3fe8

Browse files
committed
Add Jackson support for oauth2-client session related classes
Fixes gh-4886
1 parent ca5cc13 commit 04f3fe8

28 files changed

+2044
-33
lines changed

core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2015-2019 the original author or authors.
2+
* Copyright 2015-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,6 +61,7 @@
6161
* mapper.registerModule(new WebJackson2Module());
6262
* mapper.registerModule(new WebServletJackson2Module());
6363
* mapper.registerModule(new WebServerJackson2Module());
64+
* mapper.registerModule(new OAuth2ClientJackson2Module());
6465
* </pre>
6566
*
6667
* @author Jitendra Singh.
@@ -77,6 +78,10 @@ public final class SecurityJackson2Modules {
7778
);
7879
private static final String webServletJackson2ModuleClass =
7980
"org.springframework.security.web.jackson2.WebServletJackson2Module";
81+
private static final String oauth2ClientJackson2ModuleClass =
82+
"org.springframework.security.oauth2.client.jackson2.OAuth2ClientJackson2Module";
83+
private static final String javaTimeJackson2ModuleClass =
84+
"com.fasterxml.jackson.datatype.jsr310.JavaTimeModule";
8085

8186
private SecurityJackson2Modules() {
8287
}
@@ -121,6 +126,12 @@ public static List<Module> getModules(ClassLoader loader) {
121126
if (ClassUtils.isPresent("javax.servlet.http.Cookie", loader)) {
122127
addToModulesList(loader, modules, webServletJackson2ModuleClass);
123128
}
129+
if (ClassUtils.isPresent("org.springframework.security.oauth2.client.OAuth2AuthorizedClient", loader)) {
130+
addToModulesList(loader, modules, oauth2ClientJackson2ModuleClass);
131+
}
132+
if (ClassUtils.isPresent(javaTimeJackson2ModuleClass, loader)) {
133+
addToModulesList(loader, modules, javaTimeJackson2ModuleClass);
134+
}
124135
return modules;
125136
}
126137

@@ -188,8 +199,11 @@ static class WhitelistTypeIdResolver implements TypeIdResolver {
188199
"java.util.Collections$UnmodifiableRandomAccessList",
189200
"java.util.Collections$SingletonList",
190201
"java.util.Date",
202+
"java.time.Instant",
203+
"java.net.URL",
191204
"java.util.TreeMap",
192205
"java.util.HashMap",
206+
"java.util.LinkedHashMap",
193207
"org.springframework.security.core.context.SecurityContextImpl"
194208
)));
195209

gradle/dependency-management.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dependencies {
2828
constraints {
2929
management "ch.qos.logback:logback-classic:1.+"
3030
management "com.fasterxml.jackson.core:jackson-databind:2.+"
31+
management 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.+'
3132
management "com.google.appengine:appengine-api-1.0-sdk:$gaeVersion"
3233
management "com.google.appengine:appengine-api-labs:$gaeVersion"
3334
management "com.google.appengine:appengine-api-stubs:$gaeVersion"

oauth2/oauth2-client/spring-security-oauth2-client.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ dependencies {
1010
optional project(':spring-security-oauth2-jose')
1111
optional 'io.projectreactor:reactor-core'
1212
optional 'org.springframework:spring-webflux'
13+
optional 'com.fasterxml.jackson.core:jackson-databind'
14+
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
1315

1416
testCompile project(path: ':spring-security-oauth2-core', configuration: 'tests')
1517
testCompile project(path: ':spring-security-oauth2-jose', configuration: 'tests')
1618
testCompile powerMock2Dependencies
1719
testCompile 'com.squareup.okhttp3:mockwebserver'
18-
testCompile 'com.fasterxml.jackson.core:jackson-databind'
1920
testCompile 'io.projectreactor.netty:reactor-netty'
2021
testCompile 'io.projectreactor:reactor-test'
2122
testCompile 'io.projectreactor.tools:blockhound'
23+
testCompile 'org.skyscreamer:jsonassert'
2224

2325
provided 'javax.servlet:javax.servlet-api'
2426
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client.jackson2;
17+
18+
import com.fasterxml.jackson.core.JsonParser;
19+
import com.fasterxml.jackson.databind.DeserializationContext;
20+
import com.fasterxml.jackson.databind.JsonDeserializer;
21+
import com.fasterxml.jackson.databind.JsonNode;
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
import com.fasterxml.jackson.databind.util.StdConverter;
24+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
25+
import org.springframework.security.oauth2.core.AuthenticationMethod;
26+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
27+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
28+
29+
import java.io.IOException;
30+
31+
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.MAP_TYPE_REFERENCE;
32+
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.SET_TYPE_REFERENCE;
33+
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findObjectNode;
34+
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findStringValue;
35+
import static org.springframework.security.oauth2.client.jackson2.JsonNodeUtils.findValue;
36+
37+
/**
38+
* A {@code JsonDeserializer} for {@link ClientRegistration}.
39+
*
40+
* @author Joe Grandja
41+
* @since 5.3
42+
* @see ClientRegistration
43+
* @see ClientRegistrationMixin
44+
*/
45+
final class ClientRegistrationDeserializer extends JsonDeserializer<ClientRegistration> {
46+
private static final StdConverter<JsonNode, ClientAuthenticationMethod> CLIENT_AUTHENTICATION_METHOD_CONVERTER =
47+
new StdConverters.ClientAuthenticationMethodConverter();
48+
private static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER =
49+
new StdConverters.AuthorizationGrantTypeConverter();
50+
private static final StdConverter<JsonNode, AuthenticationMethod> AUTHENTICATION_METHOD_CONVERTER =
51+
new StdConverters.AuthenticationMethodConverter();
52+
53+
@Override
54+
public ClientRegistration deserialize(JsonParser parser, DeserializationContext context) throws IOException {
55+
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
56+
JsonNode clientRegistrationNode = mapper.readTree(parser);
57+
JsonNode providerDetailsNode = findObjectNode(clientRegistrationNode, "providerDetails");
58+
JsonNode userInfoEndpointNode = findObjectNode(providerDetailsNode, "userInfoEndpoint");
59+
60+
return ClientRegistration
61+
.withRegistrationId(findStringValue(clientRegistrationNode, "registrationId"))
62+
.clientId(findStringValue(clientRegistrationNode, "clientId"))
63+
.clientSecret(findStringValue(clientRegistrationNode, "clientSecret"))
64+
.clientAuthenticationMethod(
65+
CLIENT_AUTHENTICATION_METHOD_CONVERTER.convert(
66+
findObjectNode(clientRegistrationNode, "clientAuthenticationMethod")))
67+
.authorizationGrantType(
68+
AUTHORIZATION_GRANT_TYPE_CONVERTER.convert(
69+
findObjectNode(clientRegistrationNode, "authorizationGrantType")))
70+
.redirectUriTemplate(findStringValue(clientRegistrationNode, "redirectUriTemplate"))
71+
.scope(findValue(clientRegistrationNode, "scopes", SET_TYPE_REFERENCE, mapper))
72+
.clientName(findStringValue(clientRegistrationNode, "clientName"))
73+
.authorizationUri(findStringValue(providerDetailsNode, "authorizationUri"))
74+
.tokenUri(findStringValue(providerDetailsNode, "tokenUri"))
75+
.userInfoUri(findStringValue(userInfoEndpointNode, "uri"))
76+
.userInfoAuthenticationMethod(
77+
AUTHENTICATION_METHOD_CONVERTER.convert(
78+
findObjectNode(userInfoEndpointNode, "authenticationMethod")))
79+
.userNameAttributeName(findStringValue(userInfoEndpointNode, "userNameAttributeName"))
80+
.jwkSetUri(findStringValue(providerDetailsNode, "jwkSetUri"))
81+
.providerConfigurationMetadata(findValue(providerDetailsNode, "configurationMetadata", MAP_TYPE_REFERENCE, mapper))
82+
.build();
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client.jackson2;
17+
18+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
19+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
20+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
21+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
22+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
23+
24+
/**
25+
* This mixin class is used to serialize/deserialize {@link ClientRegistration}.
26+
* It also registers a custom deserializer {@link ClientRegistrationDeserializer}.
27+
*
28+
* @author Joe Grandja
29+
* @since 5.3
30+
* @see ClientRegistration
31+
* @see ClientRegistrationDeserializer
32+
* @see OAuth2ClientJackson2Module
33+
*/
34+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
35+
@JsonDeserialize(using = ClientRegistrationDeserializer.class)
36+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
37+
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
38+
@JsonIgnoreProperties(ignoreUnknown = true)
39+
abstract class ClientRegistrationMixin {
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client.jackson2;
17+
18+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
19+
import com.fasterxml.jackson.annotation.JsonCreator;
20+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
21+
import com.fasterxml.jackson.annotation.JsonProperty;
22+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
23+
import org.springframework.security.core.GrantedAuthority;
24+
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
25+
26+
import java.util.Collection;
27+
import java.util.Map;
28+
29+
/**
30+
* This mixin class is used to serialize/deserialize {@link DefaultOAuth2User}.
31+
*
32+
* @author Joe Grandja
33+
* @since 5.3
34+
* @see DefaultOAuth2User
35+
* @see OAuth2ClientJackson2Module
36+
*/
37+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
38+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
39+
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
40+
@JsonIgnoreProperties(ignoreUnknown = true)
41+
abstract class DefaultOAuth2UserMixin {
42+
43+
@JsonCreator
44+
DefaultOAuth2UserMixin(
45+
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
46+
@JsonProperty("attributes") Map<String, Object> attributes,
47+
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client.jackson2;
17+
18+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
19+
import com.fasterxml.jackson.annotation.JsonCreator;
20+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
21+
import com.fasterxml.jackson.annotation.JsonProperty;
22+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
23+
import org.springframework.security.core.GrantedAuthority;
24+
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
25+
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
26+
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
27+
28+
import java.util.Collection;
29+
30+
/**
31+
* This mixin class is used to serialize/deserialize {@link DefaultOidcUser}.
32+
*
33+
* @author Joe Grandja
34+
* @since 5.3
35+
* @see DefaultOidcUser
36+
* @see OAuth2ClientJackson2Module
37+
*/
38+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
39+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
40+
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
41+
@JsonIgnoreProperties(value = {"attributes"}, ignoreUnknown = true)
42+
abstract class DefaultOidcUserMixin {
43+
44+
@JsonCreator
45+
DefaultOidcUserMixin(
46+
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
47+
@JsonProperty("idToken") OidcIdToken idToken,
48+
@JsonProperty("userInfo") OidcUserInfo userInfo,
49+
@JsonProperty("nameAttributeKey") String nameAttributeKey) {
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.security.oauth2.client.jackson2;
17+
18+
import com.fasterxml.jackson.core.type.TypeReference;
19+
import com.fasterxml.jackson.databind.JsonNode;
20+
import com.fasterxml.jackson.databind.ObjectMapper;
21+
22+
import java.util.Map;
23+
import java.util.Set;
24+
25+
/**
26+
* Utility class for {@code JsonNode}.
27+
*
28+
* @author Joe Grandja
29+
* @since 5.3
30+
*/
31+
abstract class JsonNodeUtils {
32+
static final TypeReference<Set<String>> SET_TYPE_REFERENCE = new TypeReference<Set<String>>() {};
33+
static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<Map<String, Object>>() {};
34+
35+
36+
static String findStringValue(JsonNode jsonNode, String fieldName) {
37+
if (jsonNode == null) {
38+
return null;
39+
}
40+
JsonNode nodeValue = jsonNode.findValue(fieldName);
41+
if (nodeValue != null && nodeValue.isTextual()) {
42+
return nodeValue.asText();
43+
}
44+
return null;
45+
}
46+
47+
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference, ObjectMapper mapper) {
48+
if (jsonNode == null) {
49+
return null;
50+
}
51+
JsonNode nodeValue = jsonNode.findValue(fieldName);
52+
if (nodeValue != null && nodeValue.isContainerNode()) {
53+
return (T) mapper.convertValue(nodeValue, valueTypeReference);
54+
}
55+
return null;
56+
}
57+
58+
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
59+
if (jsonNode == null) {
60+
return null;
61+
}
62+
JsonNode nodeValue = jsonNode.findValue(fieldName);
63+
if (nodeValue != null && nodeValue.isObject()) {
64+
return nodeValue;
65+
}
66+
return null;
67+
}
68+
}

0 commit comments

Comments
 (0)