2525import org .apache .shiro .crypto .cipher .AesCipherService ;
2626import org .apache .shiro .crypto .cipher .ByteSourceBroker ;
2727import org .apache .shiro .crypto .cipher .CipherService ;
28+ import org .apache .shiro .lang .io .ClassResolvingObjectInputStream ;
2829import org .apache .shiro .lang .io .DefaultSerializer ;
2930import org .apache .shiro .lang .io .Serializer ;
3031import org .apache .shiro .lang .util .ByteSource ;
3132import org .apache .shiro .lang .util .ByteUtils ;
32- import org .apache .shiro .lang .util .ClassUtils ;
33+ import org .apache .shiro .lang .util .ClassUtils .ClassLoaderAccessor ;
34+ import org .apache .shiro .lang .util .ClassUtils .ExceptionIgnoringAccessor ;
3335import org .apache .shiro .subject .PrincipalCollection ;
3436import org .apache .shiro .subject .Subject ;
3537import org .apache .shiro .subject .SubjectContext ;
3638import org .slf4j .Logger ;
3739import org .slf4j .LoggerFactory ;
3840
41+ import java .io .IOException ;
42+ import java .io .InputStream ;
43+ import java .io .ObjectInputStream ;
44+ import java .io .Serial ;
45+ import java .io .Serializable ;
46+ import java .time .Instant ;
3947import java .util .function .Supplier ;
4048
4149/**
6674 * @since 0.9
6775 */
6876public abstract class AbstractRememberMeManager implements RememberMeManager {
77+ protected record RememberedIdentity (PrincipalCollection principals , Instant creationTime ) implements Serializable {
78+ @ Serial
79+ private static final long serialVersionUID = 1L ;
80+ }
6981
7082 /**
7183 * private inner log instance.
7284 */
7385 private static final Logger LOGGER = LoggerFactory .getLogger (AbstractRememberMeManager .class );
7486
87+ private static final ClassLoaderAccessor ADDITIONAL_CL_ACCESSOR = new ExceptionIgnoringAccessor () {
88+ @ Override
89+ protected ClassLoader doGetClassLoader () {
90+ return AbstractRememberMeManager .class .getClassLoader ();
91+ }
92+ };
93+
7594 /**
7695 * Serializer to use for converting PrincipalCollection instances to/from byte arrays
7796 */
78- private Serializer <PrincipalCollection > serializer = new DefaultSerializer <>();
97+ private Serializer <RememberedIdentity > serializer = new DefaultSerializer <>() {
98+ @ Override
99+ protected ObjectInputStream createObjectInputStream (InputStream inputStream ) throws IOException {
100+ return new ClassResolvingObjectInputStream (inputStream , ADDITIONAL_CL_ACCESSOR );
101+ }
102+ };
79103
80104 /**
81105 * Cipher to use for encrypting/decrypting serialized byte arrays for added security
@@ -121,7 +145,7 @@ public AbstractRememberMeManager(Supplier<byte[]> keySupplier) {
121145 * @return the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
122146 * persistent remember me storage.
123147 */
124- public Serializer <PrincipalCollection > getSerializer () {
148+ public Serializer <RememberedIdentity > getSerializer () {
125149 return serializer ;
126150 }
127151
@@ -134,7 +158,7 @@ public Serializer<PrincipalCollection> getSerializer() {
134158 * @param serializer the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances
135159 * for persistent remember me storage.
136160 */
137- public void setSerializer (Serializer <PrincipalCollection > serializer ) {
161+ public void setSerializer (Serializer <RememberedIdentity > serializer ) {
138162 this .serializer = serializer ;
139163 }
140164
@@ -352,14 +376,14 @@ protected void rememberIdentity(Subject subject, PrincipalCollection accountPrin
352376 /**
353377 * Converts the given principal collection the byte array that will be persisted to be 'remembered' later.
354378 * <p/>
355- * This implementation first {@link #serialize(org.apache.shiro.subject.PrincipalCollection ) serializes} the
379+ * This implementation first {@link #serialize(RememberedIdentity ) serializes} the
356380 * principals to a byte array and then {@link #encrypt(byte[]) encrypts} that byte array.
357381 *
358382 * @param principals the {@code PrincipalCollection} to convert to a byte array
359383 * @return the representative byte array to be persisted for remember me functionality.
360384 */
361385 protected byte [] convertPrincipalsToBytes (PrincipalCollection principals ) {
362- byte [] bytes = serialize (principals );
386+ byte [] bytes = serialize (new RememberedIdentity ( principals , now ()) );
363387 if (getCipherService () != null ) {
364388 bytes = encrypt (bytes );
365389 }
@@ -433,7 +457,28 @@ protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectCont
433457 if (getCipherService () != null ) {
434458 bytes = decrypt (bytes );
435459 }
436- return deserialize (bytes );
460+ RememberedIdentity remembered = deserialize (bytes );
461+ return checkExpiration (remembered );
462+ }
463+
464+ /**
465+ * Checks the given remembered identity for expiration. The default implementation does not perform any expiration
466+ * checks and simply returns the principals. Subclasses can override this method to perform expiration checks based on the
467+ * {@code creationTime} property of the {@code RememberedIdentity}
468+ * @param remembered identity
469+ * @return PrincipalCollection
470+ */
471+ protected PrincipalCollection checkExpiration (RememberedIdentity remembered ) {
472+ return remembered .principals ();
473+ }
474+
475+ /**
476+ * Returns the current time as an {@link Instant}. Subclasses can override this method to provide a different time,
477+ * such as for testing
478+ * @return the current time as an {@link Instant}
479+ */
480+ protected Instant now () {
481+ return Instant .now ();
437482 }
438483
439484 /**
@@ -508,13 +553,8 @@ protected byte[] decrypt(byte[] encrypted) {
508553 * @param principals the principal collection to serialize to a byte array
509554 * @return the serialized principal collection in the form of a byte array
510555 */
511- protected byte [] serialize (PrincipalCollection principals ) {
512- ClassUtils .setAdditionalClassLoader (AbstractRememberMeManager .class .getClassLoader ());
513- try {
514- return getSerializer ().serialize (principals );
515- } finally {
516- ClassUtils .removeAdditionalClassLoader ();
517- }
556+ protected byte [] serialize (RememberedIdentity principals ) {
557+ return getSerializer ().serialize (principals );
518558 }
519559
520560 /**
@@ -524,13 +564,8 @@ protected byte[] serialize(PrincipalCollection principals) {
524564 * @param serializedIdentity the previously serialized {@code PrincipalCollection} as a byte array
525565 * @return the deserialized (reconstituted) {@code PrincipalCollection}
526566 */
527- protected PrincipalCollection deserialize (byte [] serializedIdentity ) {
528- ClassUtils .setAdditionalClassLoader (AbstractRememberMeManager .class .getClassLoader ());
529- try {
530- return getSerializer ().deserialize (serializedIdentity );
531- } finally {
532- ClassUtils .removeAdditionalClassLoader ();
533- }
567+ protected RememberedIdentity deserialize (byte [] serializedIdentity ) {
568+ return getSerializer ().deserialize (serializedIdentity );
534569 }
535570
536571 /**
0 commit comments