AuthenticatingRealm的认证流程(简要)
推荐资源
从login()到Realm的doGetAuthenticationInfo之间发生了什么?
Realm类图
getAuthenticationInfo方法
AuthenticatingRealm的getAuthenticationInfo方法有两点功能:
- 通过缓存或者Realm的doGetAuthenticationInfo方法来获取AuthenticationInfo
- 校验AuthenticationToken与AuthenticationInfo的Credentials是否一致
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
if (info == null) {
info = this.doGetAuthenticationInfo(token);
log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
if (token != null && info != null) {
this.cacheAuthenticationInfoIfPossible(token, info);
}
} else {
log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
}
if (info != null) {
// 校验AuthenticationToken与AuthenticationInfo的Credentials是否一致
this.assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
assertCredentialsMatch方法
assertCredentialsMatch方法在getAuthenticationInfo方法中被调用,用以校验AuthenticationToken与AuthenticationInfo的Credentials是否一致。
如果校验失败,则抛出相应的异常,意味着整个认证过程失败。
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
CredentialsMatcher cm = this.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.");
}
}
注意:Realm的CredentialsMatcher是可以自定义的。
自定义CredentialsMatcher
如需要自定义CredentialsMatcher,只需要实现CredentialsMatcher接口,在doCredentialsMatch方法中校验info与token的Credentials是否一致即可。
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
public class MyCredentialsMatcher implements CredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
String password = (String)authenticationInfo.getCredentials();
String arg = new String((char[])authenticationToken.getCredentials());
return password.equals(arg);
}
}
附:配置shrio配置文件
myMatcher=matcher.MyCredentialsMatcher
myRealm=realm.MyRealm
myRealm.credentialsMatcher=$myMatcher
securityManager.realms=$myRealm
认证过程简单示例
在本例中(单Realm且R自定义Realm继承自AuthorizingRealm),执行Subject.login()后,Shiro会层层调用至AuthenticatingRealm的getAuthenticationInfo并在其中调用自定义Realm的doGetAuthenticationInfo方法。
在自定义Realm类的doGetAuthenticationInfo方法中,我们在数据库中查询了用户的账户与密码,构造了SimpleAuthenticationInfo对象并返回。
然后,自定义Realm类的父类AuthenticatingRealm会执行assertCredentialsMatch方法进行Credentials的校验。
package realm;
import entity.User;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import service.UserService;
import service.UserServiceImpl;
public class MyRealm extends AuthorizingRealm {
private UserService userService = new UserServiceImpl();
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获得输入的用户名及密码
String username = (String) authenticationToken.getPrincipal();
String password = new String((char[])authenticationToken.getCredentials());
// 从数据库中查询用户
User user = userService.findByUsername(username);
if(user == null)
throw new UnknownAccountException("不存在的用户名!");
// 返回认证信息
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),getName());
}
}