該接口是cas-server認(rèn)證入口,但從唯一實(shí)現(xiàn)類(lèi)來(lái)看,它并不是認(rèn)證執(zhí)行者,執(zhí)行者是AuthenticationHandler,該接口控制認(rèn)證流程,包括異常處理和結(jié)果封裝,將認(rèn)證結(jié)果封裝成Authentication返回。
public interface AuthenticationManager {
//有一個(gè)Credential認(rèn)證成功返回Authentication,所有Credential都認(rèn)證失敗拋出AuthenticationException
Authentication authenticate(AuthenticationTransaction authenticationTransaction) throws AuthenticationException;
}
唯一實(shí)現(xiàn)PolicyBasedAuthenticationManager
核心代碼
AuthenticationBuilder builder = new DefaultAuthenticationBuilder(NullPrincipal.getInstance());
builder.addCredential(new BasicCredentialMetaData(credential));
//執(zhí)行認(rèn)證
HandlerResult result = authenticationHandler.authenticate(credential);
//一個(gè)credential認(rèn)證成功
builder.addSuccess(authenticationHandler.getName(), result);
//一個(gè)credential認(rèn)證失敗
builder.addFailure(authenticationHandler.getName(), e.getClass());
//credential解析,Principal才是客戶真正需要的
Principal principal = principalResolver.resolve(credential);
builder.setPrincipal(principal);
//構(gòu)建authentication
Authentication authentication = builder.build();
//那些情況拋出AuthenticationException?
//一個(gè)credential都沒(méi)認(rèn)證成功
if (builder.getSuccesses().isEmpty()) {
throw new AuthenticationException(builder.getFailures(), builder.getSuccesses());
}
//不滿足認(rèn)證安全策略
if (!this.authenticationPolicy.isSatisfiedBy(builder.build())) {
throw new AuthenticationException(builder.getFailures(), builder.getSuccesses());
}
//空Principal
if (principal instanceof NullPrincipal) {
throw new UnresolvedPrincipalException(authentication);
}
相關(guān)接口
Principal
客戶端拿著serviceTicket來(lái)驗(yàn)證時(shí),驗(yàn)證通過(guò),服務(wù)器會(huì)返回一個(gè)Principal實(shí)現(xiàn),默認(rèn)只返回Principal的id,也可以附帶返回attributes,需要自己修改xml(jsp)
- SimplePrincipal
簡(jiǎn)單實(shí)現(xiàn),有id和attributes,沒(méi)有其他東西 - NullPrincipal
一個(gè)空實(shí)現(xiàn),id固定為字符串nobody,attributes為一個(gè)EmptyMap實(shí)例 - WebApplicationService
這個(gè)實(shí)現(xiàn)Principal比較奇怪,跟Principal的含義沒(méi)半毛錢(qián)關(guān)系,只是要用到id屬性,關(guān)于WebApplicationService 后面再寫(xiě)
Credential
可以把它看成用戶登錄時(shí)輸入登錄信息的實(shí)體,實(shí)際上它就是表示這個(gè)意思??纯磳?shí)現(xiàn)就知道了
- UsernamePasswordCredential
包含username、password基本的登錄信息,系統(tǒng)默認(rèn)用這個(gè),可看WEB-INF/webflow/login/login-webflow.xml - RememberMeUsernamePasswordCredential
繼承UsernamePasswordCredential ,增加了rememberMe,是不是登錄時(shí)候的那些東西 - BasicIdentifiableCredential
這個(gè)更簡(jiǎn)潔,只有一個(gè)id屬性,系統(tǒng)沒(méi)有那個(gè)地方用到它 - OneTimePasswordCredential
一次性密碼,系統(tǒng)也沒(méi)有地方使用 - HttpBasedServiceCredential
通過(guò)回調(diào)callbackUrl來(lái)驗(yàn)證,回調(diào)成功表示驗(yàn)證通過(guò),該Credential 綁定了關(guān)聯(lián)的registeredService,getId返回的是callbackUrl字符串。
AuthenticationHandler
認(rèn)證的執(zhí)行者,類(lèi)似dao,會(huì)跟數(shù)據(jù)源打交道
- HttpBasedServiceCredentialsAuthenticationHandler
代理認(rèn)證,用于HttpBasedServiceCredential認(rèn)證,里面有一個(gè)HttpClient(注入),當(dāng)RegisteredService的ProxyPolicy接受CallbackUrl且HttpClient請(qǐng)求CallbackUrl成功(返回指定的code)表示認(rèn)證通過(guò) - AbstractUsernamePasswordAuthenticationHandler
抽象類(lèi),用于UsernamePasswordCredential的認(rèn)證,子類(lèi)一般會(huì)使用外部數(shù)據(jù)源查庫(kù),如使用JDBC,只需依賴cas-server-support-jdbc支持。 - JaasAuthenticationHandler
使用JAAS認(rèn)證,用的不多,具體配置可以看源碼 - AcceptUsersAuthenticationHandler
接受一個(gè)靜態(tài)的用戶數(shù)據(jù)驗(yàn)證,默認(rèn)去cas.properties中accept.authn.users值進(jìn)行校驗(yàn),系統(tǒng)默認(rèn)用這種方式,實(shí)際中肯定要換成第三方數(shù)據(jù)源的。
PrincipalResolver
從給定的credential解析出一個(gè)Principal
- PersonDirectoryPrincipalResolver
使用Jasig Person Directory庫(kù)解析,當(dāng)客戶端除了需要id外,還需要更多的用戶屬性時(shí),推薦使用這個(gè)。通過(guò)使用IPersonAttributeDao返回Map<String, List<Object>>里面可存放關(guān)聯(lián)的屬性。默認(rèn)的實(shí)現(xiàn)StubPersonAttributeDao只是簡(jiǎn)單的維護(hù)了一個(gè)靜態(tài)backingMap,實(shí)際中建議連接數(shù)據(jù)源進(jìn)行動(dòng)態(tài)查詢,具體可看IPersonAttributeDao實(shí)現(xiàn)。 - BasicPrincipalResolver
解析工作完全委托給PrincipalFactory,自己什么也不做
AuthenticationPolicy
安全策略檢查,相當(dāng)于這里設(shè)置最后一道關(guān)卡,檢查Authentication的合法性,如果沒(méi)有特別的需求用默認(rèn)的即可
- AnyAuthenticationPolicy
類(lèi)里有一個(gè)標(biāo)志位tryAll,取cas.properties中cas.authn.policy.any.tryall值,默認(rèn)false
true:Authentication在Credentials數(shù)量必須等于Successes+Failures的數(shù)量
false:Successes不為空就行 - NotPreventedAuthenticationPolicy
繼承AnyAuthenticationPolicy - AllAuthenticationPolicy,增加了一個(gè)條件
public boolean isSatisfiedBy(final Authentication authentication) {
for (final String handler : authentication.getFailures().keySet()) {
if (authentication.getFailures().get(handler).isAssignableFrom(PreventedException.class)) {
return false;
}
}
return super.isSatisfiedBy(authentication);
}
- RequiredHandlerAuthenticationPolicy
//requiredHandlerName值取cas.properties中cas.authn.policy.req.handlername,默認(rèn)handlerName,tryAll取cas.authn.policy.req.tryall,默認(rèn)false
public boolean isSatisfiedBy(final Authentication authn) {
boolean credsOk = true;
if (this.tryAll) {
credsOk = authn.getCredentials().size() == authn.getSuccesses().size() + authn.getFailures().size();
}
return credsOk && StringUtils.isNotBlank(this.requiredHandlerName) && authn.getSuccesses().containsKey(this.requiredHandlerName);
}
- ContextualAuthenticationPolicy
接口,更復(fù)雜的校驗(yàn),如獲取某一個(gè)策略Context檢驗(yàn)Authentication,具體Context看具體實(shí)現(xiàn)
至此cas-server的Credential 認(rèn)證部分就這些內(nèi)容了,看起來(lái)比較簡(jiǎn)單,但這種架構(gòu)設(shè)計(jì)非常值得我們學(xué)習(xí),相信在大部分業(yè)務(wù)場(chǎng)景中都會(huì)用到,這里cas-server提供了良好的擴(kuò)展性,方便第三方庫(kù)集成。主要還是用到了面向接口編程。