<!--[if !supportLists]-->1、 前面已经介绍完了创建subject用户和封装shiro自己的session,这里介绍用户的登录和退出。
<!--[endif]-->直登录直接调用subject.login(token)方法即可,因为我们在创建subject的时候就把securityManager传入到了subject中,所以subject可以调用securManager的方法,当调用login时,就会调用securityManager会调用里面的login方法。
我们使用的securityManager是DefaultSecurityManager的子类DefaultWebSecurityManager,所以调用的是defaultsecurityManager中的login方法,它里面继续调用了authenticate方法,这个方法的实现在其父类AuthenticatingSecurityManager中,这个类中有一个属性authenticator是Authenticator类型的,然后在构造方法中会生成一个ModularRealmAuthenticator。这里看一下authenticator类和ModularRealmAuthenticator类。
我们看一下他的authenticator的实现AbstractAuthenticator,他的里面有个属性listeners,用户捕获用户的登录失败,登录成功的事件,然后进行相应的处理,当用户退出的时候也会进行通知。
这个类中最重要的方法就是authenticate方法,里面调用了doAuthenticate方法,该方法在ModularRealmAuthenticator类的实现,先看一下这个类的构造方法
public ModularRealmAuthenticator() {
this.authenticationStrategy = new AtLeastOneSuccessfulStrategy();
}
生成一个登录校验的策略——只要有一个realm校验成功即可。这个是用于多个reaml时才会用到的,如果我们配置了一个realm时不用管他。继续看他的实现的doAuthenticate方法。
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection<Realm> realms = getRealms();//
if (realms.size() ==1) {
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
return doMultiRealmAuthentication(realms, authenticationToken);
}
}
里面有个方法getRealms方法,所以我们必须知道他是如何传入的realm,可以在setRealms方法上打上断点然后debug,我们暂时不管它。
他是根据我们配置的realm的个数来区分调用的,因为我们都是只配一个realm,所以我们只看doSingleRealmAuthentication方法即可:
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.";
thrownew UnsupportedTokenException(msg);
}
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " +
"submitted AuthenticationToken [" + token + "].";
thrownew UnknownAccountException(msg);
}
returninfo;
}
可以发现这个方法就是调用的传入的realm的getAuthenticationInfo方法。
所以现在的关键是得到如何传入的realm。从断点可以发现,在
.RealmSecurityManager.setRealms(Collection<Realm>)这个方法中会执行afterRealmSet方法,而在AuthenticatingSecurityManager.afterRealmsSet()方法中会将传入的realm添加到authenticator(也就是ModularRealmAuthenticator)中,所以我们就明白了校验调用的方法就是传入的realm的getAuthenticationInfo方法。
protectedvoid afterRealmsSet() {
super.afterRealmsSet();
if (this.authenticatorinstanceof ModularRealmAuthenticator) {
((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
}
}
<!--[if !supportLists]-->2、 <!--[endif]-->在登陆之后会继续修改产生的subject,添加的属性有isAuthenticated,authenticationToken。authenticationInfo.在登陆成功之后会将发送的rememberMe的cookie重新发送,然后将校验通过的信息保存在session中.这个在DefaultSecurityManager中的login方法中可以发现。
<!--[if !supportLists]-->3、 <!--[endif]-->退出:我也是用debug的方法来查看的org.apache.shiro.subject.support.DelegatingSubject.logout()调用的是这个方法,源码如下:
try {
clearRunAsIdentitiesInternal();
this.securityManager.logout(this);//调用的还是securityManager,就是创建subject时绑定的securityManager。
} finally {
this.session = null;
this.principals = null;
this.authenticated = false;
}
我们查看一下调用的securityManager的logout。
beforeLogout(subject);//这个最终调动的是cookierememberMeManager的forgetIdentity方法,将cookie删掉。
PrincipalCollection principals = subject.getPrincipals();
if (principals != null && !principals.isEmpty()) {
Authenticator authc = getAuthenticator();
if (authcinstanceof LogoutAware) {
((LogoutAware) authc).onLogout(principals);//这个最终调用的是cacherealm中清楚缓存的authenticationInfo的方法,将之前缓存的信息清楚掉,当然我们默认是禁用了验证信息的缓存的。
}
}
try {
delete(subject);//这个是将把session中缓存的principalCollection和登录状态删掉。
} catch (Exception e) {
Xxx
} finally {
try {
stopSession(subject);//这个最终调用的是将httpSeesion销毁,即httpSession.invalidate();
} catch (Exception e) {
xxxx
}
}
}
可以发现,如果用户最后调用了logout,则下一次无法通过cookie获得用户信息了。