001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.mgt;
020
021import org.apache.shiro.authc.AuthenticationException;
022import org.apache.shiro.authc.AuthenticationInfo;
023import org.apache.shiro.authc.AuthenticationToken;
024import org.apache.shiro.authc.RememberMeAuthenticationToken;
025import org.apache.shiro.crypto.cipher.AesCipherService;
026import org.apache.shiro.crypto.cipher.ByteSourceBroker;
027import org.apache.shiro.crypto.cipher.CipherService;
028import org.apache.shiro.lang.io.DefaultSerializer;
029import org.apache.shiro.lang.io.Serializer;
030import org.apache.shiro.lang.util.ByteSource;
031import org.apache.shiro.lang.util.ByteUtils;
032import org.apache.shiro.subject.PrincipalCollection;
033import org.apache.shiro.subject.Subject;
034import org.apache.shiro.subject.SubjectContext;
035import org.slf4j.Logger;
036import org.slf4j.LoggerFactory;
037
038import java.util.function.Supplier;
039
040/**
041 * Abstract implementation of the {@code RememberMeManager} interface that handles
042 * {@link #setSerializer(Serializer)  serialization} and
043 * {@link #setCipherService encryption} of the remembered user identity.
044 * <p/>
045 * The remembered identity storage location and details are left to subclasses.
046 * <h2>Default encryption key</h2>
047 * This implementation uses an {@link AesCipherService AesCipherService} for strong encryption by default.  It also
048 * uses a default generated symmetric key to both encrypt and decrypt data.  As AES is a symmetric cipher, the same
049 * {@code key} is used to both encrypt and decrypt data, BUT NOTE:
050 * <p/>
051 * Because Shiro is an open-source project, if anyone knew that you were using Shiro's default
052 * {@code key}, they could download/view the source, and with enough effort, reconstruct the {@code key}
053 * and decode encrypted data at will.
054 * <p/>
055 * Of course, this key is only really used to encrypt the remembered {@code PrincipalCollection} which is typically
056 * a user id or username.  So if you do not consider that sensitive information, and you think the default key still
057 * makes things 'sufficiently difficult', then you can ignore this issue.
058 * <p/>
059 * However, if you do feel this constitutes sensitive information, it is recommended that you provide your own
060 * {@code key} via the {@link #setCipherKey setCipherKey} method to a key known only to your application,
061 * guaranteeing that no third party can decrypt your data.  You can generate your own key by calling the
062 * {@code CipherService}'s {@link AesCipherService#generateNewKey() generateNewKey} method
063 * and using that result as the {@link #setCipherKey cipherKey} configuration attribute.
064 *
065 * @since 0.9
066 */
067public abstract class AbstractRememberMeManager implements RememberMeManager {
068
069    /**
070     * private inner log instance.
071     */
072    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRememberMeManager.class);
073
074    /**
075     * Serializer to use for converting PrincipalCollection instances to/from byte arrays
076     */
077    private Serializer<PrincipalCollection> serializer = new DefaultSerializer<>();
078
079    /**
080     * Cipher to use for encrypting/decrypting serialized byte arrays for added security
081     */
082    private CipherService cipherService = new AesCipherService();
083    ;
084
085    /**
086     * Cipher encryption key to use with the Cipher when encrypting data
087     */
088    private byte[] encryptionCipherKey;
089
090    /**
091     * Cipher decryption key to use with the Cipher when decrypting data
092     */
093    private byte[] decryptionCipherKey;
094
095    /**
096     * Default constructor that initializes a {@link DefaultSerializer} as the {@link #getSerializer() serializer} and
097     * an {@link AesCipherService} as the {@link #getCipherService() cipherService}.
098     */
099    public AbstractRememberMeManager() {
100        setCipherKey(((AesCipherService) cipherService).generateNewKey().getEncoded());
101    }
102
103    /**
104     * Constructor. Pass keySupplier that supplies encryption key
105     *
106     * @param keySupplier
107     * @since 2.0
108     */
109    public AbstractRememberMeManager(Supplier<byte[]> keySupplier) {
110        this();
111        setCipherKey(keySupplier.get());
112    }
113
114    /**
115     * Returns the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
116     * persistent remember me storage.
117     * <p/>
118     * Unless overridden by the {@link #setSerializer} method, the default instance is a
119     * {@link org.apache.shiro.lang.io.DefaultSerializer}.
120     *
121     * @return the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
122     * persistent remember me storage.
123     */
124    public Serializer<PrincipalCollection> getSerializer() {
125        return serializer;
126    }
127
128    /**
129     * Sets the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances for
130     * persistent remember me storage.
131     * <p/>
132     * Unless overridden by this method, the default instance is a {@link DefaultSerializer}.
133     *
134     * @param serializer the {@code Serializer} used to serialize and deserialize {@link PrincipalCollection} instances
135     *                   for persistent remember me storage.
136     */
137    public void setSerializer(Serializer<PrincipalCollection> serializer) {
138        this.serializer = serializer;
139    }
140
141    /**
142     * Returns the {@code CipherService} to use for encrypting and decrypting serialized identity data to prevent easy
143     * inspection of Subject identity data.
144     * <p/>
145     * Unless overridden by the {@link #setCipherService} method, the default instance is an {@link AesCipherService}.
146     *
147     * @return the {@code Cipher} to use for encrypting and decrypting serialized identity data to prevent easy
148     * inspection of Subject identity data
149     */
150    public CipherService getCipherService() {
151        return cipherService;
152    }
153
154    /**
155     * Sets the {@code CipherService} to use for encrypting and decrypting serialized identity data to prevent easy
156     * inspection of Subject identity data.
157     * <p/>
158     * If the CipherService is a symmetric CipherService (using the same key for both encryption and decryption), you
159     * should set your key via the {@link #setCipherKey(byte[])} method.
160     * <p/>
161     * If the CipherService is an asymmetric CipherService (different keys for encryption and decryption, such as
162     * public/private key pairs), you should set your encryption and decryption key via the respective
163     * {@link #setEncryptionCipherKey(byte[])} and {@link #setDecryptionCipherKey(byte[])} methods.
164     * <p/>
165     * <b>N.B.</b> Unless overridden by this method, the default CipherService instance is an
166     * {@link AesCipherService}.  This {@code RememberMeManager} implementation already has a configured symmetric key
167     * to use for encryption and decryption, but it is recommended to provide your own for added security.  See the
168     * class-level JavaDoc for more information and why it might be good to provide your own.
169     *
170     * @param cipherService the {@code CipherService} to use for encrypting and decrypting serialized identity data to
171     *                      prevent easy inspection of Subject identity data.
172     */
173    public void setCipherService(CipherService cipherService) {
174        this.cipherService = cipherService;
175    }
176
177    /**
178     * Returns the cipher key to use for encryption operations.
179     *
180     * @return the cipher key to use for encryption operations.
181     * @see #setCipherService for a description of the various {@code get/set*Key} methods.
182     */
183    public byte[] getEncryptionCipherKey() {
184        return encryptionCipherKey;
185    }
186
187    /**
188     * Sets the encryption key to use for encryption operations.
189     *
190     * @param encryptionCipherKey the encryption key to use for encryption operations.
191     * @see #setCipherService for a description of the various {@code get/set*Key} methods.
192     */
193    public void setEncryptionCipherKey(byte[] encryptionCipherKey) {
194        this.encryptionCipherKey = encryptionCipherKey;
195    }
196
197    /**
198     * Returns the decryption cipher key to use for decryption operations.
199     *
200     * @return the cipher key to use for decryption operations.
201     * @see #setCipherService for a description of the various {@code get/set*Key} methods.
202     */
203    public byte[] getDecryptionCipherKey() {
204        return decryptionCipherKey;
205    }
206
207    /**
208     * Sets the decryption key to use for decryption operations.
209     *
210     * @param decryptionCipherKey the decryption key to use for decryption operations.
211     * @see #setCipherService for a description of the various {@code get/set*Key} methods.
212     */
213    public void setDecryptionCipherKey(byte[] decryptionCipherKey) {
214        this.decryptionCipherKey = decryptionCipherKey;
215    }
216
217    /**
218     * Convenience method that returns the cipher key to use for <em>both</em> encryption and decryption.
219     * <p/>
220     * <b>N.B.</b> This method can only be called if the underlying {@link #getCipherService() cipherService} is a symmetric
221     * CipherService which by definition uses the same key for both encryption and decryption.  If using an asymmetric
222     * CipherService public/private key pair, you cannot use this method, and should instead use the
223     * {@link #getEncryptionCipherKey()} and {@link #getDecryptionCipherKey()} methods individually.
224     * <p/>
225     * The default {@link AesCipherService} instance is a symmetric cipher service, so this method can be used if you are
226     * using the default.
227     *
228     * @return the symmetric cipher key used for both encryption and decryption.
229     */
230    public byte[] getCipherKey() {
231        //Since this method should only be used with symmetric ciphers
232        //(where the enc and dec keys are the same), either is fine, just return one of them:
233        return getEncryptionCipherKey();
234    }
235
236    /**
237     * Convenience method that sets the cipher key to use for <em>both</em> encryption and decryption.
238     * <p/>
239     * <b>N.B.</b> This method can only be called if the underlying {@link #getCipherService() cipherService} is a
240     * symmetric CipherService?which by definition uses the same key for both encryption and decryption.  If using an
241     * asymmetric CipherService?(such as a public/private key pair), you cannot use this method, and should instead use
242     * the {@link #setEncryptionCipherKey(byte[])} and {@link #setDecryptionCipherKey(byte[])} methods individually.
243     * <p/>
244     * The default {@link AesCipherService} instance is a symmetric CipherService, so this method can be used if you
245     * are using the default.
246     *
247     * @param cipherKey the symmetric cipher key to use for both encryption and decryption.
248     */
249    public void setCipherKey(byte[] cipherKey) {
250        //Since this method should only be used in symmetric ciphers
251        //(where the enc and dec keys are the same), set it on both:
252        setEncryptionCipherKey(cipherKey);
253        setDecryptionCipherKey(cipherKey);
254    }
255
256    /**
257     * Forgets (removes) any remembered identity data for the specified {@link Subject} instance.
258     *
259     * @param subject the subject instance for which identity data should be forgotten from the underlying persistence
260     *                mechanism.
261     */
262    protected abstract void forgetIdentity(Subject subject);
263
264    /**
265     * Determines whether or not remember me services should be performed for the specified token.  This method returns
266     * {@code true} iff:
267     * <ol>
268     * <li>The token is not {@code null} and</li>
269     * <li>The token is an {@code instanceof} {@link RememberMeAuthenticationToken} and</li>
270     * <li>{@code token}.{@link org.apache.shiro.authc.RememberMeAuthenticationToken#isRememberMe() isRememberMe()} is
271     * {@code true}</li>
272     * </ol>
273     *
274     * @param token the authentication token submitted during the successful authentication attempt.
275     * @return true if remember me services should be performed as a result of the successful authentication attempt.
276     */
277    protected boolean isRememberMe(AuthenticationToken token) {
278        return token instanceof RememberMeAuthenticationToken && ((RememberMeAuthenticationToken) token).isRememberMe();
279    }
280
281    /**
282     * Reacts to the successful login attempt by first always {@link #forgetIdentity(Subject) forgetting} any previously
283     * stored identity.  Then if the {@code token}
284     * {@link #isRememberMe(org.apache.shiro.authc.AuthenticationToken) is a RememberMe} token, the associated identity
285     * will be {@link #rememberIdentity(org.apache.shiro.subject.Subject, org.apache.shiro.authc.AuthenticationToken,
286     * org.apache.shiro.authc.AuthenticationInfo) remembered}
287     * for later retrieval during a new user session.
288     *
289     * @param subject the subject for which the principals are being remembered.
290     * @param token   the token that resulted in a successful authentication attempt.
291     * @param info    the authentication info resulting from the successful authentication attempt.
292     */
293    public void onSuccessfulLogin(Subject subject, AuthenticationToken token, AuthenticationInfo info) {
294        //always clear any previous identity:
295        forgetIdentity(subject);
296
297        //now save the new identity:
298        if (isRememberMe(token)) {
299            rememberIdentity(subject, token, info);
300        } else {
301            if (LOGGER.isDebugEnabled()) {
302                LOGGER.debug("AuthenticationToken did not indicate RememberMe is requested.  "
303                        + "RememberMe functionality will not be executed for corresponding account.");
304            }
305        }
306    }
307
308    /**
309     * Remembers a subject-unique identity for retrieval later.  This implementation first
310     * {@link #getIdentityToRemember resolves} the exact
311     * {@link PrincipalCollection principals} to remember.  It then remembers the principals by calling
312     * {@link #rememberIdentity(org.apache.shiro.subject.Subject, org.apache.shiro.subject.PrincipalCollection)}.
313     * <p/>
314     * This implementation ignores the {@link AuthenticationToken} argument, but it is available to subclasses if
315     * necessary for custom logic.
316     *
317     * @param subject   the subject for which the principals are being remembered.
318     * @param token     the token that resulted in a successful authentication attempt.
319     * @param authcInfo the authentication info resulting from the successful authentication attempt.
320     */
321    public void rememberIdentity(Subject subject, AuthenticationToken token, AuthenticationInfo authcInfo) {
322        PrincipalCollection principals = getIdentityToRemember(subject, authcInfo);
323        rememberIdentity(subject, principals);
324    }
325
326    /**
327     * Returns {@code info}.{@link org.apache.shiro.authc.AuthenticationInfo#getPrincipals() getPrincipals()} and
328     * ignores the {@link Subject} argument.
329     *
330     * @param subject the subject for which the principals are being remembered.
331     * @param info    the authentication info resulting from the successful authentication attempt.
332     * @return the {@code PrincipalCollection} to remember.
333     */
334    protected PrincipalCollection getIdentityToRemember(Subject subject, AuthenticationInfo info) {
335        return info.getPrincipals();
336    }
337
338    /**
339     * Remembers the specified account principals by first
340     * {@link #convertPrincipalsToBytes(org.apache.shiro.subject.PrincipalCollection) converting} them to a byte
341     * array and then {@link #rememberSerializedIdentity(org.apache.shiro.subject.Subject, byte[]) remembers} that
342     * byte array.
343     *
344     * @param subject           the subject for which the principals are being remembered.
345     * @param accountPrincipals the principals to remember for retrieval later.
346     */
347    protected void rememberIdentity(Subject subject, PrincipalCollection accountPrincipals) {
348        byte[] bytes = convertPrincipalsToBytes(accountPrincipals);
349        rememberSerializedIdentity(subject, bytes);
350    }
351
352    /**
353     * Converts the given principal collection the byte array that will be persisted to be 'remembered' later.
354     * <p/>
355     * This implementation first {@link #serialize(org.apache.shiro.subject.PrincipalCollection) serializes} the
356     * principals to a byte array and then {@link #encrypt(byte[]) encrypts} that byte array.
357     *
358     * @param principals the {@code PrincipalCollection} to convert to a byte array
359     * @return the representative byte array to be persisted for remember me functionality.
360     */
361    protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
362        byte[] bytes = serialize(principals);
363        if (getCipherService() != null) {
364            bytes = encrypt(bytes);
365        }
366        return bytes;
367    }
368
369    /**
370     * Persists the identity bytes to a persistent store for retrieval later via the
371     * {@link #getRememberedSerializedIdentity(SubjectContext)} method.
372     *
373     * @param subject    the Subject for which the identity is being serialized.
374     * @param serialized the serialized bytes to be persisted.
375     */
376    protected abstract void rememberSerializedIdentity(Subject subject, byte[] serialized);
377
378    /**
379     * Implements the interface method by first {@link #getRememberedSerializedIdentity(SubjectContext) acquiring}
380     * the remembered serialized byte array.  Then it {@link #convertBytesToPrincipals(byte[], SubjectContext) converts}
381     * them and returns the re-constituted {@link PrincipalCollection}.  If no remembered principals could be
382     * obtained, {@code null} is returned.
383     * <p/>
384     * If any exceptions are thrown, the {@link #onRememberedPrincipalFailure(RuntimeException, SubjectContext)} method
385     * is called to allow any necessary post-processing (such as immediately removing any previously remembered
386     * values for safety).
387     *
388     * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
389     *                       is being used to construct a {@link Subject} instance.
390     * @return the remembered principals or {@code null} if none could be acquired.
391     */
392    public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
393        PrincipalCollection principals = null;
394        byte[] bytes = null;
395        try {
396            bytes = getRememberedSerializedIdentity(subjectContext);
397            //SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
398            if (bytes != null && bytes.length > 0) {
399                principals = convertBytesToPrincipals(bytes, subjectContext);
400            }
401        } catch (RuntimeException re) {
402            principals = onRememberedPrincipalFailure(re, subjectContext);
403        } finally {
404            ByteUtils.wipe(bytes);
405        }
406
407        return principals;
408    }
409
410    /**
411     * Based on the given subject context data, retrieves the previously persisted serialized identity, or
412     * {@code null} if there is no available data.  The context map is usually populated by a {@link Subject.Builder}
413     * implementation.  See the {@link SubjectFactory} class constants for Shiro's known map keys.
414     *
415     * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
416     *                       is being used to construct a {@link Subject} instance.  To be used to assist with data
417     *                       lookup.
418     * @return the previously persisted serialized identity, or {@code null} if there is no available data for the
419     * Subject.
420     */
421    protected abstract byte[] getRememberedSerializedIdentity(SubjectContext subjectContext);
422
423    /**
424     * If a {@link #getCipherService() cipherService} is available, it will be used to first decrypt the byte array.
425     * Then the bytes are then {@link #deserialize(byte[]) deserialized} and then returned.
426     *
427     * @param bytes          the bytes to decrypt if necessary and then deserialize.
428     * @param subjectContext the contextual data, usually provided by a {@link Subject.Builder} implementation, that
429     *                       is being used to construct a {@link Subject} instance.
430     * @return the deserialized and possibly decrypted principals
431     */
432    protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
433        if (getCipherService() != null) {
434            bytes = decrypt(bytes);
435        }
436        return deserialize(bytes);
437    }
438
439    /**
440     * Called when an exception is thrown while trying to retrieve principals.  The default implementation logs a
441     * warning message and forgets ('unremembers') the problem identity by calling
442     * {@link #forgetIdentity(SubjectContext) forgetIdentity(context)} and then immediately re-throws the
443     * exception to allow the calling component to react accordingly.
444     * <p/>
445     * This method implementation never returns an
446     * object - it always rethrows, but can be overridden by subclasses for custom handling behavior.
447     * <p/>
448     * This most commonly would be called when an encryption key is updated and old principals are retrieved that have
449     * been encrypted with the previous key.
450     *
451     * @param e       the exception that was thrown.
452     * @param context the contextual data, usually provided by a {@link Subject.Builder} implementation, that
453     *                is being used to construct a {@link Subject} instance.
454     * @return nothing - the original {@code RuntimeException} is propagated in all cases.
455     */
456    protected PrincipalCollection onRememberedPrincipalFailure(RuntimeException e, SubjectContext context) {
457
458        if (LOGGER.isWarnEnabled()) {
459            String message = "There was a failure while trying to retrieve remembered principals.  This could be due to a "
460                    + "configuration problem or corrupted principals.  This could also be due to a recently "
461                    + "changed encryption key, if you are using a shiro.ini file, this property would be "
462                    + "'securityManager.rememberMeManager.cipherKey'"
463                    + "see: http://shiro.apache.org/web.html#Web-RememberMeServices. "
464                    + "The remembered identity will be forgotten and not used for this request.";
465            LOGGER.warn(message);
466        }
467        forgetIdentity(context);
468        //propagate - security manager implementation will handle and warn appropriately
469        throw e;
470    }
471
472    /**
473     * Encrypts the byte array by using the configured {@link #getCipherService() cipherService}.
474     *
475     * @param serialized the serialized object byte array to be encrypted
476     * @return an encrypted byte array returned by the configured {@link #getCipherService () cipher}.
477     */
478    protected byte[] encrypt(byte[] serialized) {
479        byte[] value = serialized;
480        CipherService cipherService = getCipherService();
481        if (cipherService != null) {
482            ByteSource byteSource = cipherService.encrypt(serialized, getEncryptionCipherKey());
483            value = byteSource.getBytes();
484        }
485        return value;
486    }
487
488    /**
489     * Decrypts the byte array using the configured {@link #getCipherService() cipherService}.
490     *
491     * @param encrypted the encrypted byte array to decrypt
492     * @return the decrypted byte array returned by the configured {@link #getCipherService () cipher}.
493     */
494    protected byte[] decrypt(byte[] encrypted) {
495        byte[] serialized = encrypted;
496        CipherService cipherService = getCipherService();
497        if (cipherService != null) {
498            ByteSourceBroker broker = cipherService.decrypt(encrypted, getDecryptionCipherKey());
499            serialized = broker.getClonedBytes();
500        }
501        return serialized;
502    }
503
504    /**
505     * Serializes the given {@code principals} by serializing them to a byte array by using the
506     * {@link #getSerializer() serializer}'s {@link Serializer#serialize(Object) serialize} method.
507     *
508     * @param principals the principal collection to serialize to a byte array
509     * @return the serialized principal collection in the form of a byte array
510     */
511    protected byte[] serialize(PrincipalCollection principals) {
512        return getSerializer().serialize(principals);
513    }
514
515    /**
516     * Deserializes the given byte array by using the {@link #getSerializer() serializer}'s
517     * {@link Serializer#deserialize deserialize} method.
518     *
519     * @param serializedIdentity the previously serialized {@code PrincipalCollection} as a byte array
520     * @return the deserialized (reconstituted) {@code PrincipalCollection}
521     */
522    protected PrincipalCollection deserialize(byte[] serializedIdentity) {
523        return getSerializer().deserialize(serializedIdentity);
524    }
525
526    /**
527     * Reacts to a failed login by immediately {@link #forgetIdentity(org.apache.shiro.subject.Subject) forgetting} any
528     * previously remembered identity.  This is an additional security feature to prevent any remnant identity data
529     * from being retained in case the authentication attempt is not being executed by the expected user.
530     *
531     * @param subject the subject which executed the failed login attempt
532     * @param token   the authentication token resulting in a failed login attempt - ignored by this implementation
533     * @param ae      the exception thrown as a result of the failed login attempt - ignored by this implementation
534     */
535    public void onFailedLogin(Subject subject, AuthenticationToken token, AuthenticationException ae) {
536        forgetIdentity(subject);
537    }
538
539    /**
540     * Reacts to a subject logging out of the application and immediately
541     * {@link #forgetIdentity(org.apache.shiro.subject.Subject) forgets} any previously stored identity and returns.
542     *
543     * @param subject the subject logging out.
544     */
545    public void onLogout(Subject subject) {
546        forgetIdentity(subject);
547    }
548}