| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.jsecurity.subject; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.jsecurity.authc.AuthenticationException; |
| import org.jsecurity.authc.AuthenticationInfo; |
| import org.jsecurity.authc.AuthenticationToken; |
| import org.jsecurity.authc.RememberMeAuthenticationToken; |
| import org.jsecurity.codec.Base64; |
| import org.jsecurity.codec.Hex; |
| import org.jsecurity.crypto.BlowfishCipher; |
| import org.jsecurity.crypto.Cipher; |
| import org.jsecurity.io.DefaultSerializer; |
| import org.jsecurity.io.SerializationException; |
| import org.jsecurity.io.Serializer; |
| |
| /** |
| * Abstract implementation of the <code>RememberMeManager</code> interface that handles |
| * {@link #setSerializer(org.jsecurity.io.Serializer) serialization} and |
| * {@link #setCipher(org.jsecurity.crypto.Cipher) encryption} of the remembered user identity. |
| * <p/> |
| * The remembered identity storage location is implementation-specific. |
| * |
| * @author Les Hazlewood |
| * @author Jeremy Haile |
| * @since 0.9 |
| */ |
| public abstract class AbstractRememberMeManager implements RememberMeManager { |
| |
| //TODO - complete JavaDoc |
| |
| /** private inner log instance. */ |
| private static final Log log = LogFactory.getLog(AbstractRememberMeManager.class); |
| |
| private Serializer serializer = new DefaultSerializer(); |
| private Cipher cipher = new BlowfishCipher(); |
| private byte[] encryptionCipherKey = null; |
| private byte[] decryptionCipherKey = null; |
| |
| public AbstractRememberMeManager() { |
| } |
| |
| public Serializer getSerializer() { |
| return serializer; |
| } |
| |
| public void setSerializer(Serializer serializer) { |
| this.serializer = serializer; |
| } |
| |
| public Cipher getCipher() { |
| return cipher; |
| } |
| |
| public void setCipher(Cipher cipher) { |
| this.cipher = cipher; |
| } |
| |
| public byte[] getEncryptionCipherKey() { |
| return encryptionCipherKey; |
| } |
| |
| public void setEncryptionCipherKey(byte[] encryptionCipherKey) { |
| this.encryptionCipherKey = encryptionCipherKey; |
| } |
| |
| public void setEncryptionCipherKeyHex(String hex) { |
| setEncryptionCipherKey(Hex.decode(hex)); |
| } |
| |
| public void setEncryptionCipherKeyBase64(String base64) { |
| setEncryptionCipherKey(Base64.decode(base64)); |
| } |
| |
| public byte[] getDecryptionCipherKey() { |
| return decryptionCipherKey; |
| } |
| |
| public void setDecryptionCipherKey(byte[] decryptionCipherKey) { |
| this.decryptionCipherKey = decryptionCipherKey; |
| } |
| |
| public void setDecryptionCipherKeyHex(String hex) { |
| setDecryptionCipherKey(Hex.decode(hex)); |
| } |
| |
| public void setDecryptionCipherKeyBase64(String base64) { |
| setDecryptionCipherKey(Base64.decode(base64)); |
| } |
| |
| public byte[] getCipherKey() { |
| //Since this method should only be used with symmetric ciphers |
| //(where the enc and dec keys are the same), either is fine, just return one of them: |
| return getEncryptionCipherKey(); |
| } |
| |
| public void setCipherKey(byte[] cipherKey) { |
| //Since this method should only be used in symmetric ciphers |
| //(where the enc and dec keys are the same), set it on both: |
| setEncryptionCipherKey(cipherKey); |
| setDecryptionCipherKey(cipherKey); |
| } |
| |
| public void setCipherKeyHex(String hex) { |
| setCipherKey(Hex.decode(hex)); |
| } |
| |
| public void setCipherKeyBase64(String base64) { |
| setCipherKey(Base64.decode(base64)); |
| } |
| |
| // Abstract methods to be implemented by subclasses |
| protected abstract void rememberSerializedIdentity(byte[] serialized); |
| |
| protected abstract byte[] getSerializedRememberedIdentity(); |
| |
| protected abstract void forgetIdentity(); |
| |
| |
| protected boolean isRememberMe(AuthenticationToken token) { |
| return token != null && (token instanceof RememberMeAuthenticationToken) && |
| ((RememberMeAuthenticationToken) token).isRememberMe(); |
| } |
| |
| public void onSuccessfulLogin(AuthenticationToken token, AuthenticationInfo info) { |
| //always clear any previous identity: |
| forgetIdentity(token); |
| |
| //reset it if necessary: |
| if (isRememberMe(token)) { |
| rememberIdentity(token, info); |
| } else { |
| if (log.isDebugEnabled()) { |
| log.debug("AuthenticationToken did not indicate RememberMe is requested. " + |
| "RememberMe functionality will not be executed for corresponding account."); |
| } |
| } |
| } |
| |
| public void rememberIdentity(AuthenticationToken submittedToken, AuthenticationInfo successfullyAuthenticated) { |
| rememberIdentity(successfullyAuthenticated); |
| } |
| |
| public void rememberIdentity(AuthenticationInfo successfullyAuthenticated) { |
| PrincipalCollection principals = getIdentityToRemember(successfullyAuthenticated); |
| rememberIdentity(principals); |
| } |
| |
| protected PrincipalCollection getIdentityToRemember(AuthenticationInfo info) { |
| return info.getPrincipals(); |
| } |
| |
| protected void rememberIdentity(PrincipalCollection accountPrincipals) { |
| try { |
| byte[] bytes = serialize(accountPrincipals); |
| if (getCipher() != null) { |
| bytes = encrypt(bytes); |
| } |
| rememberSerializedIdentity(bytes); |
| } catch (SerializationException se) { |
| if (log.isWarnEnabled()) { |
| log.warn("Unable to serialize account principals [" + accountPrincipals + "]. Identity " + |
| "cannot be remembered! This is a non fatal exception as RememberMe identity services " + |
| "are not considered critical and execution can continue as normal. But please " + |
| "investigate and resolve to prevent seeing this message again.", se); |
| } |
| } |
| } |
| |
| public PrincipalCollection getRememberedPrincipals() { |
| try { |
| |
| PrincipalCollection principals = null; |
| byte[] bytes = getSerializedRememberedIdentity(); |
| if (bytes != null) { |
| if (getCipher() != null) { |
| bytes = decrypt(bytes); |
| } |
| try { |
| principals = deserialize(bytes); |
| } catch (SerializationException e) { |
| if (log.isWarnEnabled()) { |
| log.warn("Unable to deserialize stored identity byte array. Remembered identity " + |
| "cannot be reconstituted! This is a non fatal exception as RememberMe identity services " + |
| "are not considered critical and execution can continue as normal, but please " + |
| "investigate and resolve to prevent seeing this message again.", e); |
| } |
| } |
| } |
| return principals; |
| |
| } catch( Exception e ) { |
| return onRememberedPrincipalFailure( e ); |
| } |
| } |
| |
| /** |
| * Called when an exception is thrown while trying to retrieve principals. The default implementation logs a |
| * warning and forgets the problem identity. This most commonly would occur when an encryption key is |
| * updated and old principals are retrieved that have been encrypted with the previous key. |
| * @param e the exception that was thrown. |
| * @return the principal collection to be returned. |
| */ |
| protected PrincipalCollection onRememberedPrincipalFailure(Exception e) { |
| if(log.isWarnEnabled() ) { |
| log.warn("There was a failure while trying to retrieve remembered principals. This could be due to a " + |
| "configuration problem or corrupted principals. This could also be due to a recently " + |
| "changed encryption key. The remembered identity will be forgotten and not used for this " + |
| "request.", e); |
| } |
| forgetIdentity(); |
| return null; |
| } |
| |
| protected byte[] encrypt(byte[] serialized) { |
| byte[] value = serialized; |
| Cipher cipher = getCipher(); |
| if (cipher != null) { |
| value = cipher.encrypt(serialized, getEncryptionCipherKey()); |
| } |
| return value; |
| } |
| |
| protected byte[] decrypt(byte[] encrypted) { |
| byte[] serialized = encrypted; |
| Cipher cipher = getCipher(); |
| if (cipher != null) { |
| serialized = cipher.decrypt(encrypted, getDecryptionCipherKey()); |
| } |
| return serialized; |
| } |
| |
| |
| protected byte[] serialize(PrincipalCollection principals) { |
| return getSerializer().serialize(principals); |
| } |
| |
| protected PrincipalCollection deserialize(byte[] serializedIdentity) { |
| return (PrincipalCollection) getSerializer().deserialize(serializedIdentity); |
| } |
| |
| public void onFailedLogin(AuthenticationToken token, AuthenticationException ae) { |
| forgetIdentity(token, ae); |
| } |
| |
| public void onLogout(PrincipalCollection subjectPrincipals) { |
| forgetIdentity(); |
| } |
| |
| protected void forgetIdentity(AuthenticationToken token, AuthenticationException ae) { |
| forgetIdentity(token); |
| } |
| |
| protected void forgetIdentity(AuthenticationToken token) { |
| forgetIdentity(); |
| } |
| |
| } |