Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

整合 Spring Security 和 Spring Session 反序列化时原因不明的 NullPointerException [BUG] #1286

Closed
insight720 opened this issue Mar 26, 2023 · 8 comments
Labels
bug Something isn't working fixed
Milestone

Comments

@insight720
Copy link

insight720 commented Mar 26, 2023

问题描述

整合 Spring Security 和 Spring Session,并使用 FastJson2 的 autoType 序列化 Session 信息到 Redis 中,反序列化时抛出原因不明的 java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "key" is null,堆栈信息显示异常是因为传入 ConcurrentHashMap 的 key 为 null,我没有找到 key 为 null 的原因。我查看了反序列化的类 SecurityContextImpl,它里面仅委托了一个 Authentication 接口的实现类,我使用的实现是 UsernamePasswordAuthenticationToken。将 UsernamePasswordAuthenticationToken 取出再序列化结果正常,但直接序列化 SecurityContextImpl 会爆 NPE。SecurityContextImpl 这个类比较简单,我在下面贴出源码。

环境信息

  • OS信息: Windows 11
  • JDK信息: Oracle JDK 17
  • SpringBoot版本信息:3.0.3
  • FastJson2版本信息:fastjson2-extension-spring6 2.0.25

重现步骤

如何操作可以重现该问题:

  1. 准备与环境信息相似的测试环境(初步测试使用 SpringBoot,SpringSecurity,FastJson2 即可)。
  2. 运行我给出的测试代码。
  3. 出现 NullPointerException 错误。
@SpringBootTest
public class SecurityApplicationTest {

    private final FastJsonConfig config = new FastJsonConfig();

    @Test
    public void json() throws Throwable {
        config.setReaderFeatures(JSONReader.Feature.FieldBased, JSONReader.Feature.SupportAutoType);
        config.setReaderFilters(new ContextAutoTypeBeforeHandler(new String[]{
                "org.springframework.security.core.context.SecurityContextImpl",
                "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
        }));
        String delegate = """
                {
                    "@type": "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
                    "authenticated": true,
                }
                """;
        String container = """
                {
                  "@type": "org.springframework.security.core.context.SecurityContextImpl",
                  "authentication": {
                    "@type": "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
                    "authenticated": true,
                  }
                }
                """;
        // 正常
        Object success = JSON.parseObject(delegate, Object.class, null, config.getReaderFilters(), config.getReaderFeatures());
        // java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "key" is null
        Object failure = JSON.parseObject(container, Object.class, null, config.getReaderFilters(), config.getReaderFeatures());
    }

}
public class SecurityContextImpl implements SecurityContext {

	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
        // 委托 UsernamePasswordAuthenticationToken
	private Authentication authentication;

	public SecurityContextImpl() {
	}

	public SecurityContextImpl(Authentication authentication) {
		this.authentication = authentication;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof SecurityContextImpl) {
			SecurityContextImpl other = (SecurityContextImpl) obj;
			if ((this.getAuthentication() == null) && (other.getAuthentication() == null)) {
				return true;
			}
			if ((this.getAuthentication() != null) && (other.getAuthentication() != null)
					&& this.getAuthentication().equals(other.getAuthentication())) {
				return true;
			}
		}
		return false;
	}

	@Override
	public Authentication getAuthentication() {
		return this.authentication;
	}

	@Override
	public int hashCode() {
		return ObjectUtils.nullSafeHashCode(this.authentication);
	}

	@Override
	public void setAuthentication(Authentication authentication) {
		this.authentication = authentication;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(getClass().getSimpleName()).append(" [");
		if (this.authentication == null) {
			sb.append("Null authentication");
		}
		else {
			sb.append("Authentication=").append(this.authentication);
		}
		sb.append("]");
		return sb.toString();
	}

}

期待的正确结果

可以直接反序列化 SecurityContextImpl ,而不需要取出 Authentication。

相关日志输出

*java.lang.NullPointerException: Cannot invoke "Object.hashCode()" because "key" is null

at java.base/java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:936)
at com.alibaba.fastjson2.reader.ObjectReaderBaseModule$ReaderAnnotationProcessor.getFieldInfo(ObjectReaderBaseModule.java:741)
at com.alibaba.fastjson2.reader.ObjectReaderBaseModule$ReaderAnnotationProcessor.getFieldInfo(ObjectReaderBaseModule.java:736)
at com.alibaba.fastjson2.reader.ObjectReaderProvider.getFieldInfo(ObjectReaderProvider.java:1024)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.createFieldReader(ObjectReaderCreator.java:1287)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.lambda$createFieldReaders$6(ObjectReaderCreator.java:1533)
at com.alibaba.fastjson2.util.BeanUtils.getters(BeanUtils.java:1010)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.createFieldReaders(ObjectReaderCreator.java:1530)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.createObjectReader(ObjectReaderCreator.java:928)
at com.alibaba.fastjson2.reader.ObjectReaderCreatorASM.createObjectReader(ObjectReaderCreatorASM.java:287)
at com.alibaba.fastjson2.reader.ObjectReaderProvider.getObjectReader(ObjectReaderProvider.java:974)
at com.alibaba.fastjson2.JSONReader.getObjectReader(JSONReader.java:246)
at com.alibaba.fastjson2.reader.FieldReaderObject.getObjectReader(FieldReaderObject.java:64)
at com.alibaba.fastjson2.reader.ORG_2_1_SecurityContextImpl.readObject(Unknown Source)
at com.alibaba.fastjson2.reader.ObjectReaderImplObject.readObject(ObjectReaderImplObject.java:125)
at com.alibaba.fastjson2.JSON.parseObject(JSON.java:726)
at pers.project.api.security.SecurityApplicationTest.json(SecurityApplicationTest.java:45)*
@insight720 insight720 added the bug Something isn't working label Mar 26, 2023
@wenshao wenshao added this to the 2.0.27 milestone Mar 26, 2023
@wenshao
Copy link
Member

