在shrio中,AuthenticationToken用于存储前端传来的登录信息,通俗来说就是用户名及密码等。而在这之中比较常用的就是UsernamePasswordToken。为了了解shrio的验证过程有必要先了解与验证相关的主要的几个类。首先看下shrio对UsernamePasswordToken的描述:
/**
* A simple username/password authentication token to support the most widely-used authentication mechanism. This
* class also implements the {@link RememberMeAuthenticationToken RememberMeAuthenticationToken} interface to support
* "Remember Me" services across user sessions as well as the
* {@link org.apache.shiro.authc.HostAuthenticationToken HostAuthenticationToken} interface to retain the host name
* or IP address location from where the authentication attempt is occurring.
*
* "Remember Me" authentications are disabled by default, but if the application developer wishes to allow
* it for a login attempt, all that is necessary is to call {@link #setRememberMe setRememberMe(true)}. If the underlying
* SecurityManager implementation also supports RememberMe services, the user's identity will be
* remembered across sessions.
*
* Note that this class stores a password as a char[] instead of a String
* (which may seem more logical). This is because Strings are immutable and their
* internal value cannot be overwritten - meaning even a nulled String instance might be accessible in memory at a later
* time (e.g. memory dump). This is not good for sensitive information such as passwords. For more information, see the
*
* Java Cryptography Extension Reference Guide.
*
* To avoid this possibility of later memory access, the application developer should always call
* {@link #clear() clear()} after using the token to perform a login attempt.
*
* @since 0.1
*/
public class UsernamePasswordToken implements HostAuthenticationToken, RememberMeAuthenticationToken {
这段注释对UsernamePasswordToken这个类做了十分详尽的说明。UsernamePasswordToken是一个简单的包含username及password即用户名及密码的登录验证用token,这个类同时继承了HostAuthenticationToken及RememberMeAuthenticationToken,因此UsernamePasswordToken能够存储客户端的主机地址以及确定token是否能够被后续的方法使用。值得注意的是在这里password是以char数组的形式存储的,这样设计是因为String本身是不可变的,这可能会导致无效的密码被访问到。另外,开发者应在验证之后调用clear方法清除token从而杜绝该token在后续被访问的可能性。
UserNamePasswordToken类的主要属性如下及用户名,密码,是否记住token以及验证来源的host主机地址:
/**
* The username
*/
private String username;
/**
* The password, in char[] format
*/
private char[] password;
/**
* Whether or not 'rememberMe' should be enabled for the corresponding login attempt;
* default is false
*/
private boolean rememberMe = false;
/**
* The location from where the login attempt occurs, or null
if not known or explicitly
* omitted.
*/
private String host;
/**
* Simply returns {@link #getUsername() getUsername()}.
*
* @return the {@link #getUsername() username}.
* @see org.apache.shiro.authc.AuthenticationToken#getPrincipal()
*/
public Object getPrincipal() {
return getUsername();
}
/**
* Returns the username submitted during an authentication attempt.
*
* @return the username submitted during an authentication attempt.
*/
public String getUsername() {
return username;
}
一般来说,我们会通过调用getPrincipal()方法来获取token中的用户名并以此来进行登录验证而本方法的实质就是通过调用本地getUserName()方法直接获取存储在token中的username。
/**
* Returns the {@link #getPassword() password} char array.
*
* @return the {@link #getPassword() password} char array.
* @see org.apache.shiro.authc.AuthenticationToken#getCredentials()
*/
public Object getCredentials() {
return getPassword();
}
/**
* Returns the password submitted during an authentication attempt as a character array.
*
* @return the password submitted during an authentication attempt as a character array.
*/
public char[] getPassword() {
return password;
}
一般来说我们通过getCredentials()方法来获取token中的密码,该方法与获取用户名的方式一致,调用本地getPassword()方法获取存储在token中的密码。
/**
* Clears out (nulls) the username, password, rememberMe, and inetAddress. The password bytes are explicitly set to
* 0x00 before nulling to eliminate the possibility of memory access at a later time.
*/
public void clear() {
this.username = null;
this.host = null;
this.rememberMe = false;
if (this.password != null) {
for (int i = 0; i < password.length; i++) {
this.password[i] = 0x00;
}
this.password = null;
}
}
为了防止验证完成后的后续访问,我们应在验证完成后调用clear方法清除token中数据,这里需要注意的是UsernamePasswordToken类在clear()方法中对password做了处理而不是单纯地将password的引用置null,这里首先将password引用对应的char数组的值全部置0然后再将引用断开。这里我的理解是虽然JVM会调用GC将不可达的根节点的对象回收,但是GC本身是不定时的即不是实时清除垃圾对象,因此在这里为了防止password在被GC清除前仍能被访问到,这里将char数组里的值全部置0从而根本上杜绝非法访问。
/**
* Returns the String representation. It does not include the password in the resulting
* string for security reasons to prevent accidentally printing out a password
* that might be widely viewable).
*
* @return the String representation of the UsernamePasswordToken, omitting
* the password.
*/
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName());
sb.append(" - ");
sb.append(username);
sb.append(", rememberMe=").append(rememberMe);
if (host != null) {
sb.append(" (").append(host).append(")");
}
return sb.toString();
}
这里没什么好说的,创建一个StringBuilder对象将用户名等信息拼接起来并返回。注意为了安全起见这里不会将密码拼接进返回的String中。