問題記錄前端
一、配置文件報錯,卻能夠正常運行(cas的war包和maven overlay 配置已刪除),一臉懵逼。。。java
二、spring加載bean,id="warnCookieGenerator"時web
實現不是:org.jasig.cas.web.support.CookieRetrievingCookieGeneratorspring
而是:org.jasig.cas.web.WarningCookieRetrievingCookieGeneratorapache
錯誤出現背景,比照cas中login-webflow.xml定義的流程改寫爲相應的controller和輔助類,spring-mvc
而後在在加載"warnCookieGenerator"實現時,class指定錯誤,使其識別不出HttpServletResponse實例,而後斷言一直報錯。cookie
解決思路,比較將本身改造的流程和cas原始流程都debug一遍,比較相關類的實現是否一致。session
三、將cas生成的tgt放入session中,下一次請求時,獲取不到tgt屬性。mvc
緣由多是未修改的cas處理邏輯中,某一處將名爲:「ticketGrantingTicketId「」的屬性移除了。app
暫時未找到移除位置或邏輯,將屬性名改成「ticket」,問題解決。
改造過程
一、自定義認證明現
1.一、首先建立實現了AuthenticationHandler接口(其餘AuthenticationHandler的實現類也能夠)的類。
我繼承了抽象類AbstractUsernamePasswordAuthenticationHandler,實現其中
authenticateUsernamePasswordInternal方法,就是根據前端用戶提交的登錄信息,都數據中心查一下,用戶是否合法(可調用dao層方法)。最後如何返回結果能夠參考cas的實現方法:這裏使用了
AcceptUsersAuthenticationHandler中的返回方式:
return createHandlerResult(saturnCredential, this.principalFactory.createPrincipal(accountName), null);
package com.saturn.account.ssoadapter; import com.saturn.account.domain.common.exception.SAccountInfoException; import com.saturn.account.domain.common.exception.SUnknowAccountException; import com.saturn.account.domain.manager.SAccountManager; import com.saturn.account.domain.model.SAccount; import com.saturn.account.ssoadapter.credential.SSaturnCredential; import org.apache.log4j.Logger; import org.jasig.cas.authentication.*; import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * \file SCASAuthenticationHandler * \author LiJiXiao * \date 2018/1/11 12:45 * \brief CAS認證處理類 * \par Copyright (c): * Copyright (c) Saturn Team. All rights reserved. */ public class SCASAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler { private static final Logger LOGGER = Logger.getLogger(SCASAuthenticationHandler.class); /** * 具體帳戶認證業務管理類引用 */ private SAccountManager accountManager = SAccountManager.getManager(); @Override public HandlerResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential) throws SUnknowAccountException { final SSaturnCredential saturnCredential = (SSaturnCredential) credential; int authPlatform = saturnCredential.getPlatform(); // valid info String accountName = saturnCredential.getUsername(); if (accountName == null || accountName.trim().equals("")) { LOGGER.error("accountNmae is null"); throw new SAccountInfoException("account info error"); } String password = saturnCredential.getPassword(); if (password == null || password.trim().equals("")) { LOGGER.error("password is null"); throw new SAccountInfoException("account info error"); } Map<String, Object> token = new HashMap<>(); token.put("accountName", accountName); token.put("password", password); // uri暫時寫死 token.put("uri", "db://127.0.0.1/no/com.saturn.account.domain.model.SAccount/"); // authenticate SAccount account = accountManager.authenticate(authPlatform, token); return createHandlerResult(saturnCredential, this.principalFactory.createPrincipal(accountName), null); } }
1.二、修改配置引入自定義認證明現
在deployerConfigContext.xml中找到:
<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />
下添加bean:
<bean id="acceptUsersAuthenticationHandler" class="com.saturn.account.ssoadapter.SCASAuthenticationHandler"/>
二、實現自定義登錄數據模型
2.一、新建實現了Credential接口(其它實現類也能夠)的自定義數據類,我繼承了
UsernamePasswordCredential
package com.saturn.account.ssoadapter.credential; import org.jasig.cas.authentication.UsernamePasswordCredential; import java.io.Serializable; /** * \file SSaturnCredential * \author LiJiXiao * \date 2018/1/11 11:10 * \brief 帳戶憑證 * \par Copyright (c): * Copyright (c) Saturn Team. All rights reserved. */ public class SSaturnCredential extends UsernamePasswordCredential implements Serializable { private static final long serialVersionUID = -1L; /** * 帳戶認證平臺類別 */ private int platform; /** * 圖形驗證碼 */ private String imageCode = ""; public SSaturnCredential() { } public SSaturnCredential(int platform) { this.platform = platform; } @Override public String toString() { return super.toString(); } @Override public boolean equals(Object object) { if (object == null) { return false; } if (object instanceof SSaturnCredential) { return false; } SSaturnCredential credential = (SSaturnCredential) object; if (this.platform != credential.platform) { return false; } if (!imageCode.equals(credential.getImageCode())) { return false; } return super.equals(object); } @Override public int hashCode() { int hash = super.hashCode() + this.platform; hash += imageCode.hashCode(); return hash; } /** * \name get/set */ ///@{ public int getPlatform() { return platform; } public void setPlatform(int platform) { this.platform = platform; } public String getImageCode() { return imageCode; } public void setImageCode(String imageCode) { this.imageCode = imageCode; } ///@} }
2.二、由於本次使用cas再也不使用spring-web-flow,因此不需修改:login-webflow.xml。只須要使用spring-mvc中的@requestBody等標籤,直接接收Credential實例就好。
三、自定義返回用戶信息
有兩種方法,先了解小用戶信息返回流程:
認證完以後,能夠直接獲取須要的帳戶信息,而後將帳戶信息放入handlerResult中的principle中,而後cas會一步步將principle放入authentication類中,咱們能夠從authentication中獲取principle,而後返回前臺。可是此過程當中會有一個principleResolver參與,若是principleResolver是null,則將原來handlerResult中的principle給authentication,若是不爲null,就解析一下在給authentication。cas默認的解析器是
PersonDirectoryPrincipalResolver類
此方法中判斷解析器是否爲空。
此方法中拿到用戶信息的map
具體是經過同類中的此方法獲取
其中attributeRepository,是在spring中配置的實現。
所以自定義用戶信息的兩種方法:
第一:將信息直接放入result中的principle,而後將principleResolve設爲null,可是還不知道如何設置爲空,基礎知識很差。。。
第二:自定義attributeRepository的實現類,重寫getPerson方法,在此方法中將帳戶信息放進map,好比
<"account",account>,就行了。
四、將登錄流程翻譯成controller,直接上碼
登錄controller:
package com.saturn.controller; import com.saturn.account.domain.model.SAccount; import com.saturn.account.ssoadapter.credential.SSaturnCredential; import com.saturn.handler.sso.*; import com.saturn.model.SResult; import com.saturn.model.SResultCode; import org.jasig.cas.authentication.Credential; import org.jasig.cas.authentication.principal.Principal; import org.jasig.cas.authentication.principal.Service; import org.jasig.cas.services.UnauthorizedServiceException; import org.jasig.cas.web.support.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.binding.message.DefaultMessageContext; import org.springframework.binding.message.MessageContext; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; /** * \file SLoginController * \author LiJiXiao * \date 2018/2/2 19:45 * \brief L * \par Copyright (c): * Copyright (c) Saturn Team. All rights reserved. */ @Controller public class SLoginController { private final transient Logger LOGGER = LoggerFactory.getLogger(this.getClass()); private final long EXPIRE_TIME = 60 * 1000; @Autowired SInitServletContextAndHttpServletRequestHandler initHandler; @Autowired STicketGrantingTicketCheckHandler TGTCheckHandler; @Autowired STerminateSessionHandler terminateSessionHandler; @Autowired SServiceAuthorizationCheckHandler serviceAuthorizationCheckHandler; @Autowired SGenerateServiceTicketHandler generateServiceTicketHandler; @Autowired SToAuthenticationHandler authenticationHandler; @RequestMapping (value = "/validation/login", method = RequestMethod.GET) @ResponseBody public SResult validateLogin(HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(); Service service = (Service) session.getAttribute("service"); initHandler.initServletContextAndHttpServletRequest(request, service); Event TGTCheckEvent = TGTCheckHandler.ticketGrantingTicketCheck(request); String TGTChecEventId = TGTCheckEvent.getId(); // TGT不存在 if ("notExists".equals(TGTChecEventId)) { try { serviceAuthorizationCheckHandler.check(service, session); } catch (UnauthorizedServiceException e) { return SResult.failure(SResultCode.ACCESS_INVALID); } return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN, service); } // TGT無效 if ("invalid".equals(TGTChecEventId)) { terminateSessionHandler.terminate(request, response); try { serviceAuthorizationCheckHandler.check(service, session); } catch (UnauthorizedServiceException e) { return SResult.failure(SResultCode.ACCESS_INVALID); } // 去登錄 return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN); } // TGT有效 if ("valid".equals(TGTChecEventId)) { if (service != null) { String renew = request.getParameter("renew"); if (!"".equals(renew) && null != renew) { try { serviceAuthorizationCheckHandler.check(service, session); } catch (UnauthorizedServiceException e) { return SResult.failure(SResultCode.ACCESS_INVALID); } // 去登錄 return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN); } else { Event generateServiceTicketEvent = generateServiceTicketHandler.generateServiceTicket(service, request); if ("success".equals(generateServiceTicketEvent)) { Boolean warnCookieValue = (Boolean) session.getAttribute("warnCookieValue"); if (warnCookieValue != null && warnCookieValue) { return SResult.success(SResultCode.SYSTEM_WARN); } String serviceTicketId = (String) request.getAttribute("serviceTicketId"); if (serviceTicketId == null || "".equals(serviceTicketId)) { return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN); } Map<String, Object> responseInfo = new HashMap<>(); responseInfo.put("service", service.getId()); responseInfo.put("serviceTicketId", serviceTicketId); return SResult.success(responseInfo); // 回到 } if ("authenticationFailure".equals(generateServiceTicketEvent)) { return SResult.failure(SResultCode.ACCOUNT_NOT_LOGIN); } } } else { return SResult.success(SResultCode.ACCOUNT_LOGINED,session.getAttribute("account")); } } return SResult.failure(SResultCode.SYSTEM_ERROR); } @RequestMapping (value = "/real/login") @ResponseBody public SResult realLogin( HttpServletRequest request, HttpServletResponse response, @RequestBody SSaturnCredential credential) { HttpSession session = request.getSession(); boolean checkErrorTimesResult = checkErrorTimes(session); // 檢查錯誤次數,小於5次正常登錄 if (checkErrorTimesResult) { MessageContext messageContext = new DefaultMessageContext(); Event event = authenticationHandler.submit(request, response, credential, messageContext); return responseByEvent(event, session); } else { // 大於等於5次須要驗證圖形驗證碼 boolean isNotExpire = isNotExpire(session); if (!isNotExpire) { return SResult.failure(SResultCode.VALID_CODE_EXPIRE); } boolean validImageCodeResult = validImageCode(session, credential); if (!validImageCodeResult) { return SResult.failure(SResultCode.VALID_CODE_ERROR); } // 驗證碼正取進行登錄驗證 MessageContext messageContext = new DefaultMessageContext(); Event event = authenticationHandler.submit(request, response, credential, messageContext); return responseByEvent(event, session); } } // 錯誤時記錄/添加錯誤次數 private void setErrorTimes(final HttpSession session) { int passwordErrorTimes = 0; if (session.getAttribute("errorTimes") != null) { passwordErrorTimes = (Integer) session.getAttribute("errorTimes"); passwordErrorTimes++; } else { session.setAttribute("errorTimes", passwordErrorTimes); } } private boolean checkErrorTimes(final HttpSession session) { Integer errorTimes = (Integer) session.getAttribute("errorTimes"); if (errorTimes != null) { return errorTimes < 5; } return true; } private boolean isNotExpire(final HttpSession session) { Date now = new Date(); Date createTime = (Date) session.getAttribute("createTime"); if (createTime == null) { return false; } if ((createTime.getTime() - now.getTime()) > EXPIRE_TIME) { return false; } return true; } private boolean validImageCode(final HttpSession session, final Credential credentials) { // 獲取生成的圖形驗證碼 String imageCode = (String) session.getAttribute("imageCode"); if (imageCode == null || imageCode.equals(" ")) { return false; } session.removeAttribute("imageCode"); // 獲取提交的圖形驗證碼 SSaturnCredential credential = (SSaturnCredential) credentials; String submitImageCode = credential.getImageCode(); if (submitImageCode != null && submitImageCode.equals(imageCode)) { return true; } return false; } private SResult responseByEvent(Event event, HttpSession session) { if ("success".equals(event.getId())) { session.removeAttribute("errorTimes"); SAccount account = (SAccount) session.getAttribute("account"); return SResult.success(account); } if ("successWithWarnings".equals(event.getId())) { session.removeAttribute("errorTimes"); return SResult.success("withWarning"); } else { setErrorTimes(session); return SResult.failure(SResultCode.ACCOUNT_LOGIN_FAIL); } } }
相關處理輔助類:
4.一、代替流程文件中的初始域數據類initialFlowSetupAction:
package com.saturn.handler.sso; import org.apache.commons.lang3.StringUtils; import org.jasig.cas.authentication.principal.Service; import org.jasig.cas.services.RegisteredService; import org.jasig.cas.services.RegisteredServiceAccessStrategy; import org.jasig.cas.services.ServicesManager; import org.jasig.cas.services.UnauthorizedServiceException; import org.jasig.cas.web.support.ArgumentExtractor; import org.jasig.cas.web.support.CookieRetrievingCookieGenerator; import org.jasig.cas.web.support.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.List; /** * this class is for * @since 1.8 */ @Component public class SInitServletContextAndHttpServletRequestHandler { private final transient Logger logger = LoggerFactory.getLogger(this.getClass()); /** * The services manager with access to the registry. **/ @NotNull @Autowired private ServicesManager servicesManager; /** * CookieGenerator for the Warnings. */ @NotNull @Autowired private CookieRetrievingCookieGenerator warnCookieGenerator; /** * CookieGenerator for the TicketGrantingTickets. */ @NotNull @Autowired private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator; /** * Extractors for finding the service. */ @NotNull @Size (min = 1) @Autowired @Qualifier ("argumentExtractor") private List<ArgumentExtractor> argumentExtractors; /** * If no authentication request from a service is present, halt and warn the user. */ private boolean hasAuthenticationRequest = true; public void initServletContextAndHttpServletRequest(HttpServletRequest request, Service service) { HttpSession session = request.getSession(); ServletContext context = session.getServletContext(); final String contextPath = context.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()); } String ticketValue = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request); Boolean cookieValue = Boolean.valueOf(this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request)); request.setAttribute("ticketGrantingTicketId", ticketValue); session.setAttribute("ticketGrantingTicketId", ticketValue); session.setAttribute("warnCookieValue", cookieValue); service = WebUtils.getService(this.argumentExtractors, request); 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()); session.setAttribute("registeredService", 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()); session.setAttribute("unauthorizedRedirectUrl", accessStrategy.getUnauthorizedRedirectUrl()); } } } else if (!this.hasAuthenticationRequest) { logger.warn( "No service authentication request is available at [{}]. CAS is configured to disable the flow.", request.getRequestURL()); throw new UnauthorizedServiceException("screen.service.required.message", "Service is required"); } session.setAttribute("service", service); } /** * Decide whether CAS should allow authentication requests * when no service is present in the request. Default is enabled. * @param enableFlowOnAbsentServiceRequest the enable flow on absent service request */ @Autowired public void setEnableFlowOnAbsentServiceRequest( @Value ("${create.sso.missing.service:true}") final boolean enableFlowOnAbsentServiceRequest) { this.hasAuthenticationRequest = enableFlowOnAbsentServiceRequest; } }
4.二、流程中的第一個處理節點ticketGrantingTicketCheck:
package com.saturn.handler.sso; import org.jasig.cas.CentralAuthenticationService; import org.jasig.cas.ticket.AbstractTicketException; import org.jasig.cas.ticket.Ticket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.webflow.execution.Event; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.validation.constraints.NotNull; /** * this class is for * @since 1.8 */ @Component public class STicketGrantingTicketCheckHandler { private final transient Logger LOGGER = LoggerFactory.getLogger(this.getClass()); /** * TGT does not exist event ID={@value}. **/ private static final String NOT_EXISTS = "notExists"; /** * TGT invalid event ID={@value}. **/ private static final String INVALID = "invalid"; /** * TGT valid event ID={@value}. **/ private static final String VALID = "valid"; /** * The Central authentication service. */ @NotNull private final CentralAuthenticationService centralAuthenticationService; @Autowired public STicketGrantingTicketCheckHandler( @Qualifier ("centralAuthenticationService") final CentralAuthenticationService centralAuthenticationService) { this.centralAuthenticationService = centralAuthenticationService; } /** * \param[] a * \return a * \see * \note 從session中獲取ticket屬性值,使用ticketGrantingTicketId命名屬性時,cas可能會將此屬性從session中移除,因此換了個名字「ticket」。 * \warning */ public Event ticketGrantingTicketCheck(HttpServletRequest request) { HttpSession session = request.getSession(); final String tgtFromRequest = (String) request.getAttribute("ticket"); final String tgtFromSession = (String) session.getAttribute("ticket"); final String tgtId = tgtFromRequest != null ? tgtFromRequest : tgtFromSession; if (!StringUtils.hasText(tgtId)) { return new Event(this, NOT_EXISTS); } String eventId = INVALID; try { final Ticket ticket = this.centralAuthenticationService.getTicket(tgtId, Ticket.class); if (ticket != null && !ticket.isExpired()) { eventId = VALID; } } catch (final AbstractTicketException e) { LOGGER.trace("Could not retrieve ticket id {} from registry.", e); } return new Event(this, eventId); } }
4.三、第二個節點:terminateSession
package com.saturn.handler.sso; import org.jasig.cas.CentralAuthenticationService; import org.jasig.cas.authentication.AuthenticationSystemSupport; import org.jasig.cas.authentication.DefaultAuthenticationSystemSupport; import org.jasig.cas.logout.LogoutRequest; import org.jasig.cas.web.support.CookieRetrievingCookieGenerator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.springframework.webflow.action.EventFactorySupport; import org.springframework.webflow.execution.Event; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.validation.constraints.NotNull; import java.util.List; /** * this class is for * @since 1.8 */ @Component public class STerminateSessionHandler { /** Webflow event helper component. */ private final EventFactorySupport eventFactorySupport = new EventFactorySupport(); /** The CORE to which we delegate for all CAS functionality. */ @NotNull @Autowired @Qualifier ("centralAuthenticationService") private CentralAuthenticationService centralAuthenticationService; /** CookieGenerator for TGT Cookie. */ @NotNull @Autowired @Qualifier("ticketGrantingTicketCookieGenerator") private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator; /** CookieGenerator for Warn Cookie. */ @NotNull @Autowired @Qualifier("warnCookieGenerator") private CookieRetrievingCookieGenerator warnCookieGenerator; @NotNull @Autowired(required=false) @Qualifier("defaultAuthenticationSystemSupport") private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport(); /** * Creates a new instance with the given parameters. */ public STerminateSessionHandler() {} public Event terminate(HttpServletRequest request,HttpServletResponse response) { // in login's webflow : we can get the value from context as it has already been stored HttpSession session = request.getSession(); final String tgtFromRequest = (String) request.getAttribute("ticketGrantingTicketId"); final String tgtFromSession = (String) session.getAttribute("ticketGrantingTicketId"); String tgtId = tgtFromRequest != null ? tgtFromRequest : tgtFromSession; // for logout, we need to get the cookie's value if (tgtId == null) { tgtId = this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request); } if (tgtId != null) { final List<LogoutRequest> logoutRequests = this.centralAuthenticationService.destroyTicketGrantingTicket(tgtId); session.setAttribute("logoutRequests",logoutRequests); } this.ticketGrantingTicketCookieGenerator.removeCookie(response); this.warnCookieGenerator.removeCookie(response); return this.eventFactorySupport.success(this); } }
4.四、第三各節點(暫不考慮gatewayRequestCheck):serviceAuthorizationCheck
package com.saturn.handler.sso; import org.jasig.cas.authentication.principal.Service; import org.jasig.cas.services.RegisteredService; import org.jasig.cas.services.ServicesManager; import org.jasig.cas.services.UnauthorizedServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.springframework.webflow.action.EventFactorySupport; import org.springframework.webflow.execution.Event; import javax.servlet.http.HttpSession; import javax.validation.constraints.NotNull; import java.net.URI; /** * \file SServiceAuthorizationCheckHandler * \author LiJiXiao * \date 2018/2/2 20:48 * \brief L * \par Copyright (c): * Copyright (c) Saturn Team. All rights reserved. */ @Component public class SServiceAuthorizationCheckHandler { private final transient Logger logger = LoggerFactory.getLogger(this.getClass()); @NotNull private final ServicesManager servicesManager; /** * Initialize the component with an instance of the services manager. * @param servicesManager the service registry instance. */ @Autowired public SServiceAuthorizationCheckHandler(@Qualifier ("servicesManager") final ServicesManager servicesManager) { this.servicesManager = servicesManager; } public Event check(final Service service, HttpSession session) throws UnauthorizedServiceException { //No service == plain /login request. Return success indicating transition to the login form if (service == null) { return this.getEventFactorySupport().success(this); } if (this.servicesManager.getAllServices().isEmpty()) { final String msg = String.format("No service definitions are found in the service manager. " + "Service [%s] will not be automatically authorized to request authentication.", service.getId()); logger.warn(msg); throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_EMPTY_SVC_MGMR); } final RegisteredService registeredService = this.servicesManager.findServiceBy(service); if (registeredService == null) { final String msg = String.format("Service Management: Unauthorized Service Access. " + "Service [%s] is not found in service registry.", service.getId()); logger.warn(msg); throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg); } if (!registeredService.getAccessStrategy().isServiceAccessAllowed()) { final String msg = String.format("Service Management: Unauthorized Service Access. " + "Service [%s] is not allowed access via the service registry.", service.getId()); logger.warn(msg); URI url = registeredService.getAccessStrategy().getUnauthorizedRedirectUrl(); session.setAttribute("unauthorizedRedirectUrl",url); throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg); } return this.getEventFactorySupport().success(this); } private EventFactorySupport getEventFactorySupport() { return new EventFactorySupport(); } }
4.五、generateServiceTicket節點:
package com.saturn.handler.sso; import org.jasig.cas.CentralAuthenticationService; import org.jasig.cas.authentication.*; import org.jasig.cas.authentication.principal.Service; import org.jasig.cas.services.RegisteredService; import org.jasig.cas.services.ServicesManager; import org.jasig.cas.ticket.AbstractTicketException; import org.jasig.cas.ticket.InvalidTicketException; import org.jasig.cas.ticket.ServiceTicket; import org.jasig.cas.ticket.registry.TicketRegistrySupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import org.springframework.webflow.action.EventFactorySupport; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.execution.Event; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.validation.constraints.NotNull; import java.net.URI; /** * this class is for * @since 1.8 */ @Component public class SGenerateServiceTicketHandler { private final transient Logger logger = LoggerFactory.getLogger(this.getClass()); /** * Instance of CentralAuthenticationService. */ @NotNull @Autowired @Qualifier ("centralAuthenticationService") private CentralAuthenticationService centralAuthenticationService; @Autowired @Qualifier("servicesManager") private ServicesManager servicesManager; @NotNull @Autowired @Qualifier("defaultAuthenticationSystemSupport") private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport(); @Autowired @Qualifier("defaultTicketRegistrySupport") private TicketRegistrySupport ticketRegistrySupport; public Event generateServiceTicket(Service service, HttpServletRequest request) { HttpSession session = request.getSession(); final String tgtFromRequest = (String) request.getAttribute("ticketGrantingTicketId"); final String tgtFromSession = (String) session.getAttribute("ticketGrantingTicketId"); final String ticketGrantingTicket = tgtFromRequest != null ? tgtFromRequest : tgtFromSession; try { /** * In the initial primary authentication flow, credentials are cached and available. * Since they are authenticated as part of submission first, there is no need to doubly * authenticate and verify credentials. * * In subsequent authentication flows where a TGT is available and only an ST needs to be * created, there are no cached copies of the credential, since we do have a TGT available. * So we will simply grab the available authentication and produce the final result based on that. */ final Authentication authentication = ticketRegistrySupport.getAuthenticationFrom(ticketGrantingTicket); if (authentication == null) { throw new InvalidTicketException(new AuthenticationException(), ticketGrantingTicket); } final RegisteredService registeredService = servicesManager.findServiceBy(service); session.setAttribute("registeredService",registeredService); session.setAttribute("service",service); URI url = registeredService.getAccessStrategy().getUnauthorizedRedirectUrl(); session.setAttribute("unauthorizedRedirectUrl", url); final AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder( this.authenticationSystemSupport.getPrincipalElectionStrategy()); Credential cFromRequest = (Credential)request.getAttribute("credential"); Credential cFromSession = (Credential)session.getAttribute("credential"); Credential credential = cFromRequest != null ? cFromRequest : cFromSession; credential = credential != null && org.apache.commons.lang3.StringUtils.isBlank(credential.getId()) ? null : credential; final AuthenticationContext authenticationContext = builder.collect(credential) .collect(authentication).build(service); final ServiceTicket serviceTicketId = this.centralAuthenticationService .grantServiceTicket(ticketGrantingTicket, service, authenticationContext); request.setAttribute("serviceTicketId",serviceTicketId); return this.getEventFactorySupport().success(this); } catch (final AuthenticationException e) { logger.error("Could not verify credentials to grant service ticket", e); } catch (final AbstractTicketException e) { if (e instanceof InvalidTicketException) { this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicket); } return newEvent("sendTicketGrantingTicket", e); } return this.getEventFactorySupport().error(this); } public void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) { this.centralAuthenticationService = centralAuthenticationService; } public void setAuthenticationSystemSupport(final AuthenticationSystemSupport authenticationSystemSupport) { this.authenticationSystemSupport = authenticationSystemSupport; } public void setTicketRegistrySupport(final TicketRegistrySupport ticketRegistrySupport) { this.ticketRegistrySupport = ticketRegistrySupport; } public void setServicesManager(final ServicesManager servicesManager) { this.servicesManager = servicesManager; } /** * New event based on the id, which contains an error attribute referring to the exception occurred. * * @param id the id * @param error the error * @return the event */ private Event newEvent(final String id, final Exception error) { return new Event(this, id, new LocalAttributeMap<>("error", error)); } private EventFactorySupport getEventFactorySupport() { return new EventFactorySupport(); } }
4.六、修改認證入口authenticationViaFormAction:
package com.saturn.handler.sso; import com.saturn.account.domain.model.SAccount; import org.apache.commons.lang3.StringUtils; import org.jasig.cas.CentralAuthenticationService; import org.jasig.cas.authentication.*; import org.jasig.cas.authentication.principal.Principal; import org.jasig.cas.authentication.principal.Service; import org.jasig.cas.ticket.AbstractTicketException; import org.jasig.cas.ticket.ServiceTicket; import org.jasig.cas.ticket.TicketCreationException; import org.jasig.cas.ticket.TicketGrantingTicket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.binding.message.MessageBuilder; import org.springframework.binding.message.MessageContext; import org.springframework.stereotype.Component; import org.springframework.web.util.CookieGenerator; import org.springframework.webflow.core.collection.LocalAttributeMap; import org.springframework.webflow.execution.Event; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.validation.constraints.NotNull; import java.util.Iterator; import java.util.Map; /** * \file SToAuthenticationHandler * \author LiJiXiao * \date 2018/2/2 20:24 * \brief L * \par Copyright (c): * Copyright (c) Saturn Team. All rights reserved. */ @Component public class SToAuthenticationHandler { private static final String SUCCESS_WITH_WARNINGS = "successWithWarnings"; private static final String AUTHENTICATION_FAILURE = "authenticationFailure"; private static final String PUBLIC_WORKSTATION_ATTRIBUTE = "publicWorkstation"; private static final String WARN = "warn"; private final transient Logger LOGGER = LoggerFactory.getLogger(this.getClass()); @NotNull @Autowired @Qualifier ("centralAuthenticationService") private CentralAuthenticationService centralAuthenticationService; @NotNull @Autowired @Qualifier ("warnCookieGenerator") private CookieGenerator warnCookieGenerator; @NotNull @Autowired (required = false) @Qualifier ("defaultAuthenticationSystemSupport") private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport(); public SToAuthenticationHandler() { } public final Event submit( HttpServletRequest request, HttpServletResponse response, Credential credential, MessageContext messageContext) { return this.isRequestAskingForServiceTicket(request) ? this.grantServiceTicket(request, response, credential) : this.createTicketGrantingTicket(request, response, credential, messageContext); } private boolean isRequestAskingForServiceTicket(HttpServletRequest request) { HttpSession session = request.getSession(); Service service = (Service) session.getAttribute("service"); String ticketGrantingTicketId = (String) session.getAttribute("ticketGrantingTicketId"); return StringUtils.isNotBlank(request.getParameter("renew")) && ticketGrantingTicketId != null && service != null; } private Event grantServiceTicket(HttpServletRequest request, HttpServletResponse response, Credential credential) { HttpSession session = request.getSession(); String ticketGrantingTicketId = (String) session.getAttribute("ticketGrantingTicketId"); try { Service service = (Service) session.getAttribute("service"); AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder( this.authenticationSystemSupport.getPrincipalElectionStrategy()); AuthenticationTransaction transaction = AuthenticationTransaction.wrap(credential); this.authenticationSystemSupport.getAuthenticationTransactionManager().handle(transaction, builder); AuthenticationContext authenticationContext = builder.build(service); ServiceTicket serviceTicketId = this.centralAuthenticationService .grantServiceTicket(ticketGrantingTicketId, service, authenticationContext); request.setAttribute("serviceTicketId", serviceTicketId.getId()); putWarnCookieIfRequestParameterPresent(request, response); return this.newEvent("warn"); } catch (AuthenticationException var9) { return this.newEvent(AUTHENTICATION_FAILURE, var9); } catch (TicketCreationException var10) { this.LOGGER .warn("Invalid attempt to access service using renew=true with different credential. Ending sso " + "session."); this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId); return this.newEvent("error"); } catch (AbstractTicketException var11) { return this.newEvent("error", var11); } } private Event createTicketGrantingTicket( HttpServletRequest request, HttpServletResponse response, Credential credential, MessageContext messageContext) { try { HttpSession session = request.getSession(); Service service = (Service) session.getAttribute("service"); AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder( this.authenticationSystemSupport.getPrincipalElectionStrategy()); AuthenticationTransaction transaction = AuthenticationTransaction.wrap(credential); this.authenticationSystemSupport.getAuthenticationTransactionManager().handle(transaction, builder); AuthenticationContext authenticationContext = builder.build(service); // 獲取帳戶信息 Authentication authentication = authenticationContext.getAuthentication(); Principal principal = authentication.getPrincipal(); SAccount account = (SAccount) principal.getAttributes().get("account"); session.setAttribute("account", account); TicketGrantingTicket tgt = this.centralAuthenticationService.createTicketGrantingTicket(authenticationContext); String ticketValue = tgt != null ? tgt.getId() : null; request.setAttribute("ticket", ticketValue); session.setAttribute("ticket", ticketValue); putWarnCookieIfRequestParameterPresent(request, response); putPublicWorkstationToFlowIfRequestParameterPresent(request); return this.addWarningMessagesToMessageContextIfNeeded(tgt, messageContext) ? this.newEvent(SUCCESS_WITH_WARNINGS) : this.newEvent("success"); } catch (AuthenticationException var9) { this.LOGGER.debug(var9.getMessage(), var9); return this.newEvent("authenticationFailure", var9); } catch (Exception var10) { this.LOGGER.debug(var10.getMessage(), var10); return this.newEvent("error", var10); } } private boolean addWarningMessagesToMessageContextIfNeeded( TicketGrantingTicket tgt, MessageContext messageContext) { boolean foundAndAddedWarnings = false; Iterator var5 = tgt.getAuthentication().getSuccesses().entrySet().iterator(); while (var5.hasNext()) { Map.Entry<String, HandlerResult> entry = (Map.Entry) var5.next(); for (Iterator var7 = ((HandlerResult) entry.getValue()).getWarnings().iterator(); var7.hasNext(); foundAndAddedWarnings = true) { MessageDescriptor message = (MessageDescriptor) var7.next(); addWarningToContext(messageContext, message); } } return foundAndAddedWarnings; } private static void putPublicWorkstationToFlowIfRequestParameterPresent(HttpServletRequest request) { HttpSession session = request.getSession(); if (StringUtils.isNotBlank(request.getParameter(PUBLIC_WORKSTATION_ATTRIBUTE))) { session.setAttribute(PUBLIC_WORKSTATION_ATTRIBUTE, Boolean.TRUE); } } private Event newEvent(String id) { return new Event(this, id); } private Event newEvent(String id, Exception error) { return new Event(this, id, new LocalAttributeMap<>("error", error)); } private static void addWarningToContext(MessageContext context, MessageDescriptor warning) { MessageBuilder builder = (new MessageBuilder()).warning().code(warning.getCode()).defaultText(warning.getDefaultMessage()) .args(warning.getParams()); context.addMessage(builder.build()); } public void setCentralAuthenticationService(CentralAuthenticationService centralAuthenticationService) { this.centralAuthenticationService = centralAuthenticationService; } public void setWarnCookieGenerator(CookieGenerator warnCookieGenerator) { this.warnCookieGenerator = warnCookieGenerator; } public void setAuthenticationSystemSupport(AuthenticationSystemSupport authenticationSystemSupport) { this.authenticationSystemSupport = authenticationSystemSupport; } private void putWarnCookieIfRequestParameterPresent(HttpServletRequest request, HttpServletResponse response) { if (warnCookieGenerator != null) { LOGGER.debug("Evaluating request to determine if warning cookie should be generated"); if (StringUtils.isNotBlank(request.getParameter(WARN))) { warnCookieGenerator.addCookie(response, "true"); } else { warnCookieGenerator.removeCookie(response); } } else { LOGGER.debug("No warning cookie generator is defined"); } } }
五、記着修改訪問路徑映射。。。