Coverage Report - org.apache.shiro.mgt.AbstractRememberMeManager
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractRememberMeManager
25%
19/74
12%
3/24
1.448
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.shiro.mgt;
 20  
 
 21  
 import org.apache.shiro.authc.AuthenticationException;
 22  
 import org.apache.shiro.authc.AuthenticationInfo;
 23  
 import org.apache.shiro.authc.AuthenticationToken;
 24  
 import org.apache.shiro.authc.RememberMeAuthenticationToken;
 25  
 import org.apache.shiro.codec.Base64;
 26  
 import org.apache.shiro.crypto.AesCipherService;
 27  
 import org.apache.shiro.crypto.CipherService;
 28  
 import org.apache.shiro.io.DefaultSerializer;
 29  
 import org.apache.shiro.io.Serializer;
 30  
 import org.apache.shiro.subject.PrincipalCollection;
 31  
 import org.apache.shiro.subject.Subject;
 32  
 import org.apache.shiro.subject.SubjectContext;
 33  
 import org.apache.shiro.util.ByteSource;
 34  
 import org.slf4j.Logger;
 35  
 import org.slf4j.LoggerFactory;
 36  
 
 37  
 /**
 38  
  * Abstract implementation of the {@code RememberMeManager} interface that handles
 39  
  * {@link #setSerializer(org.apache.shiro.io.Serializer) serialization} and
 40  
  * {@link #setCipherService encryption} of the remembered user identity.
 41  
  * <p/>
 42  
  * The remembered identity storage location and details are left to subclasses.
 43  
  * <h2>Default encryption key</h2>
 44  
  * This implementation uses an {@link AesCipherService AesCipherService} for strong encryption by default.  It also
 45  
  * uses a default generated symmetric key to both encrypt and decrypt data.  As AES is a symmetric cipher, the same
 46  
  * {@code key} is used to both encrypt and decrypt data, BUT NOTE:
 47  
  * <p/>
 48  
  * Because Shiro is an open-source project, if anyone knew that you were using Shiro's default
 49  
  * {@code key}, they could download/view the source, and with enough effort, reconstruct the {@code key}
 50  
  * and decode encrypted data at will.
 51  
  * <p/>
 52  
  * Of course, this key is only really used to encrypt the remembered {@code PrincipalCollection} which is typically
 53  
  * a user id or username.  So if you do not consider that sensitive information, and you think the default key still
 54  
  * makes things 'sufficiently difficult', then you can ignore this issue.
 55  
  * <p/>
 56  
  * However, if you do feel this constitutes sensitive information, it is recommended that you provide your own
 57  
  * {@code key} via the {@link #setCipherKey setCipherKey} method to a key known only to your application,
 58  
  * guaranteeing that no third party can decrypt your data.  You can generate your own key by calling the
 59  
  * {@code CipherService}'s {@link org.apache.shiro.crypto.AesCipherService#generateNewKey() generateNewKey} method
 60  
  * and using that result as the {@link #setCipherKey cipherKey} configuration attribute.
 61  
  *
 62  
  * @since 0.9
 63  
  */
 64  
 public abstract class AbstractRememberMeManager implements RememberMeManager {
 65  
 
 66  
     /**
 67  
      * private inner log instance.
 68  
      */
 69  1
     private static final Logger log = LoggerFactory.getLogger(AbstractRememberMeManager.class);
 70  
 
 71  
     /**
 72  
      * The following Base64 string was generated by auto-generating an AES Key:
 73  
      * <pre>
 74  
      * AesCipherService aes = new AesCipherService();
 75  
      * byte[] key = aes.generateNewKey().getEncoded();
 76  
      * String base64 = Base64.encodeToString(key);
 77  
      * </pre>
 78  
      * The value of 'base64' was copied-n-pasted here:
 79  
      */
 80  1
     private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
 81  
 
 82  
     /**
 83  
      * Serializer to use for converting PrincipalCollection instances to/from byte arrays
 84  
      */
 85  
     private Serializer<PrincipalCollection> serializer;
 86  
 
 87  
     /**
 88  
      * Cipher to use for encrypting/decrypting serialized byte arrays for added security
 89  
      */
 90  
     private CipherService cipherService;
 91  
 
 92  
     /**
 93  
      * Cipher encryption key to use with the Cipher when encrypting data
 94  
      */
 95  
     private byte[] encryptionCipherKey;
 96  
 
 97  
     /**
 98  
      * Cipher decryption key to use with the Cipher when decrypting data
 99  
      */
 100  
     private byte[] decryptionCipherKey;
 101  
 
 102  
     /**
 103  
      * Default constructor that initializes a {@link DefaultSerializer} as the {@link #getSerializer() serializer} and
 104  
      * an {@link AesCipherService} as the {@link #getCipherService() cipherService}.
 105  
      */
 106  2
     public AbstractRememberMeManager() {
 107  2
         this.serializer = new DefaultSerializer<PrincipalCollection>();
 108  2
         this.cipherService = new AesCipherService();
 109  2
         setCipherKey(DEFAULT_CIPHER_KEY_BYTES);
 110  2
     }
 111  
 
 112  
     /**
 113  
      * Returns the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
 114  
      * persistent remember me storage.
 115  
      * <p/>
 116  
      * Unless overridden by the {@link #setSerializer} method, the default instance is a
 117  
      * {@link org.apache.shiro.io.DefaultSerializer}.
 118  
      *
 119  
      * @return the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
 120  
      *         persistent remember me storage.
 121  
      */
 122  
     public Serializer<PrincipalCollection> getSerializer() {
 123  0
         return serializer;
 124  
     }
 125  
 
 126  
     /**
 127  
      * Sets the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
 128  
      * persistent remember me storage.
 129  
      * <p/>
 130  
      * Unless overridden by this method, the default instance is a {@link DefaultSerializer}.
 131  
      *
 132  
      * @param serializer the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances
 133  
      *                   for persistent remember me storage.
 134  
      */
 135  
     public void setSerializer(Serializer<PrincipalCollection> serializer) {
 136  0
         this.serializer = serializer;
 137  0
     }
 138  
 
 139  
     /**
 140  
      * Returns the {@code CipherService} to use for encrypting and decrypting serialized identity data to prevent easy
 141  
      * inspection of Subject identity data.
 142  
      * <p/>
 143  
      * Unless overridden by the {@link #setCipherService} method, the default instance is an {@link AesCipherService}.
 144  
      *
 145  
      * @return the {@code Cipher} to use for encrypting and decrypting serialized identity data to prevent easy
 146  
      *         inspection of Subject identity data
 147  
      */
 148  
     public CipherService getCipherService() {
 149  0
         return cipherService;
 150  
     }
 151  
 
 152  
     /**
 153  
      * Sets the {@code CipherService} to use for encrypting and decrypting serialized identity data to prevent easy
 154  
      * inspection of Subject identity data.
 155  
      * <p/>
 156  
      * If the CipherService is a symmetric CipherService (using the same key for both encryption and decryption), you
 157  
      * should set your key via the {@link #setCipherKey(byte[])} method.
 158  
      * <p/>
 159  
      * If the CipherService is an asymmetric CipherService (different keys for encryption and decryption, such as
 160  
      * public/private key pairs), you should set your encryption and decryption key via the respective
 161  
      * {@link #setEncryptionCipherKey(byte[])} and {@link #setDecryptionCipherKey(byte[])} methods.
 162  
      * <p/>
 163  
      * <b>N.B.</b> Unless overridden by this method, the default CipherService instance is an
 164  
      * {@link AesCipherService}.  This {@code RememberMeManager} implementation already has a configured symmetric key
 165  
      * to use for encryption and decryption, but it is recommended to provide your own for added security.  See the
 166  
      * class-level JavaDoc for more information and why it might be good to provide your own.
 167  
      *
 168  
      * @param cipherService the {@code CipherService} to use for encrypting and decrypting serialized identity data to
 169  
      *                      prevent easy inspection of Subject identity data.
 170  
      */
 171  
     public void setCipherService(CipherService cipherService) {
 172  0
         this.cipherService = cipherService;
 173  0
     }
 174  
 
 175  
     /**
 176  
      * Returns the cipher key to use for encryption operations.
 177  
      *
 178  
      * @return the cipher key to use for encryption operations.
 179  
      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
 180  
      */
 181  
     public byte[] getEncryptionCipherKey() {
 182  0
         return encryptionCipherKey;
 183  
     }
 184  
 
 185  
     /**
 186  
      * Sets the encryption key to use for encryption operations.
 187  
      *
 188  
      * @param encryptionCipherKey the encryption key to use for encryption operations.
 189  
      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
 190  
      */
 191  
     public void setEncryptionCipherKey(byte[] encryptionCipherKey) {
 192  2
         this.encryptionCipherKey = encryptionCipherKey;
 193  2
     }
 194  
 
 195  
     /**
 196  
      * Returns the decryption cipher key to use for decryption operations.
 197  
      *
 198  
      * @return the cipher key to use for decryption operations.
 199  
      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
 200  
      */
 201  
     public byte[] getDecryptionCipherKey() {
 202  0
         return decryptionCipherKey;
 203  
     }
 204  
 
 205  
     /**
 206  
      * Sets the decryption key to use for decryption operations.
 207  
      *
 208  
      * @param decryptionCipherKey the decryption key to use for decryption operations.
 209  
      * @see #setCipherService for a description of the various {@code get/set*Key} methods.
 210  
      */
 211  
     public void setDecryptionCipherKey(byte[] decryptionCipherKey) {
 212  2
         this.decryptionCipherKey = decryptionCipherKey;
 213  2
     }
 214  
 
 215  
     /**
 216  
      * Convenience method that returns the cipher key to use for <em>both</em> encryption and decryption.
 217  
      * <p/>
 218  
      * <b>N.B.</b> This method can only be called if the underlying {@link #getCipherService() cipherService} is a symmetric
 219  
      * CipherService which by definition uses the same key for both encryption and decryption.  If using an asymmetric
 220  
      * CipherService public/private key pair, you cannot use this method, and should instead use the
 221  
      * {@link #getEncryptionCipherKey()} and {@link #getDecryptionCipherKey()} methods individually.
 222  
      * <p/>
 223  
      * The default {@link AesCipherService} instance is a symmetric cipher service, so this method can be used if you are
 224  
      * using the default.
 225  
      *
 226  
      * @return the symmetric cipher key used for both encryption and decryption.
 227  
      */
 228  
     public byte[] getCipherKey() {
 229  
         //Since this method should only be used with symmetric ciphers
 230  
         //(where the enc and dec keys are the same), either is fine, just return one of them:
 231  0
         return getEncryptionCipherKey();
 232  
     }
 233  
 
 234  
     /**
 235  
      * Convenience method that sets the cipher key to use for <em>both</em> encryption and decryption.
 236  
      * <p/>
 237  
      * <b>N.B.</b> This method can only be called if the underlying {@link #getCipherService() cipherService} is a
 238  
      * symmetric CipherService?which by definition uses the same key for both encryption and decryption.  If using an
 239  
      * asymmetric CipherService?(such as a public/private key pair), you cannot use this method, and should instead use
 240  
      * the {@link #setEncryptionCipherKey(byte[])} and {@link #setDecryptionCipherKey(byte[])} methods individually.
 241  
      * <p/>
 242  
      * The default {@link AesCipherService} instance is a symmetric CipherService, so this method can be used if you
 243  
      * are using the default.
 244  
      *
 245  
      * @param cipherKey the symmetric cipher key to use for both encryption and decryption.
 246  
      */
 247  
     public void setCipherKey(byte[] cipherKey) {
 248  
         //Since this method should only be used in symmetric ciphers
 249  
         //(where the enc and dec keys are the same), set it on both:
 250  2
         setEncryptionCipherKey(cipherKey);
 251  2
         setDecryptionCipherKey(cipherKey);
 252  2
     }
 253  
 
 254  
     /**
 255  
      * Forgets (removes) any remembered identity data for the specified {@link Subject} instance.
 256  
      *
 257  
      * @param subject the subject instance for which identity data should be forgotten from the underlying persistence
 258  
      *                mechanism.
 259  
      */
 260  
     protected abstract void forgetIdentity(Subject subject);
 261  
 
 262  
     /**
 263  
      * Determines whether or not remember me services should be performed for the specified token.  This method returns
 264  
      * {@code true} iff:
 265  
      * <ol>
 266  
      * <li>The token is not {@code null} and</li>
 267  
      * <li>The token is an {@code instanceof} {@link RememberMeAuthenticationToken} and</li>
 268  
      * <li>{@code token}.{@link org.apache.shiro.authc.RememberMeAuthenticationToken#isRememberMe() isRememberMe()} is
 269  
      * {@code true}</li>
 270  
      * </ol>
 271  
      *
 272  
      * @param token the authentication token submitted during the successful authentication attempt.
 273  
      * @return true if remember me services should be performed as a result of the successful authentication attempt.
 274  
      */
 275  
     protected boolean isRememberMe(AuthenticationToken token) {
 276  0
         return token != null && (token instanceof RememberMeAuthenticationToken) &&
 277  
                 ((RememberMeAuthenticationToken) token).isRememberMe();
 278  
     }
 279  
 
 280  
     /**
 281  
      * Reacts to the successful login attempt by first always {@link #forgetIdentity(Subject) forgetting} any previously
 282  
      * stored identity.  Then if the {@code token}
 283  
      * {@link #isRememberMe(org.apache.shiro.authc.AuthenticationToken) is a RememberMe} token, the associated identity
 284  
      * will be {@link #rememberIdentity(org.apache.shiro.subject.Subject, org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) remembered}
 285  
      * for later retrieval during a new user session.
 286  
      *
 287  
      * @param subject the subject for which the principals are being remembered.
 288  
      * @param token   the token that resulted in a successful authentication attempt.
 289  
      * @param info    the authentication info resulting from the successful authentication attempt.
 290  
      */
 291  
     public void onSuccessfulLogin(Subject subject, AuthenticationToken token, AuthenticationInfo info) {
 292  
         //always clear any previous identity:
 293  0
         forgetIdentity(subject);
 294  
 
 295  
         //now save the new identity:
 296  0
         if (isRememberMe(token)) {
 297  0
             rememberIdentity(subject, token, info);
 298  
         } else {
 299  0
             if (log.isDebugEnabled()) {
 300  0
                 log.debug("AuthenticationToken did not indicate RememberMe is requested.  " +
 301  
                         "RememberMe functionality will not be executed for corresponding account.");
 302  
             }
 303  
         }
 304  0
     }
 305  
 
 306  
     /**
 307  
      * Remembers a subject-unique identity for retrieval later.  This implementation first
 308  
      * {@link #getIdentityToRemember resolves} the exact
 309  
      * {@link PrincipalCollection principals} to remember.  It then remembers the principals by calling
 310  
      * {@link #rememberIdentity(org.apache.shiro.subject.Subject, org.apache.shiro.subject.PrincipalCollection)}.
 311  
      * <p/>
 312  
      * This implementation ignores the {@link AuthenticationToken} argument, but it is available to subclasses if
 313  
      * necessary for custom logic.
 314  
      *
 315  
      * @param subject   the subject for which the principals are being remembered.
 316  
      * @param token     the token that resulted in a successful authentication attempt.
 317  
      * @param authcInfo the authentication info resulting from the successful authentication attempt.
 318  
      */
 319  
     public void rememberIdentity(Subject subject, AuthenticationToken token, AuthenticationInfo authcInfo) {
 320  0
         PrincipalCollection principals = getIdentityToRemember(subject, authcInfo);
 321  0
         rememberIdentity(subject, principals);
 322  0
     }
 323  
 
 324  
     /**
 325  
      * Returns {@code info}.{@link org.apache.shiro.authc.AuthenticationInfo#getPrincipals() getPrincipals()} and
 326  
      * ignores the {@link Subject} argument.
 327  
      *
 328  
      * @param subject the subject for which the principals are being remembered.
 329  
      * @param info    the authentication info resulting from the successful authentication attempt.
 330  
      * @return the {@code PrincipalCollection} to remember.
 331  
      */
 332  
     protected PrincipalCollection getIdentityToRemember(Subject subject, AuthenticationInfo info) {
 333  0
         return info.getPrincipals();
 334  
     }
 335  
 
 336  
     /**
 337  
      * Remembers the specified account principals by first
 338  
      * {@link #convertPrincipalsToBytes(org.apache.shiro.subject.PrincipalCollection) converting} them to a byte
 339  
      * array and then {@link #rememberSerializedIdentity(org.apache.shiro.subject.Subject, byte[]) remembers} that
 340  
      * byte array.
 341  
      *
 342  
      * @param subject           the subject for which the principals are being remembered.
 343  
      * @param accountPrincipals the principals to remember for retrieval later.
 344  
      */
 345  
     protected void rememberIdentity(Subject subject, PrincipalCollection accountPrincipals) {
 346  0
         byte[] bytes = convertPrincipalsToBytes(accountPrincipals);
 347  0
         rememberSerializedIdentity(subject, bytes);
 348  0
     }
 349  
 
 350  
     /**
 351  
      * Converts the given principal collection the byte array that will be persisted to be 'remembered' later.
 352  
      * <p/>
 353  
      * This implementation first {@link #serialize(org.apache.shiro.subject.PrincipalCollection) serializes} the
 354  
      * principals to a byte array and then {@link #encrypt(byte[]) encrypts} that byte array.
 355  
      *
 356  
      * @param principals the {@code PrincipalCollection} to convert to a byte array
 357  
      * @return the representative byte array to be persisted for remember me functionality.
 358  
      */
 359  
     protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
 360  0
         byte[] bytes = serialize(principals);
 361  0
         if (getCipherService() != null) {
 362  0
             bytes = encrypt(bytes);
 363  
         }
 364  0
         return bytes;
 365  
     }
 366  
 
 367  
     /**
 368  
      * Persists the identity bytes to a persistent store for retrieval later via the
 369  
      * {@link #getRememberedSerializedIdentity(SubjectContext)} method.
 370  
      *
 371  
      * @param subject    the Subject for which the identity is being serialized.
 372  
      * @param serialized the serialized bytes to be persisted.
 373  
      */
 374  
     protected abstract void rememberSerializedIdentity(Subject subject, byte[] serialized);
 375  
 
 376  
     /**
 377  
      * Implements the interface method by first {@link #getRememberedSerializedIdentity(SubjectContext) acquiring}
 378  
      * the remembered serialized byte array.  Then it {@link #convertBytesToPrincipals(byte[], SubjectContext) converts}
 379  
      * them and returns the re-constituted {@link PrincipalCollection}.  If no remembered principals could be
 380  
      * obtained, {@code null} is returned.
 381  
      * <p/>
 382  
      * If any exceptions are thrown, the {@link #onRememberedPrincipalFailure(RuntimeException, SubjectContext)} method
 383  
      * is called to allow any necessary post-processing (such as immediately removing any previously remembered
 384  
      * values for safety).
 385  
      *
 386  
      * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
 387  
      *                       is being used to construct a {@link Subject} instance.
 388  
      * @return the remembered principals or {@code null} if none could be acquired.
 389  
      */
 390  
     public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
 391  2
         PrincipalCollection principals = null;
 392  
         try {
 393  2
             byte[] bytes = getRememberedSerializedIdentity(subjectContext);
 394  
             //SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
 395  2
             if (bytes != null && bytes.length > 0) {
 396  0
                 principals = convertBytesToPrincipals(bytes, subjectContext);
 397  
             }
 398  0
         } catch (RuntimeException re) {
 399  0
             principals = onRememberedPrincipalFailure(re, subjectContext);
 400  2
         }
 401  
 
 402  2
         return principals;
 403  
     }
 404  
 
 405  
     /**
 406  
      * Based on the given subject context data, retrieves the previously persisted serialized identity, or
 407  
      * {@code null} if there is no available data.  The context map is usually populated by a {@link Subject.Builder}
 408  
      * implementation.  See the {@link SubjectFactory} class constants for Shiro's known map keys.
 409  
      *
 410  
      * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
 411  
      *                       is being used to construct a {@link Subject} instance.  To be used to assist with data
 412  
      *                       lookup.
 413  
      * @return the previously persisted serialized identity, or {@code null} if there is no available data for the
 414  
      *         Subject.
 415  
      */
 416  
     protected abstract byte[] getRememberedSerializedIdentity(SubjectContext subjectContext);
 417  
 
 418  
     /**
 419  
      * If a {@link #getCipherService() cipherService} is available, it will be used to first decrypt the byte array.
 420  
      * Then the bytes are then {@link #deserialize(byte[]) deserialized} and then returned.
 421  
      *
 422  
      * @param bytes          the bytes to decrypt if necessary and then deserialize.
 423  
      * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
 424  
      *                       is being used to construct a {@link Subject} instance.
 425  
      * @return the de-serialized and possibly decrypted principals
 426  
      */
 427  
     protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
 428  0
         if (getCipherService() != null) {
 429  0
             bytes = decrypt(bytes);
 430  
         }
 431  0
         return deserialize(bytes);
 432  
     }
 433  
 
 434  
     /**
 435  
      * Called when an exception is thrown while trying to retrieve principals.  The default implementation logs a
 436  
      * debug message and forgets ('unremembers') the problem identity by calling
 437  
      * {@link #forgetIdentity(SubjectContext) forgetIdentity(context)} and then immediately re-throws the
 438  
      * exception to allow the calling component to react accordingly.
 439  
      * <p/>
 440  
      * This method implementation never returns an
 441  
      * object - it always rethrows, but can be overridden by subclasses for custom handling behavior.
 442  
      * <p/>
 443  
      * This most commonly would be called when an encryption key is updated and old principals are retrieved that have
 444  
      * been encrypted with the previous key.
 445  
      *
 446  
      * @param e       the exception that was thrown.
 447  
      * @param context the contextual data, usually provided by a {@link Subject.Builder} implementation, that
 448  
      *                is being used to construct a {@link Subject} instance.
 449  
      * @return nothing - the original {@code RuntimeException} is propagated in all cases.
 450  
      */
 451  
     protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, SubjectContext context) {
 452  0
         if (log.isDebugEnabled()) {
 453  0
             log.debug("There was a failure while trying to retrieve remembered principals.  This could be due to a " +
 454  
                     "configuration problem or corrupted principals.  This could also be due to a recently " +
 455  
                     "changed encryption key.  The remembered identity will be forgotten and not used for this " +
 456  
                     "request.", e);
 457  
         }
 458  0
         forgetIdentity(context);
 459  
         //propagate - security manager implementation will handle and warn appropriately
 460  0
         throw e;
 461  
     }
 462  
 
 463  
     /**
 464  
      * Encrypts the byte array by using the configured {@link #getCipherService() cipherService}.
 465  
      *
 466  
      * @param serialized the serialized object byte array to be encrypted
 467  
      * @return an encrypted byte array returned by the configured {@link #getCipherService () cipher}.
 468  
      */
 469  
     protected byte[] encrypt(byte[] serialized) {
 470  0
         byte[] value = serialized;
 471  0
         CipherService cipherService = getCipherService();
 472  0
         if (cipherService != null) {
 473  0
             ByteSource byteSource = cipherService.encrypt(serialized, getEncryptionCipherKey());
 474  0
             value = byteSource.getBytes();
 475  
         }
 476  0
         return value;
 477  
     }
 478  
 
 479  
     /**
 480  
      * Decrypts the byte array using the configured {@link #getCipherService() cipherService}.
 481  
      *
 482  
      * @param encrypted the encrypted byte array to decrypt
 483  
      * @return the decrypted byte array returned by the configured {@link #getCipherService () cipher}.
 484  
      */
 485  
     protected byte[] decrypt(byte[] encrypted) {
 486  0
         byte[] serialized = encrypted;
 487  0
         CipherService cipherService = getCipherService();
 488  0
         if (cipherService != null) {
 489  0
             ByteSource byteSource = cipherService.decrypt(encrypted, getDecryptionCipherKey());
 490  0
             serialized = byteSource.getBytes();
 491  
         }
 492  0
         return serialized;
 493  
     }
 494  
 
 495  
     /**
 496  
      * Serializes the given {@code principals} by serializing them to a byte array by using the
 497  
      * {@link #getSerializer() serializer}'s {@link Serializer#serialize(Object) serialize} method.
 498  
      *
 499  
      * @param principals the principal collection to serialize to a byte array
 500  
      * @return the serialized principal collection in the form of a byte array
 501  
      */
 502  
     protected byte[] serialize(PrincipalCollection principals) {
 503  0
         return getSerializer().serialize(principals);
 504  
     }
 505  
 
 506  
     /**
 507  
      * De-serializes the given byte array by using the {@link #getSerializer() serializer}'s
 508  
      * {@link Serializer#deserialize deserialize} method.
 509  
      *
 510  
      * @param serializedIdentity the previously serialized {@code PrincipalCollection} as a byte array
 511  
      * @return the de-serialized (reconstituted) {@code PrincipalCollection}
 512  
      */
 513  
     protected PrincipalCollection deserialize(byte[] serializedIdentity) {
 514  0
         return getSerializer().deserialize(serializedIdentity);
 515  
     }
 516  
 
 517  
     /**
 518  
      * Reacts to a failed login by immediately {@link #forgetIdentity(org.apache.shiro.subject.Subject) forgetting} any
 519  
      * previously remembered identity.  This is an additional security feature to prevent any remenant identity data
 520  
      * from being retained in case the authentication attempt is not being executed by the expected user.
 521  
      *
 522  
      * @param subject the subject which executed the failed login attempt
 523  
      * @param token   the authentication token resulting in a failed login attempt - ignored by this implementation
 524  
      * @param ae      the exception thrown as a result of the failed login attempt - ignored by this implementation
 525  
      */
 526  
     public void onFailedLogin(Subject subject, AuthenticationToken token, AuthenticationException ae) {
 527  0
         forgetIdentity(subject);
 528  0
     }
 529  
 
 530  
     /**
 531  
      * Reacts to a subject logging out of the application and immediately
 532  
      * {@link #forgetIdentity(org.apache.shiro.subject.Subject) forgets} any previously stored identity and returns.
 533  
      *
 534  
      * @param subject the subject logging out.
 535  
      */
 536  
     public void onLogout(Subject subject) {
 537  0
         forgetIdentity(subject);
 538  0
     }
 539  
 }