spring-projects / spring-security-kerberos

Spring Security Kerberos
https://spring.io/projects/spring-security-kerberos
182 stars 226 forks source link

NotSerializableException on JaasSubjectHolder #178

Closed ogoedhart closed 4 months ago

ogoedhart commented 1 year ago

Summary With version 2.0.0 a serialization exception is introduced.

Explanation The org.springframework.security.kerberos.authentication.KerberosServiceRequestToken extends the AbstractAuthenticationTokenwhich implements Authentication(extends Serializable).

The KerberosServiceRequestTokenholds an org.springframework.security.kerberos.authentication.JaasSubjectHolder which is not Serializable (and the field is not transient). And therefore (in our case) spring-session-jdbc throws a NotSerializableException while trying to serialize the object.

Spring session jdbc tries to store the SecurityContextImpl which extends Serializable(refer to SecurityContextImpl docs)

The SecurityContextImplholds a org.springframework.security.core.Authentication object (in our case the KerberosServiceRequestToken.

Suggestion The field private JaasSubjectHolder jaasSubjectHolder on KerberosServiceRequestTokenshould be transient or the Object JaasSubjectHolder should extends Serializable. I'm not sure about the actual solution and implication on the rest.

Martj89 commented 1 year ago

Have the exact same problem with Spring Boot 3.1.1/Security;6.1.1/Kerberos:2.0.0

rstribrn commented 1 year ago

Same problem here:

Describe the bug spring-security-kerberos-core, 2.0.0 introduced org.springframework.security.kerberos.authentication.JaasSubjectHolder.

This object wasn't in spring-security-kerberos-core, 1.0.1-RELEASE version, which used other object:

Since 2.0.0, KerberosAuthenticationProvider started using JaasSubjectHolder.

Therefore, if Spring Security session management stores object into session, it has to be Serializable. Especially, if session is backed by Redis, for example.

To Reproduce Use: .sessionManagement(management -> management .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))

Expected behavior No exception.

Sample No sample provided, quite obvious reasoning.

Stacktrace

org.springframework.data.redis.serializer.SerializationException: Cannot serialize
        at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:96)
        at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:186)
        at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:161)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.springframework.data.redis.core.BoundOperationsProxyFactory$BoundOperationsMethodInterceptor.doInvoke(BoundOperationsProxyFactory.java:183)
        at org.springframework.data.redis.core.BoundOperationsProxyFactory$BoundOperationsMethodInterceptor.invoke(BoundOperationsProxyFactory.java:153)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
        at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:72)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:244)
        at jdk.proxy3/jdk.proxy3.$Proxy186.putAll(Unknown Source)
        at org.springframework.session.data.redis.RedisIndexedSessionRepository$RedisSession.saveDelta(RedisIndexedSessionRepository.java:852)
        at org.springframework.session.data.redis.RedisIndexedSessionRepository$RedisSession.save(RedisIndexedSessionRepository.java:840)
        at org.springframework.session.data.redis.RedisIndexedSessionRepository.save(RedisIndexedSessionRepository.java:478)
        at org.springframework.session.data.redis.RedisIndexedSessionRepository.save(RedisIndexedSessionRepository.java:258)
        at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:228)
        at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:146)
        at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149)
        at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:642)
        at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:410)
        at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:340)
        at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:277)
        at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:358)
        at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:222)
        at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:304)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:149)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1740)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer
        at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:64)
        at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:33)
        at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:94)
        ... 47 common frames omitted
Caused by: java.io.NotSerializableException: org.springframework.security.kerberos.authentication.JaasSubjectHolder
        at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1187)
        at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
        at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
        at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
        at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
        at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1572)
        at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1529)
        at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1438)
        at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
        at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:350)
        at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:46)
        at org.springframework.core.serializer.Serializer.serializeToByteArray(Serializer.java:56)
        at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:60)
        ... 49 common frames omitted
rstribrn commented 1 year ago

I've created a pull request with fix proposal. Hopefully it will work in all scenarios (I tried comparing all changes between 1.0.1-RELEASE and 2.0.0, and added also serialVersionUID to another class where it was missing).

rstribrn commented 1 year ago

BTW:

Until its merged, this is working for me:

        <dependency>
            <groupId>org.springframework.security.kerberos</groupId>
            <artifactId>spring-security-kerberos-client</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!-- Hold on 1.0.1-RELEASE, version 2.0.0 waits for:
             - https://github.com/spring-projects/spring-security-kerberos/issues/178
        -->
        <dependency>
            <groupId>org.springframework.security.kerberos</groupId>
            <artifactId>spring-security-kerberos-core</artifactId>
            <version>1.0.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.kerberos</groupId>
            <artifactId>spring-security-kerberos-web</artifactId>
            <version>2.0.0</version>
        </dependency>