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 */
019 package org.apache.shiro.realm;
020
021 import org.slf4j.Logger;
022 import org.slf4j.LoggerFactory;
023
024 import org.apache.shiro.authc.AuthenticationException;
025 import org.apache.shiro.authc.AuthenticationInfo;
026 import org.apache.shiro.authc.AuthenticationToken;
027 import org.apache.shiro.authc.IncorrectCredentialsException;
028 import org.apache.shiro.authc.LogoutAware;
029 import org.apache.shiro.authc.UsernamePasswordToken;
030 import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
031 import org.apache.shiro.authc.credential.CredentialsMatcher;
032 import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
033 import org.apache.shiro.cache.CacheManager;
034 import org.apache.shiro.subject.PrincipalCollection;
035
036
037 /**
038 * A top-level abstract implementation of the <tt>Realm</tt> interface that only implements authentication support
039 * (log-in) operations and leaves authorization (access control) behavior to subclasses.
040 *
041 * <p>Since a Realm provides both authentication <em>and</em> authorization operations, the implementation approach for
042 * this class could have been reversed. That is, authorization support could have been implemented here and
043 * authentication support left to subclasses.
044 *
045 * <p>The reason the existing implementation is in place though
046 * (authentication support) is that most authentication operations are fairly common across the large majority of
047 * applications, whereas authorization operations are more so heavily dependent upon the application's data model, which
048 * can vary widely.
049 *
050 * <p>By providing the most common authentication operations here and leaving data-model specific authorization checks
051 * to subclasses, a top-level abstract class for most common authentication behavior is more useful as an extension
052 * point for most applications.
053 *
054 * @author Les Hazlewood
055 * @author Jeremy Haile
056 * @since 0.2
057 */
058 public abstract class AuthenticatingRealm extends CachingRealm implements LogoutAware {
059
060 //TODO - complete JavaDoc
061
062 private static final Logger log = LoggerFactory.getLogger(AuthenticatingRealm.class);
063
064 /**
065 * Password matcher used to determine if the provided password matches
066 * the password stored in the data store.
067 */
068 private CredentialsMatcher credentialsMatcher = new SimpleCredentialsMatcher();
069
070 /**
071 * The class that this realm supports for authentication tokens. This is used by the
072 * default implementation of the {@link Realm#supports(org.apache.shiro.authc.AuthenticationToken)} method to
073 * determine whether or not the given authentication token is supported by this realm.
074 */
075 private Class<? extends AuthenticationToken> authenticationTokenClass = UsernamePasswordToken.class;
076
077 /*--------------------------------------------
078 | C O N S T R U C T O R S |
079 ============================================*/
080 public AuthenticatingRealm() {
081 }
082
083 public AuthenticatingRealm(CacheManager cacheManager) {
084 setCacheManager(cacheManager);
085 }
086
087 public AuthenticatingRealm(CredentialsMatcher matcher) {
088 setCredentialsMatcher(matcher);
089 }
090
091 public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
092 setCacheManager(cacheManager);
093 setCredentialsMatcher(matcher);
094 }
095
096 /*--------------------------------------------
097 | A C C E S S O R S / M O D I F I E R S |
098 ============================================*/
099 /**
100 * Returns the <code>CredentialsMatcher</code> used during an authentication attempt to verify submitted
101 * credentials with those stored in the system.
102 *
103 * <p>Unless overridden by the {@link #setCredentialsMatcher setCredentialsMatcher} method, the default
104 * value is a {@link org.apache.shiro.authc.credential.SimpleCredentialsMatcher SimpleCredentialsMatcher} instance.
105 *
106 * @return the <code>CredentialsMatcher</code> used during an authentication attempt to verify submitted
107 * credentials with those stored in the system.
108 */
109 public CredentialsMatcher getCredentialsMatcher() {
110 return credentialsMatcher;
111 }
112
113 /**
114 * Sets the CrendialsMatcher used during an authentication attempt to verify submitted credentials with those
115 * stored in the system. The implementation of this matcher can be switched via configuration to
116 * support any number of schemes, including plain text comparisons, hashing comparisons, and others.
117 *
118 * <p>Unless overridden by this method, the default value is a
119 * {@link org.apache.shiro.authc.credential.SimpleCredentialsMatcher} instance.
120 *
121 * @param credentialsMatcher the matcher to use.
122 */
123 public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
124 this.credentialsMatcher = credentialsMatcher;
125 }
126
127 /**
128 * Returns the authenticationToken class supported by this realm.
129 *
130 * <p>The default value is <tt>{@link org.apache.shiro.authc.UsernamePasswordToken UsernamePasswordToken.class}</tt>, since
131 * about 90% of realms use username/password authentication, regardless of their protocol (e.g. over jdbc, ldap,
132 * kerberos, http, etc).
133 *
134 * <p>If subclasses haven't already overridden the {@link Realm#supports Realm.supports(AuthenticationToken)} method,
135 * they must {@link #setAuthenticationTokenClass(Class) set a new class} if they won't support
136 * <tt>UsernamePasswordToken</tt> authentication token submissions.
137 *
138 * @return the authenticationToken class supported by this realm.
139 * @see #setAuthenticationTokenClass
140 */
141 public Class getAuthenticationTokenClass() {
142 return authenticationTokenClass;
143 }
144
145 /**
146 * Sets the authenticationToken class supported by this realm.
147 *
148 * <p>Unless overridden by this method, the default value is
149 * {@link org.apache.shiro.authc.UsernamePasswordToken UsernamePasswordToken.class} to support the majority of applications.
150 *
151 * @param authenticationTokenClass the class of authentication token instances supported by this realm.
152 * @see #getAuthenticationTokenClass getAuthenticationTokenClass() for more explanation.
153 */
154 public void setAuthenticationTokenClass(Class<? extends AuthenticationToken> authenticationTokenClass) {
155 this.authenticationTokenClass = authenticationTokenClass;
156 }
157
158 /*--------------------------------------------
159 | M E T H O D S |
160 ============================================*/
161 /**
162 * Convenience implementation that returns
163 * <tt>getAuthenticationTokenClass().isAssignableFrom( token.getClass() );</tt>. Can be overridden
164 * by subclasses for more complex token checking.
165 * <p>Most configurations will only need to set a different class via
166 * {@link #setAuthenticationTokenClass}, as opposed to overriding this method.
167 *
168 * @param token the token being submitted for authentication.
169 * @return true if this authentication realm can process the submitted token instance of the class, false otherwise.
170 */
171 public boolean supports(AuthenticationToken token) {
172 return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
173 }
174
175 public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
176
177 AuthenticationInfo info = doGetAuthenticationInfo(token);
178
179 if (info == null) {
180 if (log.isDebugEnabled()) {
181 String msg = "No authentication information found for submitted authentication token [" + token + "]. " +
182 "Returning null.";
183 log.debug(msg);
184 }
185 return null;
186 }
187
188 CredentialsMatcher cm = getCredentialsMatcher();
189 if (cm != null) {
190 if (!cm.doCredentialsMatch(token, info)) {
191 String msg = "The credentials provided for account [" + token +
192 "] did not match the expected credentials.";
193 throw new IncorrectCredentialsException(msg);
194 }
195 } else {
196 throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
197 "credentials during authentication. If you do not wish for credentials to be examined, you " +
198 "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
199 }
200
201 return info;
202 }
203
204 /**
205 * Retrieves authentication data from an implementation-specific datasource (RDBMS, LDAP, etc) for the given
206 * authentication token.
207 *
208 * <p>For most datasources, this means just 'pulling' authentication data for an associated subject/user and nothing
209 * more and letting Shiro do the rest. But in some systems, this method could actually perform EIS specific
210 * log-in logic in addition to just retrieving data - it is up to the Realm implementation.
211 *
212 * <p>A <tt>null</tt> return value means that no account could be associated with the specified token.
213 *
214 * @param token the authentication token containing the user's principal and credentials.
215 * @return an {@link AuthenticationInfo} object containing account data resulting from the
216 * authentication ONLY if the lookup is successful (i.e. account exists and is valid, etc.)
217 * @throws org.apache.shiro.authc.AuthenticationException
218 * if there is an error acquiring data or performing
219 * realm-specific authentication logic for the specified <tt>token</tt>
220 */
221 protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
222
223 /**
224 * Default implementation that does nothing (no-op) and exists as a convenience mechanism in case subclasses
225 * wish to override it to implement realm-specific logout logic for the given user account logging out.</p>
226 * <p/>
227 * In a single-realm Shiro configuration (most applications), the <code>principals</code> method
228 * argument will be the same as that which is contained in the <code>AuthenticationInfo</code> object returned by the
229 * {@link #doGetAuthenticationInfo} method (that is, {@link AuthenticationInfo#getPrincipals info.getPrincipals()}).
230 * <p/>
231 * In a multi-realm Shiro configuration, the given <code>principals</code> method
232 * argument could contain principals returned by many realms. Therefore the subclass implementation would need
233 * to know how to extract the principal(s) relevant to only itself and ignore other realms' principals. This is
234 * usually done by calling {@link org.apache.shiro.subject.PrincipalCollection#fromRealm(String) principals.fromRealm(name)},
235 * using the realm's own {@link Realm#getName() name}.
236 *
237 * @param principals the application-specific Subject/user identifier that is logging out.
238 */
239 public void onLogout(PrincipalCollection principals) {
240 //no-op, here for subclass override if desired.
241 }
242
243 }