wenshao commented Mar 26, 2023

https://oss.sonatype.org/content/repositories/snapshots/com/alibaba/fastjson2/fastjson2/2.0.27-SNAPSHOT/
问题已修复,请帮忙用2.0.27-SNAPSHOT版本验证

@wenshao wenshao added the fixed label Mar 26, 2023
@insight720
Copy link
Author

@wenshao 哪一个 jar 包

@wenshao
Copy link
Member

wenshao commented Mar 26, 2023

@insight720 最后一个

@insight720
Copy link
Author

我用了最后的 fastjson2-2.0.27-20230326.172629-40 jar 包,看 20230326.172629 这个时间它应该是在昨天修复之后发布的。问题和原来类似,报了另外一个 NPE,下面是报错日志和测试代码。

java.lang.NullPointerException: Cannot invoke "java.lang.Class.getInterfaces()" because "clazz" is null

at com.alibaba.fastjson2.util.TypeUtils.isProxy(TypeUtils.java:2955)
at com.alibaba.fastjson2.util.BeanUtils.declaredFields(BeanUtils.java:280)
at com.alibaba.fastjson2.reader.ObjectReaderBaseModule$ReaderAnnotationProcessor.getFieldInfo(ObjectReaderBaseModule.java:858)
at com.alibaba.fastjson2.reader.ObjectReaderBaseModule$ReaderAnnotationProcessor.getFieldInfo(ObjectReaderBaseModule.java:745)
at com.alibaba.fastjson2.reader.ObjectReaderProvider.getFieldInfo(ObjectReaderProvider.java:1024)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.createFieldReader(ObjectReaderCreator.java:1276)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.lambda$createFieldReaders$6(ObjectReaderCreator.java:1522)
at com.alibaba.fastjson2.util.BeanUtils.getters(BeanUtils.java:1022)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.createFieldReaders(ObjectReaderCreator.java:1519)
at com.alibaba.fastjson2.reader.ObjectReaderCreator.createObjectReader(ObjectReaderCreator.java:917)
at com.alibaba.fastjson2.reader.ObjectReaderCreatorASM.createObjectReader(ObjectReaderCreatorASM.java:258)
at com.alibaba.fastjson2.reader.ObjectReaderProvider.getObjectReader(ObjectReaderProvider.java:974)
at com.alibaba.fastjson2.JSONReader.getObjectReader(JSONReader.java:247)
at com.alibaba.fastjson2.reader.FieldReaderObject.getObjectReader(FieldReaderObject.java:64)
at com.alibaba.fastjson2.reader.ORG_2_1_SecurityContextImpl.readObject(Unknown Source)
at com.alibaba.fastjson2.reader.ObjectReaderImplObject.readObject(ObjectReaderImplObject.java:125)
at com.alibaba.fastjson2.JSON.parseObject(JSON.java:1208)
at pers.project.api.security.SecurityApplicationTest.json(SecurityApplicationTest.java:47)
public class SecurityApplicationTest {

    @Test
    public void json() throws Throwable {
        String delegate = """
                {
                    "@type": "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
                    "authenticated": true,
                }
                """;
        String container = """
                {
                  "@type": "org.springframework.security.core.context.SecurityContextImpl",
                  "authentication": {
                    "@type": "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
                    "authenticated": true,
                  }
                }
                """;
        ContextAutoTypeBeforeHandler contextAutoTypeBeforeHandler = new ContextAutoTypeBeforeHandler(new String[]{
                "org.springframework.security.core.context.SecurityContextImpl",
                "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
        });
        Object success = JSON.parseObject(delegate.getBytes(StandardCharsets.UTF_8), Object.class, contextAutoTypeBeforeHandler,
                JSONReader.Feature.FieldBased, JSONReader.Feature.SupportAutoType);
        Object failure = JSON.parseObject(container.getBytes(StandardCharsets.UTF_8), Object.class, contextAutoTypeBeforeHandler,
                JSONReader.Feature.FieldBased, JSONReader.Feature.SupportAutoType);
    }

}

@insight720
Copy link
Author

@wenshao 不从 Maven 导入的 jar 好像 Debug 不了,我也看不出来哪错了,就是 Class 对象为 null。

@wenshao
Copy link
Member

wenshao commented Mar 27, 2023

https://oss.sonatype.org/content/repositories/snapshots/com/alibaba/fastjson2/fastjson2/2.0.27-SNAPSHOT/
问题已修复,请帮忙更新下快照用2.0.27-SNAPSHOT版本验证

@insight720
Copy link
Author

@wenshao 验证通过,感谢修复。

@wenshao
Copy link
Member

wenshao commented Apr 8, 2023

https://github.com/alibaba/fastjson2/releases/tag/2.0.27
问题已修复,请用新版本

@wenshao wenshao closed this as completed Apr 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fixed
Projects
None yet
Development

No branches or pull requests

2 participants