问题记录
1、配置文件报错,却可以正常运行(cas的war包和maven overlay 配置已删除),一脸懵逼。。。
2、spring加载bean,id="warnCookieGenerator"时
实现不是:org.jasig.cas.web.support.CookieRetrievingCookieGenerator
而是:org.jasig.cas.web.WarningCookieRetrievingCookieGenerator
错误出现背景,比照cas中login-webflow.xml定义的流程改写为相应的controller和辅助类,
然后在在加载"warnCookieGenerator"实现时,class指定错误,使其识别不出HttpServletResponse实例,然后断言一直报错。
解决思路,比较将自己改造的流程和cas原始流程都debug一遍,比较相关类的实现是否一致。
3、将cas生成的tgt放入session中,下一次请求时,获取不到tgt属性。
原因可能是未修改的cas处理逻辑中,某一处将名为:“ticketGrantingTicketId“”的属性移除了。
暂时未找到移除位置或逻辑,将属性名改为“ticket”,问题解决。
改造过程
1、自定义认证实现
1.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"); } Maptoken = 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.2、修改配置引入自定义认证实现
在deployerConfigContext.xml中找到:
下添加bean:
2、实现自定义登陆数据模型
2.1、新建实现了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.2、因为本次使用cas不再使用spring-web-flow,所以不需修改:login-webflow.xml。只需要使用spring-mvc中的@requestBody等标签,直接接收Credential实例就好。
3、自定义返回用户信息
有两种方法,先了解小用户信息返回流程:
认证完之后,可以直接获取需要的账户信息,然后将账户信息放入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>,就好了。
4、将登陆流程翻译成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. */@Controllerpublic 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); } MapresponseInfo = 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.1、代替流程文件中的初始域数据类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 */@Componentpublic 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 ListargumentExtractors; /** * 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.2、流程中的第一个处理节点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 */@Componentpublic 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.3、第二个节点: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 */@Componentpublic 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 ListlogoutRequests = this.centralAuthenticationService.destroyTicketGrantingTicket(tgtId); session.setAttribute("logoutRequests",logoutRequests); } this.ticketGrantingTicketCookieGenerator.removeCookie(response); this.warnCookieGenerator.removeCookie(response); return this.eventFactorySupport.success(this); }}
4.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. */@Componentpublic 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.5、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 */@Componentpublic 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.6、修改认证入口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. */@Componentpublic 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.Entryentry = (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"); } }}
5、记着修改访问路径映射。。。