在前端输入用户名和密码,shiro是如何校验用户的信息完成登录的呢。
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getUserPwd());
Subject subject = SecurityUtils.getSubject();
subject.login(token);
userVO = (UserVO) subject.getPrincipal();
上面有一行代码subject.login(token);
当使用该方法时,会找到接口interface Subject的login方法,实际是进入了subject接口的实现类DelegatingSubject中:
public void login(AuthenticationToken token) throws AuthenticationException {
this.clearRunAsIdentitiesInternal();
Subject subject = this.securityManager.login(this, token);
String host = null;
PrincipalCollection principals;
if (subject instanceof DelegatingSubject) {
DelegatingSubject delegating = (DelegatingSubject)subject;
principals = delegating.principals;
host = delegating.host;
} else {
principals = subject.getPrincipals();
}
if (principals != null && !principals.isEmpty()) {
this.principals = principals;
this.authenticated = true;
if (token instanceof HostAuthenticationToken) {
host = ((HostAuthenticationToken)token).getHost();
}
if (host != null) {
this.host = host;
}
Session session = subject.getSession(false);
if (session != null) {
this.session = this.decorate(session);
} else {
this.session = null;
}
} else {
String msg = "Principals returned from securityManager.login( token ) returned a null or empty value. This value must be non null and populated with one or more elements.";
throw new IllegalStateException(msg);
}
}
执行该方法,当执行到Subject subject = this.securityManager.login(this, token)时,进入securityManager接口的实现类DefaultSecurityManager中:
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info;
try {
info = this.authenticate(token);
} catch (AuthenticationException var7) {
AuthenticationException ae = var7;
try {
this.onFailedLogin(token, ae, subject);
} catch (Exception var6) {
if (log.isInfoEnabled()) {
log.info("onFailedLogin method threw an exception. Logging and propagating original AuthenticationException.", var6);
}
}
throw var7;
}
Subject loggedIn = this.createSubject(token, info, subject);
this.onSuccessfulLogin(token, info, loggedIn);
return loggedIn;
}
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
return this.authenticator.authenticate(token);
}
执行方法info = this.authenticate(token), 顶层实现了Authenticator 接口:
Authenticator 接口以及实现类AbstractAuthenticator.class获取认证信息:
public interface Authenticator {
AuthenticationInfo authenticate(AuthenticationToken var1) throws AuthenticationException;
}
//Authenticator接口的实现类 AbstractAuthenticator
public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
if (token == null) {
throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
} else {
log.trace("Authentication attempt received for token [{}]", token);
AuthenticationInfo info;
try {
info = this.doAuthenticate(token);
if (info == null) {
String msg = "No account information found for authentication token [" + token + "] by this " + "Authenticator instance. Please check that it is configured correctly.";
throw new AuthenticationException(msg);
}
} catch (Throwable var8) {
AuthenticationException ae = null;
if (var8 instanceof AuthenticationException) {
ae = (AuthenticationException)var8;
}
if (ae == null) {
String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected " + "error? (Typical or expected login exceptions should extend from AuthenticationException).";
ae = new AuthenticationException(msg, var8);
if (log.isWarnEnabled()) {
log.warn(msg, var8);
}
}
try {
this.notifyFailure(token, ae);
} catch (Throwable var7) {
if (log.isWarnEnabled()) {
String msg = "Unable to send notification for failed authentication attempt - listener error?. Please check your AuthenticationListener implementation(s). Logging sending exception and propagating original AuthenticationException instead...";
log.warn(msg, var7);
}
}
throw ae;
}
log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info);
this.notifySuccess(token, info);
return info;
}
}
protected abstract AuthenticationInfo doAuthenticate(AuthenticationToken var1) throws AuthenticationException;
ModularRealmAuthenticator 继承AbstractAuthenticator,重写了方法doAuthenticate:
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
this.assertRealmsConfigured();
Collection realms = this.getRealms();
return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
}
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" + token + "]. Please ensure that the appropriate Realm implementation is " + "configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
} else {
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " + "submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
} else {
return info;
}
}
}
AuthenticationInfo info = realm.getAuthenticationInfo(token)方法,追寻到Realm接口的实现类AuthenticatingRealm:
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) {
this.assertCredentialsMatch(token, info);
} else {
log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token);
}
return info;
}
使用shiro认证真正主要我们写的就是获取认证数据,MyShiroRealm extends AuthorizingRealm,重写doGetAuthenticationInfo方法:
/**
* 认证信息.(身份验证) : Authentication 是用来验证用户身份
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String name = token.getUsername();
// 从数据库获取对应用户名密码的用户
R r = xxx.getUserByUserName(name);
if (null == r || !r.get("code").toString().equals("200") || !r.containsKey("data") || null == r.get("data")) {
throw new UnknownAccountException();
}
UserVO userVO = R.getData(r, UserVO.class);
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userVO, userVO.getUserPwd(), ByteSource.Util.bytes(userVO.getUserSalt()), getName());
return authenticationInfo;
}
throw new UnknownAccountException();
}
shiro对用户信息的校验完成,即用户合法,不会进行拦截,就会按照我们编辑的逻辑跳转至页面。