public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) { this.principals = new SimplePrincipalCollection(principal, realmName); this.credentials = hashedCredentials; this.credentialsSalt = credentialsSalt; }
上面是SimpleAuthenticationInfo源码的一个构造方法,这里第一个参数就是你刚才传入的用户名,第二个参数就是你传入的密码,但是 方法定义中这两个参数都是Object类型,尤其是第一个principal参数,它的意义远远不止用户名那么简单,它是用户的所有认证信息集合,登陆成 功后,
仔细看那个this.principals=new SimplePrincipalCollection,这一行,这一行构造了一个新的对象并将引用给了principals,而principals就是principalcollection。
再来看Principal那个标签
public int onDoStartTag() throws JspException {
String strValue = null; if (getSubject() != null) { // Get the principal to print out Object principal; if (type == null) { principal = getSubject().getPrincipal(); } else { principal = getPrincipalFromClassName(); } // Get the string value of the principal if (principal != null) { if (property == null) { strValue = principal.toString(); } else { strValue = getPrincipalProperty(principal, property); } } } // Print out the principal value if not null if (strValue != null) { try { pageContext.getOut().write(strValue); } catch (IOException e) { throw new JspTagException("Error writing [" + strValue + "] to JSP.", e); } } return SKIP_BODY; }
看到那个Object principal这个方法变量了么?如果标签里没有type属性,那么就直接调用getPrincipal方法,再打开这个方法,看到subject里是这么写的:
public Object getPrincipal() {
return getPrimaryPrincipal(getPrincipals());
}
getPrincipals是获取principalcollection,getprimaryprincipal就是获取这个 principalcollection的第一个元素,用的是迭代器的方式获取。具体的我不列出来了,请参见 SimplePrincipalcollection的源代码。
第一个元素你最初放的是用户名,所以你可以取得这个用户名。 如果type不为空,就会去principalcollection中找和这个type类型一致的一个对象,看源码:
private Object getPrincipalFromClassName() { Object principal = null; try { Class cls = Class.forName(type); principal = getSubject().getPrincipals().oneByType(cls); } catch (ClassNotFoundException e) { if (log.isErrorEnabled()) { log.error("Unable to find class for name [" + type + "]"); } } return principal; }
那个oneByType方法就是在principalcollection中找和type属性一致的那个类的对象,将其作为principal,如 果
所以你现在知道该怎么做了吧?
在你的realm里这么写:
List<Object> principals=new ArrayList<Object>(); principals.add(user.getUsername()); principals.add(user); SimpleAuthenticationInfo info=new SimpleAuthenticationInfo( principals, user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), this.getName());
构建一个list,第一个元素是用户名,第二个元素是user对象。第一个元素用来登陆,第二个元素用来获取登陆后user对象的属性。
他们到底怎么判断登陆的呢?
先参见HashedCredentialsMatcher类的这个方法,这是用来判断是否可以登录成功的方法:
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { Object tokenHashedCredentials = hashProvidedCredentials(token, info); Object accountCredentials = getCredentials(info); return equals(tokenHashedCredentials, accountCredentials); }
其中第一个参数token就是controller里封装的token,第二个参数info就是你刚才的
simpleauthenticationinfo,因为源码层次比较深所以简单点说,这个方法就是再判断两个密码是否相等,因为两个用户名肯定是相等的
info的用户名是token传过来的,所以只对比密码就可以了。
其中equals方法仍然是调用父类的方法,即一旦为ByteSource则进行byte匹配,否则进行引用匹配。只是这里的tokenHashedCredentials 和accountCredentials 和父类的方式不一样,如下:
protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
Object salt = null;
if (info instanceof SaltedAuthenticationInfo) {
salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
} else {
//retain 1.0 backwards compatibility:
if (isHashSalted()) {
salt = getSalt(token);
}
}
return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
}
protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
String hashAlgorithmName = assertHashAlgorithmName();
return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
}
可以看到仍然是使用算法名称和credentials(用户提交的未加密的)、salt、hash次数构建一个SimpleHash(构造时进行加密)。
再看对于已加密的credentials则是也构建一个SimpleHash,但是不再进行加密过程:
protected Object getCredentials(AuthenticationInfo info) {
Object credentials = info.getCredentials();
byte[] storedBytes = toBytes(credentials);
if (credentials instanceof String || credentials instanceof char[]) {
//account.credentials were a char[] or String, so
//we need to do text decoding first:
if (isStoredCredentialsHexEncoded()) {
storedBytes = Hex.decode(storedBytes);
} else {
storedBytes = Base64.decode(storedBytes);
}
}
AbstractHash hash = newHashInstance();
hash.setBytes(storedBytes);
return hash;
}
protected AbstractHash newHashInstance() {
String hashAlgorithmName = assertHashAlgorithmName();
return new SimpleHash(hashAlgorithmName);
}
对于HashedCredentialsMatcher也就是说AuthenticationToken token, AuthenticationInfo info都去构建一个SimpleHash,前者构建时执行加密过程,后者(已加密)不需要去执行加密过程,然后匹配这两个SimpleHash是否一致。然后就是HashedCredentialsMatcher的子类(全部被标记为已废弃),如Md5CredentialsMatcher:
public class Md5CredentialsMatcher extends HashedCredentialsMatcher {
public Md5CredentialsMatcher() {
super();
setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);
}
}
仅仅是将HashedCredentialsMatcher的算法改为md5,所以Md5CredentialsMatcher 本身就没有存在的价值。HashedCredentialsMatcher其他子类都是同样的道理。
至此CredentialsMatcher的三个分支都完成了。