一、总体图
二、结合实际代码,源码看流程。
1、通过Shiro相关的API创建SecurityManager得到一个Subjetc主体。
//1、获取SecurityManager工厂,此处使用(shiro.ini)初始化配置文件。
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//2、得到SecurityManager实例
SecurityManager securityManager = factory.getInstance();
//3、将SecurityManager设置到运行环境中
SecurityUtils.setSecurityManager(securityManager);
//4、通过SecurityUtils获取Subject主体
Subject subject = SecurityUtils.getSubject();
2、封装用户的认证信息(用户、密码)
这里直接调用构造方法进行创建。
//5、假设登录的用户名 密码是Jame 123(表示用户登录时输入信息 shiro.ini相当于数据库存信息)
UsernamePasswordToken token=new UsernamePasswordToken("James","123");
3、通过Subject.login(token) 进行用户认证
Subjetc接收用户信息,Subject是一个接口,通过调用其实现类DelegatingSubject (idea下 Ctrl+H可调出实现类)将Token委托给SecurityManager接口来完成认证。
在DelegatingSubject中有一个login()方法,在此方法中Shiro将token交给了SecurityManager接口。
SecurityManager继承了Authenticator、Authorizer、SessionManager等父类,自己又有非常多的实现类。每一层实现类都是对SecurityManager功能的增强。
SecurityManager接口是通过其实现类DefaultSecurityManager中的来login方法来完成认证过程。
在login方法中AuthenticationInfo含有身份和凭证信息,然后通过authenticate方法调用AuthenticatingSecurityManage类中的authenticate(token),将token传入。
在AuthenticatingSecurityManager.class中的authenticate()认证器是由默认实现类ModularRealmAuthenticator.class来完成认证
ModularRealmAuthenticator.class 中doAuthenticate 获取Realms信息。
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
this.assertRealmsConfigured();
Collection<Realm> realms = this.getRealms();
return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
}
在doAuthenticate方法运用了三目表达式。如果是单个Realm,就会调用doSingleRealmAuthentication( )
如果是单Realm 直接将realm和token中的信息进行比较判断是否成功。
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;
}
}
}
- 如果多realm那么需要通过AuthenticationStrategy strategy 认证策略信息来完成对应的认证工作。如果认证失败会指定抛出异常信息。
-
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { AuthenticationStrategy strategy = this.getAuthenticationStrategy(); AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); if (log.isTraceEnabled()) { log.trace("Iterating through {} realms for PAM authentication", realms.size()); } Iterator var5 = realms.iterator(); while(var5.hasNext()) { Realm realm = (Realm)var5.next(); aggregate = strategy.beforeAttempt(realm, token, aggregate); if (realm.supports(token)) { log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); AuthenticationInfo info = null; Throwable t = null; try { info = realm.getAuthenticationInfo(token); } catch (Throwable var11) { t = var11; if (log.isDebugEnabled()) { String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; log.debug(msg, var11); } } aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); } else { log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); } } aggregate = strategy.afterAllAttempts(token, aggregate); return aggregate; }
Shiro的认证过程至此结束。