package com.hwlcn.security.realm;
import com.hwlcn.cache.Cache;
import com.hwlcn.cache.CacheManager;
import com.hwlcn.security.authc.*;
import com.hwlcn.security.authc.credential.AllowAllCredentialsMatcher;
import com.hwlcn.security.authc.credential.CredentialsMatcher;
import com.hwlcn.security.authc.credential.SimpleCredentialsMatcher;
import com.hwlcn.security.subject.PrincipalCollection;
import com.hwlcn.security.util.CollectionUtils;
import com.hwlcn.security.util.Initializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class AuthenticatingRealm extends CachingRealm implements Initializable {
private static final Logger log = LoggerFactory.getLogger(AuthenticatingRealm.class);
private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
private static final String DEFAULT_AUTHORIZATION_CACHE_SUFFIX = ".authenticationCache";
private CredentialsMatcher credentialsMatcher;
private Cache<Object, AuthenticationInfo> authenticationCache;
private boolean authenticationCachingEnabled;
private String authenticationCacheName;
private Class<? extends AuthenticationToken> authenticationTokenClass;
public AuthenticatingRealm() {
this(null, new SimpleCredentialsMatcher());
}
public AuthenticatingRealm(CacheManager cacheManager) {
this(cacheManager, new SimpleCredentialsMatcher());
}
public AuthenticatingRealm(CredentialsMatcher matcher) {
this(null, matcher);
}
public AuthenticatingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
authenticationTokenClass = UsernamePasswordToken.class;
this.authenticationCachingEnabled = false;
int instanceNumber = INSTANCE_COUNT.getAndIncrement();
this.authenticationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
if (instanceNumber > 0) {
this.authenticationCacheName = this.authenticationCacheName + "." + instanceNumber;
}
if (cacheManager != null) {
setCacheManager(cacheManager);
}
if (matcher != null) {
setCredentialsMatcher(matcher);
}
}
public CredentialsMatcher getCredentialsMatcher() {
return credentialsMatcher;
}
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
this.credentialsMatcher = credentialsMatcher;
}
public Class getAuthenticationTokenClass() {
return authenticationTokenClass;
}
public void setAuthenticationTokenClass(Class<? extends AuthenticationToken> authenticationTokenClass) {
this.authenticationTokenClass = authenticationTokenClass;
}
public void setAuthenticationCache(Cache<Object, AuthenticationInfo> authenticationCache) {
this.authenticationCache = authenticationCache;
}
public Cache<Object, AuthenticationInfo> getAuthenticationCache() {
return this.authenticationCache;
}
public String getAuthenticationCacheName() {
return this.authenticationCacheName;
}
public void setAuthenticationCacheName(String authenticationCacheName) {
this.authenticationCacheName = authenticationCacheName;
}
public boolean isAuthenticationCachingEnabled() {
return this.authenticationCachingEnabled && isCachingEnabled();
}
@SuppressWarnings({"UnusedDeclaration"})
public void setAuthenticationCachingEnabled(boolean authenticationCachingEnabled) {
this.authenticationCachingEnabled = authenticationCachingEnabled;
if (authenticationCachingEnabled) {
setCachingEnabled(true);
}
}
public void setName(String name) {
super.setName(name);
String authcCacheName = this.authenticationCacheName;
if (authcCacheName != null && authcCacheName.startsWith(getClass().getName())) {
this.authenticationCacheName = name + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
}
}
public boolean supports(AuthenticationToken token) {
return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
}
public final void init() {
getAvailableAuthenticationCache();
onInit();
}
protected void onInit() {
}
protected void afterCacheManagerSet() {
getAvailableAuthenticationCache();
}
private Cache<Object, AuthenticationInfo> getAvailableAuthenticationCache() {
Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
boolean authcCachingEnabled = isAuthenticationCachingEnabled();
if (cache == null && authcCachingEnabled) {
cache = getAuthenticationCacheLazy();
}
return cache;
}
private Cache<Object, AuthenticationInfo> getAuthenticationCacheLazy() {
if (this.authenticationCache == null) {
log.trace("No authenticationCache instance set. Checking for a cacheManager...");
CacheManager cacheManager = getCacheManager();
if (cacheManager != null) {
String cacheName = getAuthenticationCacheName();
log.debug("CacheManager [{}] configured. Building authentication cache '{}'", cacheManager, cacheName);
this.authenticationCache = cacheManager.getCache(cacheName);
}
}
return this.authenticationCache;
}
private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) {
AuthenticationInfo info = null;
Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
if (cache != null && token != null) {
log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
Object key = getAuthenticationCacheKey(token);
info = cache.get(key);
if (info == null) {
log.trace("No AuthorizationInfo found in cache for key [{}]", key);
} else {
log.trace("Found cached AuthorizationInfo for key [{}]", key);
}
}
return info;
}
private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {
if (!isAuthenticationCachingEnabled(token, info)) {
if (log.isDebugEnabled()) {
log.debug("AuthenticationInfo caching is disabled for info [{}]. Submitted token: [{}].", info, token);
}
return;
}
Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
if (cache != null) {
Object key = getAuthenticationCacheKey(token);
cache.put(key, info);
if (log.isTraceEnabled()) {
log.trace("Cached AuthenticationInfo for continued authentication. key=[{}], value=[{}].", key, info);
}
}
}
protected boolean isAuthenticationCachingEnabled(AuthenticationToken token, AuthenticationInfo info) {
return isAuthenticationCachingEnabled();
}
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = getCachedAuthenticationInfo(token);
if (info == null) {
info = doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
cacheAuthenticationInfoIfPossible(token, info);
}
} else {
if (log.isDebugEnabled()) {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
}
if (info != null) {
assertCredentialsMatch(token, info);
} else {
if (log.isDebugEnabled()) {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
}
return info;
}
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
if (!cm.doCredentialsMatch(token, info)) {
String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
throw new IncorrectCredentialsException(msg);
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}
protected Object getAuthenticationCacheKey(AuthenticationToken token) {
return token != null ? token.getPrincipal() : null;
}
protected Object getAuthenticationCacheKey(PrincipalCollection principals) {
return getAvailablePrincipal(principals);
}
@Override
protected void doClearCache(PrincipalCollection principals) {
super.doClearCache(principals);
clearCachedAuthenticationInfo(principals);
}
protected void clearCachedAuthenticationInfo(PrincipalCollection principals) {
if (!CollectionUtils.isEmpty(principals)) {
Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
if (cache != null) {
Object key = getAuthenticationCacheKey(principals);
cache.remove(key);
}
}
}
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
}