Skip to content

Commit 232b3b7

Browse files
Steve Riesenbergsjohnr
Steve Riesenberg
authored andcommitted
Add jackson module for authorization server
Fixes problems with serialization of complex attribute values of various framework types such as OAuth2AuthorizationRequest and OAuth2ClientAuthenticationToken. Closes gh-324 Closes gh-328
1 parent 67e62a2 commit 232b3b7

12 files changed

+523
-1
lines changed

oauth2-authorization-server/spring-security-oauth2-authorization-server.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
compile 'com.nimbusds:nimbus-jose-jwt'
1111
compile 'com.fasterxml.jackson.core:jackson-databind'
1212

13+
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
1314
optional 'org.springframework:spring-jdbc'
1415

1516
testCompile 'org.springframework.security:spring-security-test'

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java

+15
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Set;
3030
import java.util.function.Function;
3131

32+
import com.fasterxml.jackson.databind.Module;
3233
import com.fasterxml.jackson.databind.ObjectMapper;
3334

3435
import org.springframework.dao.DataRetrievalFailureException;
@@ -41,6 +42,7 @@
4142
import org.springframework.jdbc.support.lob.LobCreator;
4243
import org.springframework.jdbc.support.lob.LobHandler;
4344
import org.springframework.lang.Nullable;
45+
import org.springframework.security.jackson2.SecurityJackson2Modules;
4446
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
4547
import org.springframework.security.oauth2.core.AuthorizationGrantType;
4648
import org.springframework.security.oauth2.core.OAuth2AccessToken;
@@ -51,6 +53,7 @@
5153
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
5254
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
5355
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
56+
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2ServerJackson2Module;
5457
import org.springframework.util.Assert;
5558
import org.springframework.util.CollectionUtils;
5659
import org.springframework.util.StringUtils;
@@ -312,6 +315,11 @@ public static class OAuth2AuthorizationRowMapper implements RowMapper<OAuth2Auth
312315
public OAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {
313316
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
314317
this.registeredClientRepository = registeredClientRepository;
318+
319+
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
320+
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
321+
this.objectMapper.registerModules(securityModules);
322+
this.objectMapper.registerModule(new OAuth2ServerJackson2Module());
315323
}
316324

