Add Jackson 3 support
This commit adds support for Jackson 3 which has the following major differences with the Jackson 2 one: - jackson subpackage instead of jackson2 - Jackson type prefix instead of Jackson2 - JsonMapper instead of ObjectMapper - For configuration, JsonMapper.Builder instead of ObjectMapper since the latter is now immutable - Remove custom support for unmodifiable collections - Use safe default typing via a PolymorphicTypeValidator Jackson 3 changes compared to Jackson 2 are documented in https://cowtowncoder.medium.com/jackson-3-0-0-ga-released-1f669cda529a and https://github.com/FasterXML/jackson/blob/main/jackson3/MIGRATING_TO_JACKSON_3.md. This commit does not cover webauthn which is a special case (uses jackson sub-package for Jackson 2 support) which will be handled in a distinct commit. See gh-17832 Signed-off-by: Sébastien Deleuze <sdeleuze@users.noreply.github.com>
This commit is contained in:
committed by
Rob Winch
parent
916a687b29
commit
65a14d6c6d
@@ -25,6 +25,7 @@ dependencies {
|
||||
optional 'org.springframework:spring-jdbc'
|
||||
optional 'org.springframework:spring-tx'
|
||||
optional 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor'
|
||||
optional 'tools.jackson.core:jackson-databind'
|
||||
|
||||
testImplementation 'commons-collections:commons-collections'
|
||||
testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This is a Jackson mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken} class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
class AnonymousAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create object of
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken}.
|
||||
* @param keyHash hashCode of key provided at the time of token creation by using
|
||||
* {@link org.springframework.security.authentication.AnonymousAuthenticationToken#AnonymousAuthenticationToken(String, Object, Collection)}
|
||||
* @param principal the principal (typically a <code>UserDetails</code>)
|
||||
* @param authorities the authorities granted to the principal
|
||||
*/
|
||||
@JsonCreator
|
||||
AnonymousAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
|
||||
@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.BadCredentialsException} class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Yannick Lombardi
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonIgnoreProperties({ "cause", "stackTrace", "authenticationRequest" })
|
||||
class BadCredentialsExceptionMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create
|
||||
* {@link org.springframework.security.authentication.BadCredentialsException} object.
|
||||
* @param message the detail message
|
||||
*/
|
||||
@JsonCreator
|
||||
BadCredentialsExceptionMixin(@JsonProperty("message") String message) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.cfg.DateTimeFeature;
|
||||
import tools.jackson.databind.cfg.MapperBuilder;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* Jackson module for spring-security-core. This module register
|
||||
* {@link AnonymousAuthenticationTokenMixin}, {@link RememberMeAuthenticationTokenMixin},
|
||||
* {@link SimpleGrantedAuthorityMixin}, {@link FactorGrantedAuthorityMixin},
|
||||
* {{@link UserMixin}, {@link UsernamePasswordAuthenticationTokenMixin} and
|
||||
* {@link UsernamePasswordAuthenticationTokenMixin}.
|
||||
*
|
||||
* <p>
|
||||
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
|
||||
* to enable properly automatic inclusion of type information with related validation.
|
||||
*
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.O
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
|
||||
public class CoreJacksonModule extends SecurityJacksonModule {
|
||||
|
||||
public CoreJacksonModule() {
|
||||
super(CoreJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
protected CoreJacksonModule(String name, Version version) {
|
||||
super(name, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
|
||||
builder.allowIfSubType(Instant.class)
|
||||
.allowIfSubType(Duration.class)
|
||||
.allowIfSubType(SimpleGrantedAuthority.class)
|
||||
.allowIfSubType(FactorGrantedAuthority.class)
|
||||
.allowIfSubType(UsernamePasswordAuthenticationToken.class)
|
||||
.allowIfSubType(RememberMeAuthenticationToken.class)
|
||||
.allowIfSubType(AnonymousAuthenticationToken.class)
|
||||
.allowIfSubType(User.class)
|
||||
.allowIfSubType(BadCredentialsException.class)
|
||||
.allowIfSubType(SecurityContextImpl.class)
|
||||
.allowIfSubType(TestingAuthenticationToken.class)
|
||||
.allowIfSubType("java.util.Collections$UnmodifiableSet")
|
||||
.allowIfSubType("java.util.Collections$UnmodifiableRandomAccessList")
|
||||
.allowIfSubType("java.util.Collections$EmptyList")
|
||||
.allowIfSubType("java.util.ArrayList")
|
||||
.allowIfSubType("java.util.HashMap")
|
||||
.allowIfSubType("java.util.Collections$EmptyMap")
|
||||
.allowIfSubType("java.util.Date")
|
||||
.allowIfSubType("java.util.Arrays$ArrayList")
|
||||
.allowIfSubType("java.util.Collections$UnmodifiableMap")
|
||||
.allowIfSubType("java.util.LinkedHashMap")
|
||||
.allowIfSubType("java.util.Collections$SingletonList")
|
||||
.allowIfSubType("java.util.TreeMap")
|
||||
.allowIfSubType("java.util.HashSet")
|
||||
.allowIfSubType("java.util.LinkedHashSet");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
((MapperBuilder<?, ?>) context.getOwner()).enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
context.setMixIn(AnonymousAuthenticationToken.class, AnonymousAuthenticationTokenMixin.class);
|
||||
context.setMixIn(RememberMeAuthenticationToken.class, RememberMeAuthenticationTokenMixin.class);
|
||||
context.setMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);
|
||||
context.setMixIn(FactorGrantedAuthority.class, FactorGrantedAuthorityMixin.class);
|
||||
context.setMixIn(User.class, UserMixin.class);
|
||||
context.setMixIn(UsernamePasswordAuthenticationToken.class, UsernamePasswordAuthenticationTokenMixin.class);
|
||||
context.setMixIn(BadCredentialsException.class, BadCredentialsExceptionMixin.class);
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson Mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Rob Winch
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class FactorGrantedAuthorityMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor.
|
||||
* @param authority the authority
|
||||
*/
|
||||
@JsonCreator
|
||||
FactorGrantedAuthorityMixin(@JsonProperty("authority") String authority,
|
||||
@JsonProperty("issuedAt") Instant issuedAt) {
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.authentication.RememberMeAuthenticationToken}
|
||||
* class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class RememberMeAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Constructor used by Jackson to create
|
||||
* {@link org.springframework.security.authentication.RememberMeAuthenticationToken}
|
||||
* object.
|
||||
* @param keyHash hashCode of above given key.
|
||||
* @param principal the principal (typically a <code>UserDetails</code>)
|
||||
* @param authorities the authorities granted to the principal
|
||||
*/
|
||||
@JsonCreator
|
||||
RememberMeAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash,
|
||||
@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import tools.jackson.core.Version;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
import tools.jackson.databind.module.SimpleModule;
|
||||
|
||||
/**
|
||||
* Jackson module allowing to contribute {@link PolymorphicTypeValidator} configuration.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 7.0
|
||||
*/
|
||||
public abstract class SecurityJacksonModule extends SimpleModule {
|
||||
|
||||
public SecurityJacksonModule() {
|
||||
super();
|
||||
}
|
||||
|
||||
public SecurityJacksonModule(String name, Version version) {
|
||||
super(name, version, null);
|
||||
}
|
||||
|
||||
public abstract void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tools.jackson.databind.DefaultTyping;
|
||||
import tools.jackson.databind.JacksonModule;
|
||||
import tools.jackson.databind.cfg.MapperBuilder;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
|
||||
import tools.jackson.databind.module.SimpleModule;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* This utility class will find all the Jackson modules contributed by Spring Security in
|
||||
* the classpath (except {@code OAuth2AuthorizationServerJacksonModule} and
|
||||
* {@code WebauthnJacksonModule}), enable automatic inclusion of type information and
|
||||
* configure a {@link PolymorphicTypeValidator} that handles the validation of class
|
||||
* names.
|
||||
*
|
||||
* <p>
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* If needed, you can add custom classes to the validation handling.
|
||||
* <p>
|
||||
* <pre>
|
||||
* ClassLoader loader = getClass().getClassLoader();
|
||||
* BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()
|
||||
* .allowIfSubType(MyCustomType.class);
|
||||
* JsonMapper mapper = JsonMapper.builder()
|
||||
* .addModules(SecurityJacksonModules.getModules(loader, builder))
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
*/
|
||||
public final class SecurityJacksonModules {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SecurityJacksonModules.class);
|
||||
|
||||
private static final List<String> securityJacksonModuleClasses = Arrays.asList(
|
||||
"org.springframework.security.jackson.CoreJacksonModule",
|
||||
"org.springframework.security.web.jackson.WebJacksonModule",
|
||||
"org.springframework.security.web.server.jackson.WebServerJacksonModule");
|
||||
|
||||
private static final String webServletJacksonModuleClass = "org.springframework.security.web.jackson.WebServletJacksonModule";
|
||||
|
||||
private static final String oauth2ClientJacksonModuleClass = "org.springframework.security.oauth2.client.jackson.OAuth2ClientJacksonModule";
|
||||
|
||||
private static final String ldapJacksonModuleClass = "org.springframework.security.ldap.jackson.LdapJacksonModule";
|
||||
|
||||
private static final String saml2JacksonModuleClass = "org.springframework.security.saml2.jackson.Saml2JacksonModule";
|
||||
|
||||
private static final String casJacksonModuleClass = "org.springframework.security.cas.jackson.CasJacksonModule";
|
||||
|
||||
private static final boolean webServletPresent;
|
||||
|
||||
private static final boolean oauth2ClientPresent;
|
||||
|
||||
private static final boolean ldapJacksonPresent;
|
||||
|
||||
private static final boolean saml2JacksonPresent;
|
||||
|
||||
private static final boolean casJacksonPresent;
|
||||
|
||||
static {
|
||||
|
||||
ClassLoader classLoader = SecurityJacksonModules.class.getClassLoader();
|
||||
webServletPresent = ClassUtils.isPresent("jakarta.servlet.http.Cookie", classLoader);
|
||||
oauth2ClientPresent = ClassUtils.isPresent("org.springframework.security.oauth2.client.OAuth2AuthorizedClient",
|
||||
classLoader);
|
||||
ldapJacksonPresent = ClassUtils.isPresent(ldapJacksonModuleClass, classLoader);
|
||||
saml2JacksonPresent = ClassUtils.isPresent(saml2JacksonModuleClass, classLoader);
|
||||
casJacksonPresent = ClassUtils.isPresent(casJacksonModuleClass, classLoader);
|
||||
}
|
||||
|
||||
private SecurityJacksonModules() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static @Nullable SecurityJacksonModule loadAndGetInstance(String className, ClassLoader loader) {
|
||||
try {
|
||||
Class<? extends SecurityJacksonModule> securityModule = (Class<? extends SecurityJacksonModule>) ClassUtils
|
||||
.forName(className, loader);
|
||||
logger.debug(LogMessage.format("Loaded module %s, now registering", className));
|
||||
return securityModule.getConstructor().newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.debug(LogMessage.format("Cannot load module %s", className), ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of available security modules in classpath, enable automatic
|
||||
* inclusion of type information and configure a default
|
||||
* {@link PolymorphicTypeValidator} that handles the validation of class names.
|
||||
* @param loader the ClassLoader to use
|
||||
* @return List of available security modules in classpath
|
||||
* @see #getModules(ClassLoader, BasicPolymorphicTypeValidator.Builder)
|
||||
*/
|
||||
public static List<JacksonModule> getModules(ClassLoader loader) {
|
||||
return getModules(loader, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of available security modules in classpath, enable automatic
|
||||
* inclusion of type information and configure a default
|
||||
* {@link PolymorphicTypeValidator} customizable with the provided builder that
|
||||
* handles the validation of class names.
|
||||
* @param loader the ClassLoader to use
|
||||
* @param typeValidatorBuilder the builder to configure custom types allowed in
|
||||
* addition to Spring Security ones
|
||||
* @return List of available security modules in classpath.
|
||||
*/
|
||||
public static List<JacksonModule> getModules(ClassLoader loader,
|
||||
BasicPolymorphicTypeValidator.@Nullable Builder typeValidatorBuilder) {
|
||||
|
||||
List<JacksonModule> modules = new ArrayList<>();
|
||||
for (String className : securityJacksonModuleClasses) {
|
||||
addToModulesList(loader, modules, className);
|
||||
}
|
||||
if (webServletPresent) {
|
||||
addToModulesList(loader, modules, webServletJacksonModuleClass);
|
||||
}
|
||||
if (oauth2ClientPresent) {
|
||||
addToModulesList(loader, modules, oauth2ClientJacksonModuleClass);
|
||||
}
|
||||
if (ldapJacksonPresent) {
|
||||
addToModulesList(loader, modules, ldapJacksonModuleClass);
|
||||
}
|
||||
if (saml2JacksonPresent) {
|
||||
addToModulesList(loader, modules, saml2JacksonModuleClass);
|
||||
}
|
||||
if (casJacksonPresent) {
|
||||
addToModulesList(loader, modules, casJacksonModuleClass);
|
||||
}
|
||||
applyPolymorphicTypeValidator(modules, typeValidatorBuilder);
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static void applyPolymorphicTypeValidator(List<JacksonModule> modules,
|
||||
BasicPolymorphicTypeValidator.@Nullable Builder typeValidatorBuilder) {
|
||||
|
||||
BasicPolymorphicTypeValidator.Builder builder = (typeValidatorBuilder != null) ? typeValidatorBuilder
|
||||
: BasicPolymorphicTypeValidator.builder();
|
||||
for (JacksonModule module : modules) {
|
||||
if (module instanceof SecurityJacksonModule securityModule) {
|
||||
securityModule.configurePolymorphicTypeValidator(builder);
|
||||
}
|
||||
}
|
||||
modules.add(new SimpleModule() {
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
((MapperBuilder<?, ?>) context.getOwner()).activateDefaultTyping(builder.build(),
|
||||
DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loader the ClassLoader to use
|
||||
* @param modules list of the modules to add
|
||||
* @param className name of the class to instantiate
|
||||
*/
|
||||
private static void addToModulesList(ClassLoader loader, List<JacksonModule> modules, String className) {
|
||||
SecurityJacksonModule module = loadAndGetInstance(className, loader);
|
||||
if (module != null) {
|
||||
modules.add(module);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
/**
|
||||
* Jackson Mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.authority.SimpleGrantedAuthority}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
public abstract class SimpleGrantedAuthorityMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor.
|
||||
* @param role the role
|
||||
*/
|
||||
@JsonCreator
|
||||
public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.node.MissingNode;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* Custom Deserializer for {@link User} class. This is already registered with
|
||||
* {@link UserMixin}. You can also use it directly with your mixin class.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see UserMixin
|
||||
*/
|
||||
class UserDeserializer extends ValueDeserializer<User> {
|
||||
|
||||
private static final TypeReference<Set<GrantedAuthority>> GRANTED_AUTHORITY_SET = new TypeReference<>() {
|
||||
};
|
||||
|
||||
/**
|
||||
* This method will create {@link User} object. It will ensure successful object
|
||||
* creation even if password key is null in serialized json, because credentials may
|
||||
* be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In
|
||||
* that case there won't be any password key in serialized json.
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws JacksonException if an error during JSON processing occurs
|
||||
*/
|
||||
@Override
|
||||
public User deserialize(JsonParser jp, DeserializationContext ctxt) throws JacksonException {
|
||||
JsonNode jsonNode = ctxt.readTree(jp);
|
||||
JsonNode authoritiesNode = readJsonNode(jsonNode, "authorities");
|
||||
Set<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,
|
||||
ctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_SET));
|
||||
JsonNode passwordNode = readJsonNode(jsonNode, "password");
|
||||
String username = readJsonNode(jsonNode, "username").asString();
|
||||
String password = (passwordNode.isMissingNode()) ? null : passwordNode.stringValue();
|
||||
boolean enabled = readJsonNode(jsonNode, "enabled").asBoolean();
|
||||
boolean accountNonExpired = readJsonNode(jsonNode, "accountNonExpired").asBoolean();
|
||||
boolean credentialsNonExpired = readJsonNode(jsonNode, "credentialsNonExpired").asBoolean();
|
||||
boolean accountNonLocked = readJsonNode(jsonNode, "accountNonLocked").asBoolean();
|
||||
User result = new User(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked,
|
||||
authorities);
|
||||
if (passwordNode.asString(null) == null) {
|
||||
result.eraseCredentials();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* This mixin class helps in serialize/deserialize
|
||||
* {@link org.springframework.security.core.userdetails.User}. This class also register a
|
||||
* custom deserializer {@link UserDeserializer} to deserialize User object successfully.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see UserDeserializer
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonDeserialize(using = UserDeserializer.class)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class UserMixin {
|
||||
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tools.jackson.core.JacksonException;
|
||||
import tools.jackson.core.JsonParser;
|
||||
import tools.jackson.core.exc.StreamReadException;
|
||||
import tools.jackson.core.type.TypeReference;
|
||||
import tools.jackson.databind.DatabindException;
|
||||
import tools.jackson.databind.DeserializationContext;
|
||||
import tools.jackson.databind.JsonNode;
|
||||
import tools.jackson.databind.ValueDeserializer;
|
||||
import tools.jackson.databind.node.MissingNode;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* Custom deserializer for {@link UsernamePasswordAuthenticationToken}. At the time of
|
||||
* deserialization it will invoke suitable constructor depending on the value of
|
||||
* <b>authenticated</b> property. It will ensure that the token's state must not change.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @author Greg Turnquist
|
||||
* @author Onur Kagan Ozcan
|
||||
* @since 7.0
|
||||
* @see UsernamePasswordAuthenticationTokenMixin
|
||||
*/
|
||||
class UsernamePasswordAuthenticationTokenDeserializer extends ValueDeserializer<UsernamePasswordAuthenticationToken> {
|
||||
|
||||
private static final TypeReference<List<GrantedAuthority>> GRANTED_AUTHORITY_LIST = new TypeReference<>() {
|
||||
};
|
||||
|
||||
/**
|
||||
* This method construct {@link UsernamePasswordAuthenticationToken} object from
|
||||
* serialized json.
|
||||
* @param jp the JsonParser
|
||||
* @param ctxt the DeserializationContext
|
||||
* @return the user
|
||||
* @throws JacksonException if an error during JSON processing occurs
|
||||
*/
|
||||
@Override
|
||||
public UsernamePasswordAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt)
|
||||
throws JacksonException {
|
||||
JsonNode jsonNode = ctxt.readTree(jp);
|
||||
boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
|
||||
JsonNode principalNode = readJsonNode(jsonNode, "principal");
|
||||
Object principal = getPrincipal(ctxt, principalNode);
|
||||
JsonNode credentialsNode = readJsonNode(jsonNode, "credentials");
|
||||
Object credentials = getCredentials(credentialsNode);
|
||||
JsonNode authoritiesNode = readJsonNode(jsonNode, "authorities");
|
||||
List<GrantedAuthority> authorities = ctxt.readTreeAsValue(authoritiesNode,
|
||||
ctxt.getTypeFactory().constructType(GRANTED_AUTHORITY_LIST));
|
||||
UsernamePasswordAuthenticationToken token = (!authenticated)
|
||||
? UsernamePasswordAuthenticationToken.unauthenticated(principal, credentials)
|
||||
: UsernamePasswordAuthenticationToken.authenticated(principal, credentials, authorities);
|
||||
JsonNode detailsNode = readJsonNode(jsonNode, "details");
|
||||
if (detailsNode.isNull() || detailsNode.isMissingNode()) {
|
||||
token.setDetails(null);
|
||||
}
|
||||
else {
|
||||
Object details = ctxt.readTreeAsValue(detailsNode, Object.class);
|
||||
token.setDetails(details);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
private @Nullable Object getCredentials(JsonNode credentialsNode) {
|
||||
if (credentialsNode.isNull() || credentialsNode.isMissingNode()) {
|
||||
return null;
|
||||
}
|
||||
return credentialsNode.asString();
|
||||
}
|
||||
|
||||
private Object getPrincipal(DeserializationContext ctxt, JsonNode principalNode)
|
||||
throws StreamReadException, DatabindException {
|
||||
if (principalNode.isObject()) {
|
||||
return ctxt.readTreeAsValue(principalNode, Object.class);
|
||||
}
|
||||
return principalNode.asString();
|
||||
}
|
||||
|
||||
private JsonNode readJsonNode(JsonNode jsonNode, String field) {
|
||||
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import tools.jackson.databind.annotation.JsonDeserialize;
|
||||
|
||||
/**
|
||||
* This mixin class is used to serialize / deserialize
|
||||
* {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken}.
|
||||
* This class register a custom deserializer
|
||||
* {@link UsernamePasswordAuthenticationTokenDeserializer}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Jitendra Singh
|
||||
* @since 7.0
|
||||
* @see CoreJacksonModule
|
||||
* @see SecurityJacksonModules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonDeserialize(using = UsernamePasswordAuthenticationTokenDeserializer.class)
|
||||
abstract class UsernamePasswordAuthenticationTokenMixin {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Jackson 3+ serialization support.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.jackson;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
+1
-1
@@ -71,7 +71,7 @@ class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<U
|
||||
throws IOException, JsonProcessingException {
|
||||
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
|
||||
JsonNode jsonNode = mapper.readTree(jp);
|
||||
Boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
|
||||
boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean();
|
||||
JsonNode principalNode = readJsonNode(jsonNode, "principal");
|
||||
Object principal = getPrincipal(mapper, principalNode);
|
||||
JsonNode credentialsNode = readJsonNode(jsonNode, "credentials");
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Mix-in classes to add Jackson serialization support.
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* Jackson 2 serialization support.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.jackson2;
|
||||
|
||||
+6
-5
@@ -34,9 +34,9 @@ import java.util.TreeSet;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
@@ -340,13 +340,14 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
||||
assertThat(factory.proxy(35)).isEqualTo(35);
|
||||
}
|
||||
|
||||
// TODO Find why callbacks property is serialized with Jackson 3, not with Jackson 2
|
||||
@Disabled("callbacks property is serialized with Jackson 3, not with Jackson 2")
|
||||
@Test
|
||||
public void serializeWhenAuthorizationProxyObjectThenOnlyIncludesProxiedProperties()
|
||||
throws JsonProcessingException {
|
||||
public void serializeWhenAuthorizationProxyObjectThenOnlyIncludesProxiedProperties() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.admin);
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
User user = proxy(factory, this.alan);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonMapper mapper = new JsonMapper();
|
||||
String serialized = mapper.writeValueAsString(user);
|
||||
Map<String, Object> properties = mapper.readValue(serialized, Map.class);
|
||||
assertThat(properties).hasSize(3).containsKeys("id", "firstName", "lastName");
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
/**
|
||||
* @author Jitenra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public abstract class AbstractMixinTests {
|
||||
|
||||
protected JsonMapper mapper;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfSubType(
|
||||
"org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal");
|
||||
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader, builder)).build();
|
||||
}
|
||||
|
||||
User createDefaultUser() {
|
||||
return createUser("admin", "1234", "ROLE_USER");
|
||||
}
|
||||
|
||||
User createUser(String username, String password, String authority) {
|
||||
return new User(username, password, AuthorityUtils.createAuthorityList(authority));
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.ValueInstantiationException;
|
||||
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class AnonymousAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
|
||||
private static final String HASH_KEY = "key";
|
||||
|
||||
// @formatter:off
|
||||
private static final String ANONYMOUS_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.AnonymousAuthenticationToken\", "
|
||||
+ "\"details\": null,"
|
||||
+ "\"principal\": " + UserDeserializerTests.USER_JSON + ","
|
||||
+ "\"authenticated\": true, "
|
||||
+ "\"keyHash\": " + HASH_KEY.hashCode() + ","
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeAnonymousAuthenticationTokenTest() throws JSONException {
|
||||
User user = createDefaultUser();
|
||||
AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(ANONYMOUS_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAnonymousAuthenticationTokenTest() {
|
||||
AnonymousAuthenticationToken token = this.mapper.readValue(ANONYMOUS_JSON, AnonymousAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getKeyHash()).isEqualTo(HASH_KEY.hashCode());
|
||||
assertThat(token.getAuthorities()).isNotNull().hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAnonymousAuthenticationTokenWithoutAuthoritiesTest() {
|
||||
String jsonString = "{\"@class\": \"org.springframework.security.authentication.AnonymousAuthenticationToken\", \"details\": null,"
|
||||
+ "\"principal\": \"user\", \"authenticated\": true, \"keyHash\": " + HASH_KEY.hashCode() + ","
|
||||
+ "\"authorities\": [\"java.util.ArrayList\", []]}";
|
||||
assertThatExceptionOfType(ValueInstantiationException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(jsonString, AnonymousAuthenticationToken.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAnonymousAuthenticationTokenMixinAfterEraseCredentialTest() throws JSONException {
|
||||
User user = createDefaultUser();
|
||||
AnonymousAuthenticationToken token = new AnonymousAuthenticationToken(HASH_KEY, user, user.getAuthorities());
|
||||
token.eraseCredentials();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(ANONYMOUS_JSON.replace(UserDeserializerTests.USER_PASSWORD, "null"), actualJson, true);
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Yannick Lombardi
|
||||
* @since 5.0
|
||||
*/
|
||||
public class BadCredentialsExceptionMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
private static final String EXCEPTION_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.BadCredentialsException\","
|
||||
+ "\"localizedMessage\": \"message\", "
|
||||
+ "\"message\": \"message\", "
|
||||
+ "\"suppressed\": [\"[Ljava.lang.Throwable;\",[]]"
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeBadCredentialsExceptionMixinTest() throws JsonProcessingException, JSONException {
|
||||
BadCredentialsException exception = new BadCredentialsException("message");
|
||||
String serializedJson = this.mapper.writeValueAsString(exception);
|
||||
JSONAssert.assertEquals(EXCEPTION_JSON, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeBadCredentialsExceptionMixinTest() throws IOException {
|
||||
BadCredentialsException exception = this.mapper.readValue(EXCEPTION_JSON, BadCredentialsException.class);
|
||||
assertThat(exception).isNotNull();
|
||||
assertThat(exception.getCause()).isNull();
|
||||
assertThat(exception.getMessage()).isEqualTo("message");
|
||||
assertThat(exception.getLocalizedMessage()).isEqualTo("message");
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.FactorGrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 7.0
|
||||
*/
|
||||
class FactorGrantedAuthorityMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.FactorGrantedAuthority\", \"authority\": \"FACTOR_PASSWORD\", \"issuedAt\": 1759177143.043000000 }";
|
||||
|
||||
private Instant issuedAt = Instant.ofEpochMilli(1759177143043L);
|
||||
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
void serializeSimpleGrantedAuthorityTest() throws JSONException {
|
||||
GrantedAuthority authority = FactorGrantedAuthority.withAuthority("FACTOR_PASSWORD")
|
||||
.issuedAt(this.issuedAt)
|
||||
.build();
|
||||
String serializeJson = this.mapper.writeValueAsString(authority);
|
||||
JSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deserializeGrantedAuthorityTest() {
|
||||
FactorGrantedAuthority authority = (FactorGrantedAuthority) this.mapper.readValue(AUTHORITY_JSON, Object.class);
|
||||
assertThat(authority).isNotNull();
|
||||
assertThat(authority.getAuthority()).isEqualTo("FACTOR_PASSWORD");
|
||||
assertThat(authority.getIssuedAt()).isEqualTo(this.issuedAt);
|
||||
}
|
||||
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class RememberMeAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
|
||||
private static final String REMEMBERME_KEY = "rememberMe";
|
||||
|
||||
// @formatter:off
|
||||
private static final String REMEMBERME_AUTH_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.RememberMeAuthenticationToken\", "
|
||||
+ "\"keyHash\": " + REMEMBERME_KEY.hashCode() + ", "
|
||||
+ "\"authenticated\": true, \"details\": null" + ", "
|
||||
+ "\"principal\": " + UserDeserializerTests.USER_JSON + ", "
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
// @formatter:off
|
||||
private static final String REMEMBERME_AUTH_STRINGPRINCIPAL_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.RememberMeAuthenticationToken\","
|
||||
+ "\"keyHash\": " + REMEMBERME_KEY.hashCode() + ", "
|
||||
+ "\"authenticated\": true, "
|
||||
+ "\"details\": null,"
|
||||
+ "\"principal\": \"admin\", "
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
public void testWithNullPrincipal() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new RememberMeAuthenticationToken("key", null, Collections.<GrantedAuthority>emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithNullKey() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new RememberMeAuthenticationToken(null, "principal", Collections.<GrantedAuthority>emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeRememberMeAuthenticationToken() throws JsonProcessingException, JSONException {
|
||||
RememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, "admin",
|
||||
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeRememberMeAuthenticationWithUserToken() throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
RememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,
|
||||
user.getAuthorities());
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(String.format(REMEMBERME_AUTH_JSON, "\"password\""), actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeRememberMeAuthenticationWithUserTokenAfterEraseCredential()
|
||||
throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
RememberMeAuthenticationToken token = new RememberMeAuthenticationToken(REMEMBERME_KEY, user,
|
||||
user.getAuthorities());
|
||||
token.eraseCredentials();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(REMEMBERME_AUTH_JSON.replace(UserDeserializerTests.USER_PASSWORD, "null"), actualJson,
|
||||
true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeRememberMeAuthenticationToken() throws IOException {
|
||||
RememberMeAuthenticationToken token = this.mapper.readValue(REMEMBERME_AUTH_STRINGPRINCIPAL_JSON,
|
||||
RememberMeAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isEqualTo("admin").isEqualTo(token.getName());
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeRememberMeAuthenticationTokenWithUserTest() throws IOException {
|
||||
RememberMeAuthenticationToken token = this.mapper.readValue(String.format(REMEMBERME_AUTH_JSON, "\"password\""),
|
||||
RememberMeAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(((User) token.getPrincipal()).getUsername()).isEqualTo("admin");
|
||||
assertThat(((User) token.getPrincipal()).getPassword()).isEqualTo("1234");
|
||||
assertThat(((User) token.getPrincipal()).getAuthorities()).hasSize(1)
|
||||
.contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(((User) token.getPrincipal()).isEnabled()).isEqualTo(true);
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class SecurityContextMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
public static final String SECURITY_CONTEXT_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.core.context.SecurityContextImpl\", "
|
||||
+ "\"authentication\": " + UsernamePasswordAuthenticationTokenMixinTests.AUTHENTICATED_STRINGPRINCIPAL_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void securityContextSerializeTest() throws JsonProcessingException, JSONException {
|
||||
SecurityContext context = new SecurityContextImpl();
|
||||
context.setAuthentication(UsernamePasswordAuthenticationToken.authenticated("admin", "1234",
|
||||
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))));
|
||||
String actualJson = this.mapper.writeValueAsString(context);
|
||||
JSONAssert.assertEquals(SECURITY_CONTEXT_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityContextDeserializeTest() throws IOException {
|
||||
SecurityContext context = this.mapper.readValue(SECURITY_CONTEXT_JSON, SecurityContextImpl.class);
|
||||
assertThat(context).isNotNull();
|
||||
assertThat(context.getAuthentication()).isNotNull().isInstanceOf(UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(context.getAuthentication().getPrincipal()).isEqualTo("admin");
|
||||
assertThat(context.getAuthentication().getCredentials()).isEqualTo("1234");
|
||||
assertThat(context.getAuthentication().isAuthenticated()).isTrue();
|
||||
Collection authorities = context.getAuthentication().getAuthorities();
|
||||
assertThat(authorities).hasSize(1);
|
||||
assertThat(authorities).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import tools.jackson.databind.JacksonModule;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
public class SecurityJacksonModulesTests {
|
||||
|
||||
@Test
|
||||
public void addModulesWithNoTypeValidatorBuilder() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
List<JacksonModule> modules = SecurityJacksonModules.getModules(loader);
|
||||
JsonMapper mapper = JsonMapper.builder().addModules(modules).build();
|
||||
User user = new User("user", null, List.of(new SimpleGrantedAuthority("SCOPE_message:read")));
|
||||
String json = mapper.writeValueAsString(user);
|
||||
User deserializedUer = mapper.readerFor(User.class).readValue(json);
|
||||
assertThat(deserializedUer).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addModulesWithDefaultTypeValidatorBuilder() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
List<JacksonModule> modules = SecurityJacksonModules.getModules(loader,
|
||||
BasicPolymorphicTypeValidator.builder());
|
||||
JsonMapper mapper = JsonMapper.builder().addModules(modules).build();
|
||||
User user = new User("user", null, List.of(new SimpleGrantedAuthority("SCOPE_message:read")));
|
||||
String json = mapper.writeValueAsString(user);
|
||||
User deserializedUer = mapper.readerFor(User.class).readValue(json);
|
||||
assertThat(deserializedUer).isEqualTo(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addModulesWithCustomTypeValidator() {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
BasicPolymorphicTypeValidator.Builder builder = BasicPolymorphicTypeValidator.builder()
|
||||
.allowIfSubType(TestGrantedAuthority.class);
|
||||
List<JacksonModule> modules = SecurityJacksonModules.getModules(loader, builder);
|
||||
JsonMapper mapper = JsonMapper.builder().addModules(modules).build();
|
||||
User user = new User("user", null, List.of(new TestGrantedAuthority()));
|
||||
String json = mapper.writeValueAsString(user);
|
||||
User deserializedUer = mapper.readerFor(User.class).readValue(json);
|
||||
assertThat(deserializedUer).isEqualTo(user);
|
||||
}
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
private static class TestGrantedAuthority implements GrantedAuthority {
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.ValueInstantiationException;
|
||||
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class SimpleGrantedAuthorityMixinTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}";
|
||||
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", [" + AUTHORITY_JSON + "]]";
|
||||
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON + "]]";
|
||||
public static final String NO_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", []]";
|
||||
public static final String EMPTY_AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$EmptyList\", []]";
|
||||
public static final String NO_AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", []]";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeSimpleGrantedAuthorityTest() throws JsonProcessingException, JSONException {
|
||||
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");
|
||||
String serializeJson = this.mapper.writeValueAsString(authority);
|
||||
JSONAssert.assertEquals(AUTHORITY_JSON, serializeJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeGrantedAuthorityTest() throws IOException {
|
||||
SimpleGrantedAuthority authority = this.mapper.readValue(AUTHORITY_JSON, SimpleGrantedAuthority.class);
|
||||
assertThat(authority).isNotNull();
|
||||
assertThat(authority.getAuthority()).isNotNull().isEqualTo("ROLE_USER");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeGrantedAuthorityWithoutRoleTest() throws IOException {
|
||||
String json = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\"}";
|
||||
assertThatExceptionOfType(ValueInstantiationException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(json, SimpleGrantedAuthority.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class UnmodifiableMapTests extends AbstractMixinTests {
|
||||
|
||||
// @formatter:off
|
||||
private static final String DEFAULT_MAP_JSON = "{"
|
||||
+ "\"@class\": \"java.util.Collections$UnmodifiableMap\","
|
||||
+ "\"Key\": \"Value\""
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
|
||||
@Test
|
||||
void shouldSerialize() throws Exception {
|
||||
String mapJson = mapper
|
||||
.writeValueAsString(Collections.unmodifiableMap(Collections.singletonMap("Key", "Value")));
|
||||
|
||||
JSONAssert.assertEquals(DEFAULT_MAP_JSON, mapJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeserialize() throws Exception {
|
||||
Map<String, String> map = mapper.readValue(DEFAULT_MAP_JSON,
|
||||
Collections.unmodifiableMap(Collections.emptyMap()).getClass());
|
||||
|
||||
assertThat(map).isNotNull()
|
||||
.isInstanceOf(Collections.unmodifiableMap(Collections.emptyMap()).getClass())
|
||||
.containsAllEntriesOf(Collections.singletonMap("Key", "Value"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.exc.MismatchedInputException;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
import tools.jackson.databind.node.ObjectNode;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
*/
|
||||
public class UserDeserializerTests extends AbstractMixinTests {
|
||||
|
||||
public static final String USER_PASSWORD = "\"1234\"";
|
||||
|
||||
// @formatter:off
|
||||
public static final String USER_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.core.userdetails.User\", "
|
||||
+ "\"username\": \"admin\","
|
||||
+ " \"password\": " + USER_PASSWORD + ", "
|
||||
+ "\"accountNonExpired\": true, "
|
||||
+ "\"accountNonLocked\": true, "
|
||||
+ "\"credentialsNonExpired\": true, "
|
||||
+ "\"enabled\": true, "
|
||||
+ "\"authorities\": " + SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON
|
||||
+ "}";
|
||||
// @formatter:on
|
||||
@Test
|
||||
public void serializeUserTest() throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
String userJson = this.mapper.writeValueAsString(user);
|
||||
JSONAssert.assertEquals(userWithPasswordJson(user.getPassword()), userJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeUserWithoutAuthority() throws JsonProcessingException, JSONException {
|
||||
User user = new User("admin", "1234", Collections.<GrantedAuthority>emptyList());
|
||||
String userJson = this.mapper.writeValueAsString(user);
|
||||
JSONAssert.assertEquals(userWithNoAuthoritiesJson(), userJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithNullPasswordEmptyAuthorityTest() throws IOException {
|
||||
String userJsonWithoutPasswordString = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,
|
||||
"[]");
|
||||
assertThatExceptionOfType(MismatchedInputException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(userJsonWithoutPasswordString, User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithNullPasswordNoAuthorityTest() throws Exception {
|
||||
String userJsonWithoutPasswordString = removeNode(userWithNoAuthoritiesJson(), this.mapper, "password");
|
||||
User user = this.mapper.readValue(userJsonWithoutPasswordString, User.class);
|
||||
assertThat(user).isNotNull();
|
||||
assertThat(user.getUsername()).isEqualTo("admin");
|
||||
assertThat(user.getPassword()).isNull();
|
||||
assertThat(user.getAuthorities()).isEmpty();
|
||||
assertThat(user.isEnabled()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithNoClassIdInAuthoritiesTest() throws Exception {
|
||||
String userJson = USER_JSON.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,
|
||||
"[{\"authority\": \"ROLE_USER\"}]");
|
||||
assertThatExceptionOfType(MismatchedInputException.class)
|
||||
.isThrownBy(() -> this.mapper.readValue(userJson, User.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUserWithClassIdInAuthoritiesTest() {
|
||||
User user = this.mapper.readValue(userJson(), User.class);
|
||||
assertThat(user).isNotNull();
|
||||
assertThat(user.getUsername()).isEqualTo("admin");
|
||||
assertThat(user.getPassword()).isEqualTo("1234");
|
||||
assertThat(user.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
private String removeNode(String json, JsonMapper mapper, String toRemove) throws Exception {
|
||||
ObjectNode node = mapper.createParser(json).readValueAsTree();
|
||||
node.remove(toRemove);
|
||||
String result = mapper.writeValueAsString(node);
|
||||
JSONAssert.assertNotEquals(json, result, false);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String userJson() {
|
||||
return USER_JSON;
|
||||
}
|
||||
|
||||
public static String userWithPasswordJson(String password) {
|
||||
return userJson().replaceAll(Pattern.quote(USER_PASSWORD), "\"" + password + "\"");
|
||||
}
|
||||
|
||||
public static String userWithNoAuthoritiesJson() {
|
||||
return userJson().replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_SET_JSON,
|
||||
SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_SET_JSON);
|
||||
}
|
||||
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.security.jackson;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonClassDescription;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Value;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Jitendra Singh
|
||||
* @author Greg Turnquist
|
||||
* @author Onur Kagan Ozcan
|
||||
* @since 4.2
|
||||
*/
|
||||
public class UsernamePasswordAuthenticationTokenMixinTests extends AbstractMixinTests {
|
||||
|
||||
private static final String AUTHENTICATED_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\","
|
||||
+ "\"principal\": " + UserDeserializerTests.USER_JSON + ", " + "\"credentials\": \"1234\", "
|
||||
+ "\"authenticated\": true, " + "\"details\": null, " + "\"authorities\": "
|
||||
+ SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON + "}";
|
||||
|
||||
public static final String AUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_JSON
|
||||
.replace(UserDeserializerTests.USER_JSON, "\"admin\"");
|
||||
|
||||
private static final String NON_USER_PRINCIPAL_JSON = "{"
|
||||
+ "\"@class\": \"org.springframework.security.jackson.UsernamePasswordAuthenticationTokenMixinTests$NonUserPrincipal\", "
|
||||
+ "\"username\": \"admin\"" + "}";
|
||||
|
||||
private static final String AUTHENTICATED_STRINGDETAILS_JSON = AUTHENTICATED_JSON.replace("\"details\": null, ",
|
||||
"\"details\": \"details\", ");
|
||||
|
||||
private static final String AUTHENTICATED_NON_USER_PRINCIPAL_JSON = AUTHENTICATED_JSON
|
||||
.replace(UserDeserializerTests.USER_JSON, NON_USER_PRINCIPAL_JSON)
|
||||
.replaceAll(UserDeserializerTests.USER_PASSWORD, "null")
|
||||
.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,
|
||||
SimpleGrantedAuthorityMixinTests.NO_AUTHORITIES_ARRAYLIST_JSON);
|
||||
|
||||
private static final String UNAUTHENTICATED_STRINGPRINCIPAL_JSON = AUTHENTICATED_STRINGPRINCIPAL_JSON
|
||||
.replace("\"authenticated\": true, ", "\"authenticated\": false, ")
|
||||
.replace(SimpleGrantedAuthorityMixinTests.AUTHORITIES_ARRAYLIST_JSON,
|
||||
SimpleGrantedAuthorityMixinTests.EMPTY_AUTHORITIES_ARRAYLIST_JSON);
|
||||
|
||||
@Test
|
||||
public void serializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated("admin",
|
||||
"1234");
|
||||
String serializedJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(UNAUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
User user = createDefaultUser();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.authenticated(user.getUsername(), user.getPassword(), user.getAuthorities());
|
||||
String serializedJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_STRINGPRINCIPAL_JSON, serializedJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeUnauthenticatedUsernamePasswordAuthenticationTokenMixinTest() {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(UNAUTHENTICATED_STRINGPRINCIPAL_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.isAuthenticated()).isEqualTo(false);
|
||||
assertThat(token.getAuthorities()).isNotNull().hasSize(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenMixinTest() {
|
||||
UsernamePasswordAuthenticationToken expectedToken = createToken();
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGPRINCIPAL_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.isAuthenticated()).isTrue();
|
||||
assertThat(token.getAuthorities()).isEqualTo(expectedToken.getAuthorities());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithUserTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
UsernamePasswordAuthenticationToken token = createToken();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithUserTest() throws IOException {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()
|
||||
.hasSize(1)
|
||||
.contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.isAuthenticated()).isEqualTo(true);
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinAfterEraseCredentialInvoked()
|
||||
throws JsonProcessingException, JSONException {
|
||||
UsernamePasswordAuthenticationToken token = createToken();
|
||||
token.eraseCredentials();
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_JSON.replaceAll(UserDeserializerTests.USER_PASSWORD, "null"), actualJson,
|
||||
true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializeAuthenticatedUsernamePasswordAuthenticationTokenMixinWithNonUserPrincipalTest()
|
||||
throws JsonProcessingException, JSONException {
|
||||
NonUserPrincipal principal = new NonUserPrincipal();
|
||||
principal.setUsername("admin");
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(principal, null,
|
||||
new ArrayList<>());
|
||||
String actualJson = this.mapper.writeValueAsString(token);
|
||||
JSONAssert.assertEquals(AUTHENTICATED_NON_USER_PRINCIPAL_JSON, actualJson, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithNonUserPrincipalTest()
|
||||
throws IOException {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_NON_USER_PRINCIPAL_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(NonUserPrincipal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deserializeAuthenticatedUsernamePasswordAuthenticationTokenWithDetailsTest() {
|
||||
UsernamePasswordAuthenticationToken token = this.mapper.readValue(AUTHENTICATED_STRINGDETAILS_JSON,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(token).isNotNull();
|
||||
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
|
||||
assertThat(((User) token.getPrincipal()).getAuthorities()).isNotNull()
|
||||
.hasSize(1)
|
||||
.contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.isAuthenticated()).isEqualTo(true);
|
||||
assertThat(token.getAuthorities()).hasSize(1).contains(new SimpleGrantedAuthority("ROLE_USER"));
|
||||
assertThat(token.getDetails()).isExactlyInstanceOf(String.class).isEqualTo("details");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializingThenDeserializingWithNoCredentialsOrDetailsShouldWork() {
|
||||
UsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated("Frodo",
|
||||
null);
|
||||
String serialized = this.mapper.writeValueAsString(original);
|
||||
UsernamePasswordAuthenticationToken deserialized = this.mapper.readValue(serialized,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(deserialized).isEqualTo(original);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializingThenDeserializingWithConfiguredJsontMapperShouldWork() {
|
||||
JsonMapper jsonMapper = this.mapper.rebuild()
|
||||
.changeDefaultPropertyInclusion((p) -> Value.construct(Include.NON_ABSENT, Include.NON_ABSENT))
|
||||
.build();
|
||||
|
||||
UsernamePasswordAuthenticationToken original = UsernamePasswordAuthenticationToken.unauthenticated("Frodo",
|
||||
null);
|
||||
String serialized = jsonMapper.writeValueAsString(original);
|
||||
UsernamePasswordAuthenticationToken deserialized = jsonMapper.readValue(serialized,
|
||||
UsernamePasswordAuthenticationToken.class);
|
||||
assertThat(deserialized).isEqualTo(original);
|
||||
}
|
||||
|
||||
private UsernamePasswordAuthenticationToken createToken() {
|
||||
User user = createDefaultUser();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.authenticated(user,
|
||||
user.getPassword(), user.getAuthorities());
|
||||
return token;
|
||||
}
|
||||
|
||||
@JsonClassDescription
|
||||
public static class NonUserPrincipal {
|
||||
|
||||
private String username;
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user