4 源碼解析
4.1 Server源碼解析
Cas server端采用Spring WebFlow來(lái)進(jìn)行流程控制,因此本文以系統(tǒng)webflow文件為切入點(diǎn),對(duì)流程相關(guān)源碼進(jìn)行分析。Cas系統(tǒng)的webflow文件位于WEB-INF/webflow目錄下,分為登陸流程和登出流程。
4.1.1 登陸流程解析
4.1.1.1 訪問(wèn)接入Cas系統(tǒng)的應(yīng)用系統(tǒng)Client1
登陸流程配置文件為login-webflow.xm。
瀏覽器首次訪問(wèn)配置了單點(diǎn)登錄的應(yīng)用系統(tǒng)時(shí)(http://www.client1.com/index),Client1會(huì)將請(qǐng)求重定向到cas系統(tǒng)
(http://www.casserver.com/serviceValidate?service=http://www.client1.com/index)
cas系統(tǒng)接收到瀏覽器發(fā)來(lái)的請(qǐng)求,整個(gè)登錄流程從此處開(kāi)始,流程初始化。
WEB-INF/login-webflow.xml部分代碼:
<on-start>
<evaluate expression="initialFlowSetupAction"/>
</on-start>
初始化部分會(huì)調(diào)用InitialFlowSetupAction類的doExecute方法,如果有特殊需求,可以在此方法中增加相應(yīng)的邏輯。
InitialFlowSetupAction的doExecute方法:
@Override
protected Event doExecute(final RequestContext context) throws Exception {
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
final String contextPath = context.getExternalContext().getContextPath();
final String cookiePath = StringUtils.isNotBlank(contextPath) ? contextPath + '/' : "/";
if (StringUtils.isBlank(warnCookieGenerator.getCookiePath())) {
logger.info("Setting path for cookies for warn cookie generator to: {} ", cookiePath);
this.warnCookieGenerator.setCookiePath(cookiePath);
} else {
logger.debug("Warning cookie path is set to {} and path {}", warnCookieGenerator.getCookieDomain(),
warnCookieGenerator.getCookiePath());
}
if (StringUtils.isBlank(ticketGrantingTicketCookieGenerator.getCookiePath())) {
logger.info("Setting path for cookies for TGC cookie generator to: {} ", cookiePath);
this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath);
} else {
logger.debug("TGC cookie path is set to {} and path {}", ticketGrantingTicketCookieGenerator.getCookieDomain(),
ticketGrantingTicketCookieGenerator.getCookiePath());
}
//將TGT放在FlowScope作用域中
WebUtils.putTicketGrantingTicketInScopes(context,
this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request));
//將warnCookieValue放在FlowScope作用域中
WebUtils.putWarningCookie(context,
Boolean.valueOf(this.warnCookieGenerator.retrieveCookieValue(request)));
//獲取service參數(shù)
final Service service = WebUtils.getService(this.argumentExtractors, context);
if (service != null) {
logger.debug("Placing service in context scope: [{}]", service.getId());
final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
if (registeredService != null && registeredService.getAccessStrategy().isServiceAccessAllowed()) {
logger.debug("Placing registered service [{}] with id [{}] in context scope",
registeredService.getServiceId(),
registeredService.getId());
WebUtils.putRegisteredService(context, registeredService);
final RegisteredServiceAccessStrategy accessStrategy = registeredService.getAccessStrategy();
if (accessStrategy.getUnauthorizedRedirectUrl() != null) {
logger.debug("Placing registered service's unauthorized redirect url [{}] with id [{}]in context scope",
accessStrategy.getUnauthorizedRedirectUrl(),
registeredService.getServiceId());
WebUtils.putUnauthorizedRedirectUrl(context, accessStrategy.getUnauthorizedRedirectUrl());
}
}
} else if (!this.enableFlowOnAbsentServiceRequest) {
logger.warn("No service authentication request is available at [{}]. CAS is configured to disable the flow.",
WebUtils.getHttpServletRequest(context).getRequestURL());
throw new NoSuchFlowExecutionException(context.getFlowExecutionContext().getKey(),
new UnauthorizedServiceException("screen.service.required.message", "Service is required"));
}
//將service放在FlowScope作用域中
WebUtils.putService(context, service);
return result("success");
}
InitialFlowSetupAction的doExecute要做的就是把ticketGrantingTicketId,warnCookieValue和service放到FlowScope的作用域中,以便在登錄流程中的state中進(jìn)行判斷。初始化完成后,登錄流程流轉(zhuǎn)到第一個(gè)state(ticketGrantingTicketExistsCheck)。
<action-state id="ticketGrantingTicketCheck">
<evaluate expression="ticketGrantingTicketCheckAction"/>
<transition on="notExists" to="gatewayRequestCheck"/>
<transition on="invalid" to="terminateSession"/>
<transition on="valid" to="hasServiceCheck"/>
</action-state>
ticketGrantingTicketCheckAction的doExecute方法判斷request的Cookie中是否攜帶有效的TGT,第一次訪問(wèn)時(shí)沒(méi)有攜帶TGT,流程跳轉(zhuǎn)到gatewayRequestCheck。
<decision-state id="gatewayRequestCheck">
<if test="requestParameters.gateway != '' and requestParameters.gateway != null and flowScope.service != null"
then="gatewayServicesManagementCheck" else="serviceAuthorizationCheck"/>
</decision-state>
因?yàn)槌跏蓟瘯r(shí),盡管把service保存在了FlowScope作用域中,但request中的參數(shù)gateway不存在,登錄流程流轉(zhuǎn)到第三個(gè)state(serviceAuthorizationCheck)。
<action-state id="serviceAuthorizationCheck">
<evaluate expression="serviceAuthorizationCheck"/>
<transition to="initializeLogin"/>
</action-state>
ServiceAuthorizationCheck的doExecute方法,要做的就是判斷FlowScope作用域中是否存在service,如果service存在,查找service的注冊(cè)信息。登錄流程流轉(zhuǎn)到第四個(gè)state(generateLoginTicket)。
<action-state id="initializeLogin">
<evaluate expression="'success'"/>
<transition on="success" to="viewLoginForm"/>
</action-state>
initializeLogin不做判斷,存在只是為了兼容舊cas版本。直接跳轉(zhuǎn)到viewLoginForm。
<view-state id="viewLoginForm" view="casLoginView" model="credential">
<binder>
<binding property="username" required="true"/>
<binding property="password" required="true"/>
</binder>
<on-entry>
<set name="viewScope.commandName" value="'credential'"/>
</on-entry>
<transition on="submit" bind="true" validate="true" to="realSubmit"/>
</view-state>
此時(shí)流轉(zhuǎn)到CAS單點(diǎn)登錄服務(wù)器端的登錄頁(yè)面casLoginView.jsp。
<action-state id="realSubmit">
<evaluate
expression="authenticationViaFormAction.submit(flowRequestContext, flowScope.credential, messageContext)"/>
<transition on="warn" to="warn"/>
<transition on="success" to="sendTicketGrantingTicket"/>
<transition on="successWithWarnings" to="showMessages"/>
<transition on="authenticationFailure" to="handleAuthenticationFailure"/>
<transition on="error" to="initializeLogin"/>
</action-state>
用戶在登錄頁(yè)面輸入賬號(hào)密碼提交后,流程走到realSumit。
authenticationViaFormAction類的submit()對(duì)用戶提交的認(rèn)證信息進(jìn)行驗(yàn)證。
public final Event submit(final RequestContext context, final Credential credential,
final MessageContext messageContext) {
//判斷是否是已登錄過(guò),請(qǐng)求ST的
if (isRequestAskingForServiceTicket(context)) {
//如果已登錄,則生成ST
return grantServiceTicket(context, credential);
}
//未登陸過(guò),生成TGT
return createTicketGrantingTicket(context, credential, messageContext);
}
驗(yàn)證成功則跳轉(zhuǎn)到sendTicketGrantingTicket。
<action-state id="sendTicketGrantingTicket">
<evaluate expression="sendTicketGrantingTicketAction"/>
<transition to="serviceCheck"/>
</action-state>
接著跳轉(zhuǎn)到serviceCheck
<decision-state id="serviceCheck">
<if test="flowScope.service != null" then="generateServiceTicket" else="viewGenericLoginSuccess"/>
</decision-state>
判斷是否是由應(yīng)用頁(yè)面跳轉(zhuǎn)到登錄頁(yè)面登陸的,如果是,則跳轉(zhuǎn)到generateServiceTicket,不是則跳轉(zhuǎn)到viewGenericLoginSuccess。
此處我們跳轉(zhuǎn)到generateServiceTicket
<action-state id="generateServiceTicket">
<evaluate expression="generateServiceTicketAction"/>
<transition on="success" to="warn"/>
<transition on="unregisteredService" to="viewGenericLoginSuccess"/>
<transition on="authenticationFailure" to="handleAuthenticationFailure"/>
<transition on="error" to="initializeLogin"/>
<transition on="gateway" to="gatewayServicesManagementCheck"/>
</action-state>
generateServiceTicketAction類的doExecute方法生成ST,并跳轉(zhuǎn)到warn
<decision-state id="warn">
<if test="flowScope.warnCookieValue" then="showWarningView" else="redirect"/>
</decision-state>
跳轉(zhuǎn)到redirect
<action-state id="redirect">
<evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)"
result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response"/>
<transition to="postRedirectDecision"/>
</action-state>
<decision-state id="postRedirectDecision">
<if test="requestScope.response.responseType.name() == 'POST'" then="postView" else="redirectView"/>
</decision-state>
<end-state id="redirectView" view="externalRedirect:#{requestScope.response.url}"/>
最終返回給瀏覽器跳轉(zhuǎn)回Client1的響應(yīng)。
4.1.1.2 訪問(wèn)接入Cas系統(tǒng)的應(yīng)用系統(tǒng)Client2
訪問(wèn)Client1并登陸之后,訪問(wèn)Client2,與訪問(wèn)Client1一樣,先經(jīng)過(guò)initialFlowSetupAction。
隨后登錄流程流轉(zhuǎn)到第一個(gè)state(ticketGrantingTicketExistsCheck)。
<action-state id="ticketGrantingTicketCheck">
<evaluate expression="ticketGrantingTicketCheckAction"/>
<transition on="notExists" to="gatewayRequestCheck"/>
<transition on="invalid" to="terminateSession"/>
<transition on="valid" to="hasServiceCheck"/>
</action-state>
因?yàn)橐呀?jīng)登陸過(guò),擁有請(qǐng)求的Cookie中存在有效的TGT,于是流程跳轉(zhuǎn)到hasServiceCheck。
<decision-state id="hasServiceCheck">
<if test="flowScope.service != null" then="renewRequestCheck" else="viewGenericLoginSuccess"/>
</decision-state>
判斷是否是由應(yīng)用頁(yè)面跳轉(zhuǎn)到登錄頁(yè)面登陸的,如果是,則跳轉(zhuǎn)到generateServiceTicket,不是則跳轉(zhuǎn)到viewGenericLoginSuccess。
此處跳轉(zhuǎn)到renewRequestCheck。
<decision-state id="renewRequestCheck">
<if test="requestParameters.renew != '' and requestParameters.renew != null" then="serviceAuthorizationCheck"
else="generateServiceTicket"/>
</decision-state>
request中不存在renew,登錄流程流轉(zhuǎn)到第四個(gè)state(generateServiceTicket)。
<action-state id="generateServiceTicket">
<evaluate expression="generateServiceTicketAction"/>
<transition on="success" to="warn"/>
<transition on="unregisteredService" to="viewGenericLoginSuccess"/>
<transition on="authenticationFailure" to="handleAuthenticationFailure"/>
<transition on="error" to="initializeLogin"/>
<transition on="gateway" to="gatewayServicesManagementCheck"/>
</action-state>
后續(xù)的流轉(zhuǎn)與應(yīng)用系統(tǒng)webapp1相同,請(qǐng)參考前面webapp1的流轉(zhuǎn)。
4.1.2 登出流程解析
登出的流程定義在logout-webflow.xml中。
首先訪問(wèn)登出接口/logout,流程跳轉(zhuǎn)到terminateSession
<action-state id="terminateSession">
<evaluate expression="terminateSessionAction.terminate(flowRequestContext)" />
<transition to="doLogout" />
</action-state>
登出的方法主要調(diào)用路徑如下:
TerminateSessionAction.terminate()
--> CentralAuthenticationServiceImpl.destroyTicketGrantingTicket()
銷毀TGT的方法
--> LogoutManagerImpl.performLogout()
執(zhí)行登出的方法,在該方法中向每個(gè)訪問(wèn)過(guò)的應(yīng)用系統(tǒng)發(fā)送登出請(qǐng)求, 應(yīng)用系統(tǒng)收到請(qǐng)求會(huì)銷毀與用戶的session
--> handleLogoutForSloService()
向應(yīng)用系統(tǒng)發(fā)送登出請(qǐng)求的方法
--> performBackChannelLogout() 發(fā)送登出請(qǐng)求
terminateSessionAction.terminate()執(zhí)行完畢之后,流程跳轉(zhuǎn)到doLogout
<action-state id="doLogout">
<evaluate expression="logoutAction" />
<transition on="finish" to="finishLogout" />
<transition on="front" to="frontLogout" />
</action-state>
<decision-state id="finishLogout">
<if test="flowScope.logoutRedirectUrl != null" then="redirectView" else="logoutView" />
</decision-state>
<end-state id="logoutView" view="externalRedirect:casLoginView" />
最終跳轉(zhuǎn)到登錄頁(yè)面。
4.2 Client端源碼解析
Cas client應(yīng)用系統(tǒng)端通過(guò)幾個(gè)Filter來(lái)實(shí)現(xiàn)登陸跳轉(zhuǎn)和登出等功能。
下面以在web.xml中配置的幾個(gè)Filter順序來(lái)進(jìn)行分析 。
4.2.1 SingleSignOutFilter
org.jasig.cas.client.session.SingleSignOutFilter是處理登出請(qǐng)求的Filter。該Filter判斷是否是Cas Server端發(fā)過(guò)來(lái)的登出請(qǐng)求,如果是登出請(qǐng)求,則根據(jù)請(qǐng)求中的logoutMessage清除對(duì)應(yīng)的Session。
登出請(qǐng)求的主要調(diào)用路徑如下:
SingleSignOutFilter.doFilter()
--> SingleSignOutHandler.process()
--> destroySession(request)
--> session.invalidate();
4.2.2 AuthenticationFilter
org.jasig.cas.client.authentication.AuthenticationFilter是驗(yàn)證請(qǐng)求是否登陸過(guò)的Filter。
public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
// 判斷該請(qǐng)求是否不需要驗(yàn)證,如果不需要,則跳轉(zhuǎn)到下一個(gè)Filter
if(this.isRequestUrlExcluded(request)) {
this.logger.debug("Request is ignored.");
filterChain.doFilter(request, response);
} else {
HttpSession session = request.getSession(false);
//從session中獲取名為"_const_cas_assertion_"的Assertion
Assertion assertion = session != null?(Assertion)session.getAttribute("_const_cas_assertion_"):null;
//如果存在,則說(shuō)明已經(jīng)登錄,本過(guò)濾器處理完成,處理下個(gè)過(guò)濾器
if(assertion != null) {
filterChain.doFilter(request, response);
} else {
//生成serviceUrl
String serviceUrl = this.constructServiceUrl(request, response);
//從request中獲取ST
String ticket = this.retrieveTicketFromRequest(request);
boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
//如果ticket不為空,本過(guò)濾器處理完成,處理下個(gè)過(guò)濾器
if(!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
this.logger.debug("no ticket and no assertion found");
String modifiedServiceUrl;
if(this.gateway) {
this.logger.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}
this.logger.debug("Constructed service url: {}", modifiedServiceUrl);
//生成重定向URL
String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
this.logger.debug("redirecting to \"{}\"", urlToRedirectTo);
String reqType = request.getHeader("X-Requested-With");
//如果是異步請(qǐng)求,則返回410狀態(tài)碼給前端
if("XMLHttpRequest".equalsIgnoreCase(reqType)) {
String json = "{\"flag\":0,\"error\":401,\"data\":{}}";
response.getWriter().write(json);
} else {
//跳轉(zhuǎn)到CAS服務(wù)器的登錄頁(yè)面
this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
}
} else {
filterChain.doFilter(request, response);
}
}
}
}
當(dāng)我們從瀏覽器訪問(wèn)配置了單點(diǎn)登錄的應(yīng)用系統(tǒng)時(shí)(http://www.client1.com/index),由于集成了CAS單點(diǎn)登錄客戶端,此時(shí)進(jìn)入到第一個(gè)過(guò)濾器AuthenticationFilter(不考慮其他非單點(diǎn)登錄的過(guò)濾器),執(zhí)行以下操作:
1 從session中獲取名為“const_cas_assertion”的assertion對(duì)象,判斷assertion是否存在,如果存在,說(shuō)明已經(jīng)登錄,執(zhí)行下一個(gè)過(guò)濾器。如果不存在,執(zhí)行第2步。
2 生成serviceUrl(http://www.client1.com/index),從request中獲取票據(jù)參數(shù)ticket,判斷ticket是否為空,如果不為空?qǐng)?zhí)行下一個(gè)過(guò)濾器。如果為空,執(zhí)行第3步。
3 生成重定向URL,如:
http://www.casserver.com/login?service=http://www.client1.com/index
4 跳轉(zhuǎn)到單點(diǎn)登錄服務(wù)器,顯示登錄頁(yè)面,此時(shí)第一個(gè)過(guò)濾器執(zhí)行完成。
4.2.3 ticketValidationFilter
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter為驗(yàn)證Service Ticket的Filter。
Cas20ProxyReceivingTicketValidationFilter父類AbstractTicketValidationFilter中的doFilter方法:
public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if(this.preFilter(servletRequest, servletResponse, filterChain)) {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
//從request中獲取參數(shù)
String ticket = this.retrieveTicketFromRequest(request);
//ticket不為空,驗(yàn)證ticket,否則本過(guò)濾器處理完成,處理下個(gè)過(guò)濾器
if(CommonUtils.isNotBlank(ticket)) {
this.logger.debug("Attempting to validate ticket: {}", ticket);
try {
//驗(yàn)證ticket并產(chǎn)生Assertion對(duì)象,錯(cuò)誤拋出TicketValidationException異常
Assertion assertion = this.ticketValidator.validate(ticket, this.constructServiceUrl(request, response));
this.logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());
//給request設(shè)置assertion
request.setAttribute("_const_cas_assertion_", assertion);
//給session設(shè)置assertion
if(this.useSession) {
request.getSession().setAttribute("_const_cas_assertion_", assertion);
}
this.onSuccessfulValidation(request, response, assertion);
if(this.redirectAfterValidation) {
this.logger.debug("Redirecting after successful ticket validation.");
response.sendRedirect(this.constructServiceUrl(request, response));
return;
}
} catch (TicketValidationException var8) {
this.logger.debug(var8.getMessage(), var8);
this.onFailedValidation(request, response);
if(this.exceptionOnValidationFailure) {
throw new ServletException(var8);
}
response.sendError(403, var8.getMessage());
return;
}
}
filterChain.doFilter(request, response);
}
}
假設(shè)當(dāng)執(zhí)行完第一個(gè)過(guò)濾器后,跳轉(zhuǎn)到CAS服務(wù)器端的登錄頁(yè)面,輸入用戶名和密碼,驗(yàn)證通過(guò)后。CAS服務(wù)器端會(huì)生成ticket,并將ticket作為重新跳轉(zhuǎn)到應(yīng)用系統(tǒng)的參數(shù)(http://www.client1.com/index?ticket=ST-1-4hH2s5tzsMGCcToDvGCb-cas01.example.org)。此時(shí)又進(jìn)入第一個(gè)過(guò)濾器AuthenticationFilter,由于存在ticket參數(shù),進(jìn)入到第二個(gè)過(guò)濾器TicketValidationFilter,執(zhí)行以下操作:
1 從request獲取ticket參數(shù),如果ticket為空,繼續(xù)處理下一個(gè)過(guò)濾器。如果參數(shù)不為空,驗(yàn)證ticket參數(shù)的合法性。
2 驗(yàn)證ticket,TicketValidationFilter的validate方法通過(guò)httpClient訪問(wèn)CAS服務(wù)器端(http://www.casserver.com/serviceValidate?ticket=ST-1-4hH2s5tzsMGCcToDvGCb-cas01.example.org&service=http://www.client1.com/index)驗(yàn)證ticket是否正確,并返回assertion對(duì)象。如果驗(yàn)證失敗,拋出異常,跳轉(zhuǎn)到錯(cuò)誤頁(yè)面。如果驗(yàn)證成功,session會(huì)以"const_cas_assertion"的名稱保存assertion對(duì)象,繼續(xù)處理下一個(gè)過(guò)濾器。
4.2.4 HttpServletRequestWrapperFilter
org.jasig.cas.client.util.HttpServletRequestWrapperFilter對(duì)HttpServletRequest對(duì)象再包裝一次,讓其支持getUserPrincipal,getRemoteUser方法來(lái)取得登錄的用戶信息。
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//從Session或者request中取得AttributePrincipal,其實(shí)Assertion的一個(gè)principal屬性
AttributePrincipal principal = this.retrievePrincipalFromSessionOrRequest(servletRequest);
//對(duì)request進(jìn)行包裝,并處理后面的過(guò)濾器,使其后面的過(guò)濾器或者servlet能夠在request.getRemoteUser()或者request.getUserPrincipal()取得用戶信息
filterChain.doFilter(newHttpServletRequestWrapperFilter.CasHttpServletRequestWrapper((HttpServletRequest)servletRequest, principal), servletResponse); }
5 常見(jiàn)問(wèn)題
6 其他
6.1 Gradle相關(guān)
6.1.1 Gradle 打包實(shí)現(xiàn)生產(chǎn)環(huán)境與測(cè)試環(huán)境配置分離
在build.gradle中加入以下代碼
#默認(rèn)情況下為ent-dev
def env = System.getProperty("profile") ?: "ent-dev"
sourceSets {
main {
resources {
srcDirs = ["src/main/resources", "src/main/$env"]
}
}
}
在/src/main目錄下建立各個(gè)環(huán)境目錄,如 ent-dev、ent-prod等。
對(duì)于cas系統(tǒng),配置參數(shù)都在cas.properties中,可以將cas.properties放入各個(gè)環(huán)境目錄中, 并修改讀取cas.properties路徑。
修改WEB-INF/spring-configuration目錄中的propertyFileConfigurer.xml
將
<util:properties id="casProperties" location="${cas.properties.config.location:/WEB-INF/cas.properties}"/>
修改為
<util:properties id="casProperties" location="${cas.properties.config.location:classpath:/cas.properties}"/>
build生產(chǎn)環(huán)境時(shí)可使用命令:gradle build -D profile=ent-prod
6.2 調(diào)用263和LDAP驗(yàn)證用戶名密碼步驟
6.2.1 在項(xiàng)目中添加依賴
在項(xiàng)目build.gradle中加入以下內(nèi)容,如果是Maven項(xiàng)目,則在pom.xml文件中加入maven格式依賴。
shangdeCommonSdfVersion=0.1.0.5-ENT-SNAPSHOT
compile group: 'com.xxx.common', name: 'sdf-common-util', version: shangdeCommonSdfVersion
compile group: 'com.xxx.common', name: 'sdf-common-web', version: shangdeCommonSdfVersion
compile group: 'com.xxx.common', name: 'sdf-common-auth-web', version: shangdeCommonSdfVersion
compile group: 'com.xxx.common', name: 'sdf-common-sys', version: shangdeCommonSdfVersion
compile group: 'com.xxx.common', name: 'sdf-common-authentication-263', version: shangdeCommonSdfVersion
compile group: 'com.xxx.common', name: 'sdf-common-auth', version: shangdeCommonSdfVersion
6.2.2 在properties文件中添加以下內(nèi)容
263.domain=xyz.com
263.account=xyz.com
263.key=Zs54D6jo#
263.webServiceUrl=http://macom.263.net/axis/xmapi
ldap.url=ldap://172.16.117.215:389
6.2.3 調(diào)用驗(yàn)證方法
@Autowired
private SdfPasswordValidatorImpl sdfPasswordValidatorImpl;
// 返回值為true及驗(yàn)證成功
boolean isValidUser = sdfPasswordValidatorImpl.authenticate(username,password);