317325
@Override
@@ -446,6 +454,13 @@ private Map<String, Object> parseMap(String data) {
446454
public static class OAuth2AuthorizationParametersMapper implements Function<OAuth2Authorization, List<SqlParameterValue>> {
447455
private ObjectMapper objectMapper = new ObjectMapper();
448456

457+
public OAuth2AuthorizationParametersMapper() {
458+
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
459+
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
460+
this.objectMapper.registerModules(securityModules);
461+
this.objectMapper.registerModule(new OAuth2ServerJackson2Module());
462+
}
463+
449464
@Override
450465
public List<SqlParameterValue> apply(OAuth2Authorization authorization) {
451466
List<SqlParameterValue> parameters = new ArrayList<>();
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+
17+
package org.springframework.security.oauth2.server.authorization.jackson2;
18+
19+
import java.util.HashSet;
20+
import java.util.Set;
21+
22+
import com.fasterxml.jackson.annotation.JsonCreator;
23+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
24+
25+
/**
26+
* This mixin class is used to serialize/deserialize {@link HashSet}.
27+
*
28+
* @author Steve Riesenberg
29+
* @see HashSet
30+
* @see OAuth2ServerJackson2Module
31+
* @since 0.1.2
32+
*/
33+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
34+
abstract class HashSetMixin {
35+
36+
@JsonCreator
37+
HashSetMixin(Set<?> set) {
38+
}
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
17+
package org.springframework.security.oauth2.server.authorization.jackson2;
18+
19+
import java.util.Map;
20+
import java.util.Set;
21+
22+
import com.fasterxml.jackson.core.type.TypeReference;
23+
import com.fasterxml.jackson.databind.JsonNode;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
25+
26+
/**
27+
* Utility class for {@code JsonNode}.
28+
*
29+
* @author Joe Grandja
30+
* @since 0.1.2
31+
*/
32+
abstract class JsonNodeUtils {
33+
34+
static final TypeReference<Set<String>> STRING_SET = new TypeReference<Set<String>>() {
35+
};
36+
37+
static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<Map<String, Object>>() {
38+
};
39+
40+
static String findStringValue(JsonNode jsonNode, String fieldName) {
41+
if (jsonNode == null) {
42+
return null;
43+
}
44+
JsonNode value = jsonNode.findValue(fieldName);
45+
return (value != null && value.isTextual()) ? value.asText() : null;
46+
}
47+
48+
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
49+
ObjectMapper mapper) {
50+
if (jsonNode == null) {
51+
return null;
52+
}
53+
JsonNode value = jsonNode.findValue(fieldName);
54+
return (value != null && value.isContainerNode()) ? mapper.convertValue(value, valueTypeReference) : null;
55+
}
56+
57+
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
58+
if (jsonNode == null) {
59+
return null;
60+
}
61+
JsonNode value = jsonNode.findValue(fieldName);
62+
return (value != null && value.isObject()) ? value : null;
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
17+
package org.springframework.security.oauth2.server.authorization.jackson2;
18+
19+
import java.io.IOException;
20+
21+
import com.fasterxml.jackson.core.JsonParseException;
22+
import com.fasterxml.jackson.core.JsonParser;
23+
import com.fasterxml.jackson.databind.DeserializationContext;
24+
import com.fasterxml.jackson.databind.JsonDeserializer;
25+
import com.fasterxml.jackson.databind.JsonNode;
26+
import com.fasterxml.jackson.databind.ObjectMapper;
27+
import com.fasterxml.jackson.databind.util.StdConverter;
28+
29+
import org.springframework.security.oauth2.core.AuthorizationGrantType;
30+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
31+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest.Builder;
32+
33+
/**
34+
* A {@code JsonDeserializer} for {@link OAuth2AuthorizationRequest}.
35+
*
36+
* @author Joe Grandja
37+
* @since 0.1.2
38+
* @see OAuth2AuthorizationRequest
39+
* @see OAuth2AuthorizationRequestMixin
40+
*/
41+
final class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer<OAuth2AuthorizationRequest> {
42+
43+
private static final StdConverter<JsonNode, AuthorizationGrantType> AUTHORIZATION_GRANT_TYPE_CONVERTER = new StdConverters.AuthorizationGrantTypeConverter();
44+
45+
@Override
46+
public OAuth2AuthorizationRequest deserialize(JsonParser parser, DeserializationContext context)
47+
throws IOException {
48+
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
49+
JsonNode root = mapper.readTree(parser);
50+
return deserialize(parser, mapper, root);
51+
}
52+
53+
private OAuth2AuthorizationRequest deserialize(JsonParser parser, ObjectMapper mapper, JsonNode root)
54+
throws JsonParseException {
55+
AuthorizationGrantType authorizationGrantType = AUTHORIZATION_GRANT_TYPE_CONVERTER
56+
.convert(JsonNodeUtils.findObjectNode(root, "authorizationGrantType"));
57+
Builder builder = getBuilder(parser, authorizationGrantType);
58+
builder.authorizationUri(JsonNodeUtils.findStringValue(root, "authorizationUri"));
59+
builder.clientId(JsonNodeUtils.findStringValue(root, "clientId"));
60+
builder.redirectUri(JsonNodeUtils.findStringValue(root, "redirectUri"));
61+
builder.scopes(JsonNodeUtils.findValue(root, "scopes", JsonNodeUtils.STRING_SET, mapper));
62+
builder.state(JsonNodeUtils.findStringValue(root, "state"));
63+
builder.additionalParameters(
64+
JsonNodeUtils.findValue(root, "additionalParameters", JsonNodeUtils.STRING_OBJECT_MAP, mapper));
65+
builder.authorizationRequestUri(JsonNodeUtils.findStringValue(root, "authorizationRequestUri"));
66+
builder.attributes(JsonNodeUtils.findValue(root, "attributes", JsonNodeUtils.STRING_OBJECT_MAP, mapper));
67+
return builder.build();
68+
}
69+
70+
private Builder getBuilder(JsonParser parser,
71+
AuthorizationGrantType authorizationGrantType) throws JsonParseException {
72+
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
73+
return OAuth2AuthorizationRequest.authorizationCode();
74+
}
75+
if (AuthorizationGrantType.IMPLICIT.equals(authorizationGrantType)) {
76+
return OAuth2AuthorizationRequest.implicit();
77+
}
78+
throw new JsonParseException(parser, "Invalid authorizationGrantType");
79+
}
80+
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
17+
package org.springframework.security.oauth2.server.authorization.jackson2;
18+
19+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
20+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
21+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
22+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
23+
24+
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
25+
26+
/**
27+
* This mixin class is used to serialize/deserialize {@link OAuth2AuthorizationRequest}.
28+
* It also registers a custom deserializer {@link OAuth2AuthorizationRequestDeserializer}.
29+
*
30+
* @author Joe Grandja
31+
* @since 0.1.2
32+
* @see OAuth2AuthorizationRequest
33+
* @see OAuth2AuthorizationRequestDeserializer
34+
* @see OAuth2ServerJackson2Module
35+
*/
36+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
37+
@JsonDeserialize(using = OAuth2AuthorizationRequestDeserializer.class)
38+
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
39+
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
40+
@JsonIgnoreProperties(ignoreUnknown = true)
41+
abstract class OAuth2AuthorizationRequestMixin {
42+
43+
}
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+
17+
package org.springframework.security.oauth2.server.authorization.jackson2;
18+
19+
import java.util.Map;
20+
21+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
22+
import com.fasterxml.jackson.annotation.JsonCreator;
23+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
26+
27+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
28+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
29+
30+
/**
31+
* This mixin class is used to serialize/deserialize {@link OAuth2ClientAuthenticationToken}.
32+
*
33+
* @author Joe Grandja
34+
* @since 0.1.2
35+
* @see OAuth2ClientAuthenticationToken
36+
* @see OAuth2ServerJackson2Module
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 = { "authenticated" }, ignoreUnknown = true)
42+
abstract class OAuth2ClientAuthenticationTokenMixin {
43+
44+
@JsonCreator
45+
OAuth2ClientAuthenticationTokenMixin(@JsonProperty("clientId") String clientId,
46+
@JsonProperty("clientSecret") String clientSecret,
47+
@JsonProperty("clientAuthenticationMethod") ClientAuthenticationMethod clientAuthenticationMethod,
48+
@JsonProperty("additionalParameters") Map<String, Object> additionalParameters) {
49+
}
50+
51+
}

0 commit comments

Comments
 (0)