Error "java.io.NotSerializableException" on Java class "org.apereo.cas.authentication.MyCustomAuthenticationHandler" after navigating to CAS 6.6.14 custom page

56 views
Skip to first unread message

Luís Costa

unread,
Apr 25, 2024, 1:14:16 AM4/25/24
to CAS Community
Hello CAS Community,


We are using CAS 6.6.14 for authentication against database and LDAP.


So far our use of CAS 66x is ok, but recently we've encountered a scenario a little bit different, that is causing us problems.


The scenario is:

1) after a successeful authentication, if our custom code concludes the password is expired,
   we customized the spring web flow "login flow" to move to a custom page
   with message "your password is expired, you should proceed to Recover Password"

2) once we click on a "Recover Password" button,
   which should provoke navigation to another custom page,
   the following error occurs:

2024-04-16 12:49:12,889 [http-nio-8443-exec-4] WARN : org.apereo.cas.authentication.MyCustomAuthenticationHandler
java.io.NotSerializableException: org.apereo.cas.authentication.MyCustomAuthenticationHandler
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.writeObject(ObjectOutputStream.java:350)
at java.base/java.util.TreeMap.writeObject(TreeMap.java:2758)
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 java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
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.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 java.base/java.util.LinkedHashMap.internalWriteEntries(LinkedHashMap.java:334)
at java.base/java.util.HashMap.writeObject(HashMap.java:1497)
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 java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
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.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 java.base/java.util.HashMap.internalWriteEntries(HashMap.java:1944)
at java.base/java.util.HashMap.writeObject(HashMap.java:1497)
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 java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
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.defaultWriteObject(ObjectOutputStream.java:443)
at org.springframework.webflow.core.collection.LocalAttributeMap.writeObject(LocalAttributeMap.java:333)
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 java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1070)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1516)
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.apereo.cas.web.flow.executor.EncryptedTranscoder.writeObjectToOutputStream(EncryptedTranscoder.java:89)
at org.apereo.cas.web.flow.executor.EncryptedTranscoder.encode(EncryptedTranscoder.java:60)
at org.apereo.cas.web.flow.executor.ClientFlowExecutionRepository.getKey(ClientFlowExecutionRepository.java:97)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.assignKey(FlowExecutionImpl.java:419)
    (...)


It seems the problemas cause is, the following CAS authentication class,
org.apereo.cas.authentication.DefaultAuthentication
(which contains a string reference to the successful authentication handler),
is beeing store in the flow variable "flowExecutionKey", which in Thymeleaf pages is in,
"<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>",
and then, after submiting our custom page,
this flowExecutionKey is deserialized, including the refered class DefaultAuthentication.

We found what we think is a temporary fix for this problem,
which is making our "org.apereo.cas.authentication.MyCustomAuthenticationHandler" class serializable,
but this involves things like having to create new dummy parent class, not serializable, with an explicit default constructor,
(because we can't create a default constructor on our class current parent class,
 which is org.apereo.cas.adaptors.jdbc.QueryAndEncodeDatabaseAuthenticationHandler)
and having to make some fields transient, to excluded them from serialization.

It's true that some CAS authentication related classes are serializable, like:
-org.apereo.cas.authentication.DefaultAuthentication
and
-org.apereo.cas.authentication.AuthenticationHandlerExecutionResult

But on the other hand, none of the
"org.apereo.cas.authentication.AuthenticationHandler" child classes are serializable,
and also, the AuthenticationHandlerExecutionResult class only contains a "String handlerName"
(name of the authentication handler that successfully authenticated a credential),
not an AuthenticationHandler interface implementation class attribute.


We couldn't find any information about this problem in the following sources:
- CAS 6 official site, https://apereo.github.io/cas/6.6.x/
- CAS community, https://groups.google.com/a/apereo.org/g/cas-user?pli=1
- Misagh Moayyed "Fawnoos blog", https://fawnoos.com/blog/



Did anybody had this problem?


Any advice or hint on what's the best solution for this problem?



Kind regards,


Luis Costa

Luís Costa

unread,
May 10, 2024, 5:40:05 AM5/10/24
to CAS Community, Luís Costa
Hello,


Did anybody had a similar problem?


Kind regards,

Luis Costa
Reply all
Reply to author
Forward
0 new messages