Realm相关源码分析
在上一章的分析中提到了Realm的supports函数和getAuthenticationInfo,下面就来看。参照第四章开头的Spring配置,最后得到的是一个AuthorizingRealm。
先看一下AuthorizingRealm的继承关系,
public abstract class AuthorizingRealm extends AuthenticatingRealm
implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware
public abstract class AuthenticatingRealm extends CachingRealm implements Initializable
public abstract class CachingRealm implements Realm, Nameable, CacheManagerAware, LogoutAware
AuthorizingRealm
首先看一下AuthorizingRealm的构造函数
public AuthorizingRealm() {
this(null, null);
}
public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
super();
if (cacheManager != null) setCacheManager(cacheManager);
if (matcher != null) setCredentialsMatcher(matcher);
this.authorizationCachingEnabled = true;
this.permissionResolver = new WildcardPermissionResolver();
int instanceNumber = INSTANCE_COUNT.getAndIncrement();
this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
if (instanceNumber > 0) {
this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
}
}
AuthorizingRealm主要就构造了一个WildcardPermissionResolver用来解析权限,该构造函数为空函数。
AuthorizingRealm的父类AuthenticatingRealm的构造函数如下所示,
public AuthenticatingRealm() {
this(null, new SimpleCredentialsMatcher());
}
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);
}
}
该构造函数也是简单的赋值,这里就不分析了。
createToken
下面先分析一下createToken函数,该函数通过request和response构造一个AuthenticationToken,定义在
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
return createToken(username, password, request, response);
}
getUsername和getPassword中就是获得request中的参数,这里的createToken定义在AuthenticatingFilter中,
protected AuthenticationToken createToken(String username, String password,
ServletRequest request, ServletResponse response) {
boolean rememberMe = isRememberMe(request);
String host = getHost(request);
return createToken(username, password, rememberMe, host);
}
isRememberMe定义在FormAuthenticationFilter中,
protected boolean isRememberMe(ServletRequest request) {
return WebUtils.isTrue(request, getRememberMeParam());
}
这个createToken定义在AuthenticatingFilter中,
protected AuthenticationToken createToken(String username, String password,
boolean rememberMe, String host) {
return new UsernamePasswordToken(username, password, rememberMe, host);
}
UsernamePasswordToken的构造函数就是简单的赋值。
supports
public boolean supports(AuthenticationToken token) {
return token != null && getAuthenticationTokenClass().isAssignableFrom(token.getClass());
}
getAuthenticationTokenClass返回前面在AuthenticatingRealm构造函数中的UsernamePasswordToken,isAssignableFrom用来判断token是否为UsernamePasswordToken的实例。
getAuthenticationInfo
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 {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
if (info != null) {
assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
这里首先调用getCachedAuthenticationInfo从缓存中获取登录信息,如果缓存中没有,就再调用doGetAuthenticationInfo进行验证,假设验证成功,然后调用cacheAuthenticationInfoIfPossible和assertCredentialsMatch。下面一一分析。
getCachedAuthenticationInfo
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;
}
这里首先获得缓存cache,然后通过getAuthenticationCacheKey计算Key并从缓存中提取登录信息。其实前面默认的构造函数中,该cache为null。getAuthenticationCacheKey通过getPrincipal()获取Key,
protected Object getAuthenticationCacheKey(AuthenticationToken token) {
return token != null ? token.getPrincipal() : null;
}
doGetAuthenticationInfo
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;
doGetAuthenticationInfo并未定义,所以在实际项目中必须自定义一个Realm继承自AuthorizingRealm并实现该方法。
cacheAuthenticationInfoIfPossible
private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {
if (!isAuthenticationCachingEnabled(token, info)) {
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);
log.trace("Cached AuthenticationInfo for continued authentication. key=[{}], value=[{}].", key, info);
}
}
该函数就是把刚刚的登录信息添加到缓存中,下次要验证时直接从缓存中提取登录信息。
assertCredentialsMatch
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.");
}
}
getCredentialsMatcher返回前面在AuthenticatingRealm构造函数中的SimpleCredentialsMatcher,其doCredentialsMatch如下,
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenCredentials = getCredentials(token);
Object accountCredentials = getCredentials(info);
return equals(tokenCredentials, accountCredentials);
}
这里就是验证两者credential是否相等啦。