相关阅读

  • CAS基础组件 简介
  • CAS流程简析 服务端处理未携带Service登录请求
  • CAS流程简析 服务端处理携带Service登录请求
  • CAS基础组件 客户端过滤器

简介

用户访问客户端的请求若携带Ticket信息,经过客户端配置的过滤器Cas30ProxyReceivingTicketValidationFilter时,该过滤器会将请求中携带的Ticket信息发送到服务端进行校验,若校验通过,才返回鉴权结果;

简析

Cas30ProxyReceivingTicketValidationFilter发送到服务端的请求路径为:/p3/serviceValidate,对应服务端的处理器为V3ServiceValidateController,核心代码如下:

@Component("v3ServiceValidateController")
@Controller
public class V3ServiceValidateController extends AbstractServiceValidateController {/*** Handle model and view.** @param request the request* @param response the response* @return the model and view* @throws Exception the exception*/@RequestMapping(path="/p3/serviceValidate", method = RequestMethod.GET)protected ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response)throws Exception {return super.handleRequestInternal(request, response);}@Override@Autowiredpublic void setValidationSpecificationClass(@Value("org.jasig.cas.validation.Cas20WithoutProxyingValidationSpecification")final Class<?> validationSpecificationClass) {super.setValidationSpecificationClass(validationSpecificationClass);}@Override@Autowiredpublic void setFailureView(@Value("cas3ServiceFailureView") final String failureView) {super.setFailureView(failureView);}@Override@Autowiredpublic void setSuccessView(@Value("cas3ServiceSuccessView") final String successView) {super.setSuccessView(successView);}@Override@Autowiredpublic void setProxyHandler(@Qualifier("proxy20Handler") final ProxyHandler proxyHandler) {super.setProxyHandler(proxyHandler);}
}

V3ServiceValidateController校验Ticket方法来源于父类AbstractServiceValidateControllerhandleRequestInternal方法,代码如下:

protected ModelAndView handleRequestInternal(final HttpServletRequest request, final HttpServletResponse response)throws Exception {// 从请求中获取Service信息final WebApplicationService service = this.argumentExtractor.extractService(request);// 获取ST IDfinal String serviceTicketId = service != null ? service.getArtifactId() : null;// 校验Service和ST IDif (service == null || serviceTicketId == null) {logger.debug("Could not identify service and/or service ticket for service: [{}]", service);return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_REQUEST,CasProtocolConstants.ERROR_CODE_INVALID_REQUEST, null, request, service);}try {// 获取pgtUrl的鉴权结果// 代理模式涉及,本例不涉及TicketGrantingTicket proxyGrantingTicketId = null;final Credential serviceCredential = getServiceCredentialsFromRequest(service, request);if (serviceCredential != null) {proxyGrantingTicketId = handleProxyGrantingTicketDelivery(serviceTicketId, serviceCredential);if (proxyGrantingTicketId == null) {return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,new Object[]{serviceCredential.getId()}, request, service);}}// 校验ST IDfinal Assertion assertion = this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service);// 校验认证结果if (!validateAssertion(request, serviceTicketId, assertion)) {return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_TICKET,CasProtocolConstants.ERROR_CODE_INVALID_TICKET, null, request, service);}// 获取proxyIou// 代理模式涉及,本例不涉及String proxyIou = null;if (serviceCredential != null && this.proxyHandler.canHandle(serviceCredential)) {proxyIou = this.proxyHandler.handle(serviceCredential, proxyGrantingTicketId);if (StringUtils.isEmpty(proxyIou)) {return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,new Object[] {serviceCredential.getId()}, request, service);}}// 校验成功时处理onSuccessfulValidation(serviceTicketId, assertion);logger.debug("Successfully validated service ticket {} for service [{}]", serviceTicketId, service.getId());// 创建成功视图return generateSuccessView(assertion, proxyIou, service, proxyGrantingTicketId);} catch (final AbstractTicketValidationException e) {final String code = e.getCode();return generateErrorView(code, code,new Object[] {serviceTicketId, e.getOriginalService().getId(), service.getId()}, request, service);} catch (final AbstractTicketException te) {return generateErrorView(te.getCode(), te.getCode(),new Object[] {serviceTicketId}, request, service);} catch (final UnauthorizedProxyingException e) {return generateErrorView(e.getMessage(), e.getMessage(), new Object[] {service.getId()}, request, service);} catch (final UnauthorizedServiceException e) {return generateErrorView(e.getMessage(), e.getMessage(), null, request, service);}
}

主要逻辑如下:

  1. 获取并校验Service信息和ST ID;
  2. 获取pgtUrl;
  3. 校验ST ID;
  4. 校验认证结果;
  5. 获取proxyIou;
  6. 创建视图;

1 获取并校验Service信息和ST ID

首先分析从request中如何获取Service和ST ID,代码如下:

@Qualifier("defaultArgumentExtractor")
private ArgumentExtractor argumentExtractor;final WebApplicationService service = this.argumentExtractor.extractService(request);
final String serviceTicketId = service != null ? service.getArtifactId() : null;

defaultArgumentExtractor对应的是DefaultArgumentExtractor,其extractService继承自父类AbstractArgumentExtractorAbstractArgumentExtractor实现了extractService的算法模板,提供算法细节extractServiceInternal由子类实现实现,还提供了serviceFactoryList供子类实现使用,其代码如下:

public final WebApplicationService extractService(final HttpServletRequest request) {// 从request中抽取Service信息final WebApplicationService service = extractServiceInternal(request);// logif (service == null) {logger.debug("Extractor did not generate service.");} else {logger.debug("Extractor generated service for: {}", service.getId());}return service;
}protected abstract WebApplicationService extractServiceInternal(HttpServletRequest request);@Resource(name="serviceFactoryList")
protected List<ServiceFactory<? extends WebApplicationService>> serviceFactoryList;protected final List<ServiceFactory<? extends WebApplicationService>> getServiceFactories() {return serviceFactoryList;
}

serviceFactoryList的配置如下:

<!-- services-context.xml -->
<util:list id="serviceFactoryList" value-type="org.jasig.cas.authentication.principal.ServiceFactory"><ref bean="webApplicationServiceFactory" />
</util:list>

DefaultArgumentExtractor实现了算法细节extractServiceInternal,代码如下:

public WebApplicationService extractServiceInternal(final HttpServletRequest request) {for (final ServiceFactory<? extends WebApplicationService> factory : getServiceFactories()) {final WebApplicationService service = factory.createService(request);if (service != null) {// 创建成功则直接返回logger.debug("Created {} based on {}", service, factory);return service;}}logger.debug("No service could be extracted based on the given request");return null;
}

webApplicationServiceFactory对应的是WebApplicationServiceFactory,其createService方法代码如下:

public WebApplicationService createService(final HttpServletRequest request) {final String targetService = request.getParameter(CasProtocolConstants.PARAMETER_TARGET_SERVICE);final String service = request.getParameter(CasProtocolConstants.PARAMETER_SERVICE);final String serviceAttribute = (String) request.getAttribute(CasProtocolConstants.PARAMETER_SERVICE);final String method = request.getParameter(CasProtocolConstants.PARAMETER_METHOD);final String format = request.getParameter(CasProtocolConstants.PARAMETER_FORMAT);final String serviceToUse;if (StringUtils.isNotBlank(targetService)) {// 优先使用targetServiceserviceToUse = targetService;} else if (StringUtils.isNotBlank(service)) {// 其次使用请求参数中的service serviceToUse = service;} else {// 最后使用请求属性中的serviceserviceToUse = serviceAttribute;}// 校验service信息if (StringUtils.isBlank(serviceToUse)) {return null;}// 去除jsession信息final String id = AbstractServiceFactory.cleanupUrl(serviceToUse);// 获取请求参数中的ticket信息,并将其作为Service的artifactIdfinal String artifactId = request.getParameter(CasProtocolConstants.PARAMETER_TICKET);final Response.ResponseType type = HttpMethod.POST.name().equalsIgnoreCase(method) ? Response.ResponseType.POST: Response.ResponseType.REDIRECT;// 创建SimpleWebApplicationServiceImplfinal SimpleWebApplicationServiceImpl webApplicationService =new SimpleWebApplicationServiceImpl(id, serviceToUse,artifactId, new WebApplicationServiceResponseBuilder(type));try {if (StringUtils.isNotBlank(format)) {// 若请求参数中存在format信息,则设置该属性final ValidationResponseType formatType = ValidationResponseType.valueOf(format.toUpperCase());webApplicationService.setFormat(formatType);}} catch (final Exception e) {logger.error("Format specified in the request [{}] is not recognized", format);return null;}return webApplicationService;
}

WebApplicationServiceFactory创建的ServiceSimpleWebApplicationServiceImpl,支持单点登出;

public final class SimpleWebApplicationServiceImpl extends AbstractWebApplicationService
public abstract class AbstractWebApplicationService implements SingleLogoutService
public interface SingleLogoutService extends WebApplicationService
public interface WebApplicationService extends Service

2 获取pgtUrl

根据请求中的pgtUrl信息获取对应的鉴权结果,代码如下:

protected Credential getServiceCredentialsFromRequest(final WebApplicationService service, final HttpServletRequest request) {// 获取请求中的"pgtUrl"参数final String pgtUrl = request.getParameter(CasProtocolConstants.PARAMETER_PROXY_CALLBACK_URL);if (StringUtils.hasText(pgtUrl)) {// pgtUrl参数存在try {// 获取对应的已注册Service信息final RegisteredService registeredService = this.servicesManager.findServiceBy(service);// 校验已注册Service属性verifyRegisteredServiceProperties(registeredService, service);return new HttpBasedServiceCredential(new URL(pgtUrl), registeredService);} catch (final Exception e) {logger.error("Error constructing pgtUrl", e);}}return null;
}

代理模式涉及该处理,本例不涉及;

3 校验ST ID

从request请求中获取到ST ID,需要对其进行校验,代码如下:

final Assertion assertion = this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service);@Qualifier("centralAuthenticationService")
private CentralAuthenticationService centralAuthenticationService;

centralAuthenticationService对应的是CentralAuthenticationServiceImpl,其validateServiceTicket方法代码如下:

public Assertion validateServiceTicket(final String serviceTicketId, final Service service) throws AbstractTicketException {// 根据Service信息获取已注册Service信息final RegisteredService registeredService = this.servicesManager.findServiceBy(service);// 校验已注册Service属性verifyRegisteredServiceProperties(registeredService, service);// 根据ST ID获取STfinal ServiceTicket serviceTicket =  this.ticketRegistry.getTicket(serviceTicketId, ServiceTicket.class);// 校验STif (serviceTicket == null) {logger.info("Service ticket [{}] does not exist.", serviceTicketId);throw new InvalidTicketException(serviceTicketId);}try {synchronized (serviceTicket) {// ST是否过期if (serviceTicket.isExpired()) {logger.info("ServiceTicket [{}] has expired.", serviceTicketId);throw new InvalidTicketException(serviceTicketId);}// ST是否支持当前Serviceif (!serviceTicket.isValidFor(service)) {logger.error("Service ticket [{}] with service [{}] does not match supplied service [{}]",serviceTicketId, serviceTicket.getService().getId(), service);throw new UnrecognizableServiceForServiceTicketValidationException(serviceTicket.getService());}}// 获取ST对应的TGTfinal TicketGrantingTicket root = serviceTicket.getGrantingTicket().getRoot();// 获取鉴权结果final Authentication authentication = getAuthenticationSatisfiedByPolicy(root, new ServiceContext(serviceTicket.getService(), registeredService));// 获取Principalfinal Principal principal = authentication.getPrincipal();final RegisteredServiceAttributeReleasePolicy attributePolicy = registeredService.getAttributeReleasePolicy();logger.debug("Attribute policy [{}] is associated with service [{}]", attributePolicy, registeredService);@SuppressWarnings("unchecked")final Map<String, Object> attributesToRelease = attributePolicy != null? attributePolicy.getAttributes(principal) : Collections.EMPTY_MAP;final String principalId = registeredService.getUsernameAttributeProvider().resolveUsername(principal, service);final Principal modifiedPrincipal = this.principalFactory.createPrincipal(principalId, attributesToRelease);final AuthenticationBuilder builder = DefaultAuthenticationBuilder.newInstance(authentication);builder.setPrincipal(modifiedPrincipal);// 创建认证结果final Assertion assertion = new ImmutableAssertion(builder.build(),serviceTicket.getGrantingTicket().getChainedAuthentications(),serviceTicket.getService(),serviceTicket.isFromNewLogin());// 发布ST校验成功事件doPublishEvent(new CasServiceTicketValidatedEvent(this, serviceTicket, assertion));// 返回认证结果return assertion;} finally {if (serviceTicket.isExpired()) {this.ticketRegistry.deleteTicket(serviceTicketId);}}
}

主要逻辑如下:

  1. 根据Service信息获取已注册Service信息并校验;
  2. 根据ST ID获取ST;
  3. 校验ST;
  4. 创建认证结果;

3.1 根据Service信息获取已注册Service信息并校验

代码如下:

final RegisteredService registeredService = this.servicesManager.findServiceBy(service);private ServicesManager servicesManager;@Autowired
public void setServicesManager(@Qualifier("servicesManager") final ServicesManager servicesManager) {this.servicesManager = servicesManager;
}

servicesManager对应的是DefaultServicesManagerImplfindServiceBy方法的代码如下:

public RegisteredService findServiceBy(final Service service) {final Collection<RegisteredService> c = convertToTreeSet();// 遍历注册的Servicefor (final RegisteredService r : c) {if (r.matches(service)) {// 若匹配则返回该注册的Servicereturn r;}}return null;
}public TreeSet<RegisteredService> convertToTreeSet() {return new TreeSet<>(this.services.values());
}private ConcurrentHashMap<Long, RegisteredService> services = new ConcurrentHashMap<>();public void load() {final ConcurrentHashMap<Long, RegisteredService> localServices =new ConcurrentHashMap<>();// 借助this.serviceRegistryDao加载注册Service信息for (final RegisteredService r : this.serviceRegistryDao.load()) {LOGGER.debug("Adding registered service {}", r.getServiceId());localServices.put(r.getId(), r);}this.services = localServices;LOGGER.info("Loaded {} services from {}.", this.services.size(),this.serviceRegistryDao);
}public DefaultServicesManagerImpl(@Qualifier("serviceRegistryDao") final ServiceRegistryDao serviceRegistryDao) {this.serviceRegistryDao = serviceRegistryDao;load();
}

ServiceRegistryDao接口有多种实现,可根据实际需求,在配置文件中自行配置该接口的实现;以InMemoryServiceRegistryDaoImpl为例,分析load方法实现,代码如下:

public List<RegisteredService> load() {return this.registeredServices;
}@PostConstruct
public void afterPropertiesSet() {final String[] aliases =this.applicationContext.getAutowireCapableBeanFactory().getAliases("inMemoryServiceRegistryDao");// 如果配置了"inMemoryServiceRegistryDao"if (aliases.length > 0) {LOGGER.debug("{} is used as the active service registry dao", this.getClass().getSimpleName());try {// 从IOC容器中找到"inMemoryRegisteredServices"的配置final List<RegisteredService> list = (List<RegisteredService>)this.applicationContext.getBean("inMemoryRegisteredServices", List.class);if (list != null) {LOGGER.debug("Loaded {} services from the application context for {}",list.size(),this.getClass().getSimpleName());this.registeredServices = list;}} catch (final Exception e) {LOGGER.debug("No registered services are defined for {}", this.getClass().getSimpleName());}}
}

inMemoryRegisteredServices配置信息举例如下:

<util:list id="inMemoryRegisteredServices"><bean class="org.jasig.cas.services.RegexRegisteredService"p:id="0" p:name="HTTP and IMAP" p:description="Allows HTTP(S) and IMAP(S) protocols"p:serviceId="^(https?|imaps?)://.*" p:evaluationOrder="10000001" ><property name="attributeReleasePolicy"><bean class="org.jasig.cas.services.ReturnAllAttributeReleasePolicy" /></property></bean>
</util:list>

常用的RegisteredService接口的实现类为RegexRegisteredService,支持正则表达式,其match方法实现代码如下:

public boolean matches(final Service service) {if (this.servicePattern == null) {this.servicePattern = RegexUtils.createPattern(this.serviceId);}// 根据serviceId的正则规则进行匹配return service != null && this.servicePattern != null&& this.servicePattern.matcher(service.getId()).matches();
}

3.2 根据ST ID获取ST

代码如下:

final ServiceTicket serviceTicket =  this.ticketRegistry.getTicket(serviceTicketId, ServiceTicket.class);@Resource(name="ticketRegistry")
protected TicketRegistry ticketRegistry;

TicketRegistry由用户配置,可以看下DefaultTicketRegistry的实现,getTicket方法由AbstractTicketRegistry实现了算法模板,代码如下:

public final <T extends Ticket> T getTicket(final String ticketId, final Class<? extends Ticket> clazz) {Assert.notNull(clazz, "clazz cannot be null");final Ticket ticket = this.getTicket(ticketId);if (ticket == null) {return null;}if (!clazz.isAssignableFrom(ticket.getClass())) {throw new ClassCastException("Ticket [" + ticket.getId()+ " is of type " + ticket.getClass()+ " when we were expecting " + clazz);}return (T) ticket;
}

DefaultTicketRegistry使用内存中的Map存储Ticket信息,其getTicket方法的代码如下:

public Ticket getTicket(final String ticketId) {if (ticketId == null) {return null;}logger.debug("Attempting to retrieve ticket [{}]", ticketId);final Ticket ticket = this.cache.get(ticketId);if (ticket != null) {logger.debug("Ticket [{}] found in registry.", ticketId);}return ticket;
}

3.3 校验ST

3.3.1 ST是否过期

ServiceTicketImplisExpired方法继承自父类AbstractTicketAbstractTicket实现了isExpired的算法模板,代码如下:

public final boolean isExpired() {final TicketGrantingTicket tgt = getGrantingTicket();return this.expirationPolicy.isExpired(this)|| (tgt != null && tgt.isExpired())|| isExpiredInternal();
}public final TicketGrantingTicket getGrantingTicket() {return this.ticketGrantingTicket;
}protected boolean isExpiredInternal() {return false;
}

this.expirationPolicy属性由DefaultServiceTicketFactory创建ServiceTicketImpl时传入,代码如下:

final ServiceTicket serviceTicket = ticketGrantingTicket.grantServiceTicket(ticketId,service,this.serviceTicketExpirationPolicy,credentialsProvided,this.onlyTrackMostRecentSession);@Resource(name="serviceTicketExpirationPolicy")
protected ExpirationPolicy serviceTicketExpirationPolicy;

serviceTicketExpirationPolicy可由用户自行配置,默认配置如下:

<!-- deployerConfigContext.xml -->
<alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" />
<alias name="multiTimeUseOrTimeoutExpirationPolicy" alias="serviceTicketExpirationPolicy" />

multiTimeUseOrTimeoutExpirationPolicy对应的是MultiTimeUseOrTimeoutExpirationPolicy,其isExpired方法代码如下:

// 默认超时时间为10s
@Value("#{${st.timeToKillInSeconds:10}*1000L}")
private final long timeToKillInMilliSeconds;// 默认为1
@Value("${st.numberOfUses:1}")
private final int numberOfUses;public boolean isExpired(final TicketState ticketState) {if (ticketState == null) {LOGGER.debug("Ticket state is null for {}", this.getClass().getSimpleName());return true;}// 校验ST的使用数final long countUses = ticketState.getCountOfUses();if (countUses >= this.numberOfUses) {LOGGER.debug("Ticket usage count {} is greater than or equal to {}", countUses, this.numberOfUses);return true;}final long systemTime = System.currentTimeMillis();final long lastTimeUsed = ticketState.getLastTimeUsed();final long difference = systemTime - lastTimeUsed;// 校验ST的超时时间if (difference >= this.timeToKillInMilliSeconds) {LOGGER.debug("Ticket has expired because the difference between current time [{}] "+ "and ticket time [{}] is greater than or equal to [{}]", systemTime, lastTimeUsed,this.timeToKillInMilliSeconds);return true;}return false;
}

st.timeToKillInSecondsst.numberOfUses可在cas.properties文件中配置;

3.3.2 ST是否支持当前Service

代码如下:

public boolean isValidFor(final Service serviceToValidate) {// 更新ST的访问记录updateState();// return serviceToValidate.matches(this.service);
}// AbstractWebApplicationService.javapublic boolean matches(final Service service) {try {final String thisUrl = URLDecoder.decode(this.id, "UTF-8");final String serviceUrl = URLDecoder.decode(service.getId(), "UTF-8");logger.trace("Decoded urls and comparing [{}] with [{}]", thisUrl, serviceUrl);return thisUrl.equalsIgnoreCase(serviceUrl);} catch (final Exception e) {logger.error(e.getMessage(), e);}return false;
}

3.4 创建认证结果

ST校验通过后,根据ST找到对应的TGT,从而找到对应的鉴权结果,然后将创建认证结果;

4 校验认证结果

代码如下:

private boolean validateAssertion(final HttpServletRequest request, final String serviceTicketId, final Assertion assertion) {final ValidationSpecification validationSpecification = this.getCommandClass();final ServletRequestDataBinder binder = new ServletRequestDataBinder(validationSpecification, "validationSpecification");initBinder(request, binder);binder.bind(request);// 是否满足特定校验要求if (!validationSpecification.isSatisfiedBy(assertion)) {logger.debug("Service ticket [{}] does not satisfy validation specification.", serviceTicketId);return false;}return true;
}private ValidationSpecification getCommandClass() {try {return (ValidationSpecification) this.validationSpecificationClass.newInstance();} catch (final Exception e) {throw new RuntimeException(e);}
}private Class<?> validationSpecificationClass = Cas20ProtocolValidationSpecification.class;

默认使用的是Cas20ProtocolValidationSpecification,其isSatisfiedBy方法继承自父类AbstractCasProtocolValidationSpecificationAbstractCasProtocolValidationSpecification实现了isSatisfiedBy的算法模板,并提供算法细节isSatisfiedByInternal由子类实现,代码如下:

public final boolean isSatisfiedBy(final Assertion assertion) {return isSatisfiedByInternal(assertion)&& (!this.renew || assertion.isFromNewLogin());
}protected abstract boolean isSatisfiedByInternal(Assertion assertion);

Cas20ProtocolValidationSpecification实现了算法细节isSatisfiedByInternal,代码如下:

protected boolean isSatisfiedByInternal(final Assertion assertion) {return true;
}

5 获取proxyIou

本例不涉及;

6 创建视图

代码如下:

private ModelAndView generateSuccessView(final Assertion assertion, final String proxyIou,final WebApplicationService service,final TicketGrantingTicket proxyGrantingTicket) {final ModelAndView modelAndView = getModelAndView(true, service);modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_ASSERTION, assertion);modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_SERVICE, service);modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET_IOU, proxyIou);if (proxyGrantingTicket != null) {modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET, proxyGrantingTicket.getId());}final Map<String, ?> augmentedModelObjects = augmentSuccessViewModelObjects(assertion);if (augmentedModelObjects != null) {modelAndView.addAllObjects(augmentedModelObjects);}return modelAndView;
}private ModelAndView getModelAndView(final boolean isSuccess, final WebApplicationService service) {if (service != null){if (service.getFormat() == ValidationResponseType.JSON) {return new ModelAndView(DEFAULT_SERVICE_VIEW_NAME_JSON);}}return new ModelAndView(isSuccess ? this.successView : this.failureView);
}

this.successViewthis.failureViewV3ServiceValidateController重写,代码如下:

public void setFailureView(@Value("cas3ServiceFailureView") final String failureView) {super.setFailureView(failureView);
}public void setSuccessView(@Value("cas3ServiceSuccessView") final String successView) {super.setSuccessView(successView);
}

6.1 成功视图

cas3ServiceSuccessView的配置如下:

@Component("cas3ServiceSuccessView")
public static class Success extends Cas30ResponseView {/*** Instantiates a new Success.* @param view the view*/@Autowiredpublic Success(@Qualifier("cas3JstlSuccessView")final AbstractUrlBasedView view) {super(view);super.setSuccessResponse(true);}
}

cas3JstlSuccessView的配置如下:

<!-- protocolViewsConfiguration.xml -->
<bean id="cas3JstlSuccessView" class="org.springframework.web.servlet.view.JstlView"c:url="/WEB-INF/view/jsp/protocol/3.0/casServiceValidationSuccess.jsp" />

casServiceValidationSuccess.jsp文件的内容如下:

<%@ page session="false" contentType="application/xml; charset=UTF-8" %>
<%@ page import="java.util.*, java.util.Map.Entry" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationSuccess><cas:user>${fn:escapeXml(principal.id)}</cas:user><c:if test="${not empty pgtIou}"><cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket></c:if><c:if test="${fn:length(chainedAuthentications) > 0}"><cas:proxies><c:forEach var="proxy" items="${chainedAuthentications}" varStatus="loopStatus" begin="0"end="${fn:length(chainedAuthentications)}" step="1"><cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy></c:forEach></cas:proxies></c:if><c:if test="${fn:length(attributes) > 0}"><cas:attributes><c:forEach var="attr"items="${attributes}"varStatus="loopStatus" begin="0"end="${fn:length(attributes)}"step="1"><c:forEach var="attrval" items="${attr.value}"><cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attrval)}</cas:${fn:escapeXml(attr.key)}></c:forEach></c:forEach></cas:attributes></c:if></cas:authenticationSuccess>
</cas:serviceResponse>

本例中,服务端只会将principal.id信息返回给客户端;

6.2 失败视图

cas3ServiceFailureView的配置如下:

<!-- protocolViewsConfiguration.xml -->
<bean id="cas3ServiceFailureView" class="org.springframework.web.servlet.view.JstlView"c:url="/WEB-INF/view/jsp/protocol/3.0/casServiceValidationFailure.jsp" />

casServiceValidationFailure.jsp文件的内容如下:

<%@ page session="false" contentType="application/xml; charset=UTF-8" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationFailure code='${code}'>${fn:escapeXml(description)}</cas:authenticationFailure>
</cas:serviceResponse>

服务端将错误码和错误信息返回给客户端;

至此,服务端校验Ticket流程结束。

CAS流程简析 服务端校验Ticket相关推荐

  1. 简析服务端通过geotools导入SHP至PG的方法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 项目中需要在浏览器端直接上传SHP后服务端进行数据的自动入PG ...

  2. facebook登录服务端校验,Facebook oidc,Meta oidc服务端校验

    相对应的客户端文档可以先阅读这里: https://developers.facebook.com/docs/facebook-login/limited-login/ios 如果你选择的是 http ...

  3. Android开机启动流程简析

    Android开机启动流程简析 (一) 文章目录 Android开机启动流程简析 (一) 前言 一.开机启动的流程概述 二.Android的启动过程分析 (1).总体流程 init简述 Zygote简 ...

  4. Linux的启动流程简析(以Debian为例)

    Linux的启动流程简析(以Debian为例) 正文: 前面的文章探讨BIOS和主引导记录的作用.那篇文章不涉及操作系统,只与主板的板载程序有关.今天,我想接着往下写,探讨操作系统接管硬件以后发生的事 ...

  5. 魔方APP项目-07-客户端提交登录信息、在APICloud中集成防水墙验证码,前端获取显示并校验验证码、服务端校验验证码、保存用户登录状态,APICloud提供的数据存储、客户端保存用户登陆数据

    用户登录 一.客户端提交登录信息 html/login.html,代码: <!DOCTYPE html> <html> <head><title>登录& ...

  6. uboot源码分析(1)uboot 命令解析流程简析

    uboot 命令解析流程简析 uboot正常启动后,会调用main_loop(void)函数,进入main_loop()之后,如果在规定的时间(CONFIG_BOOTDELAY)内,没有检查到任何按键 ...

  7. 微信APP支付接入流程(含服务端,非第三方)

    此文章与简书内容同步更新https://www.jianshu.com/p/3dac1db27247 首先是微信APP的支付流程,看着很多流程,其实主要的大致分为以下几个步骤 1.客户端选择商品下单( ...

  8. Python源码学习:启动流程简析

    Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> Python简介: python主要是动态语言,虽然Python语言也有编译,生成中 ...

  9. CAS (1) —— Mac下配置CAS到Tomcat(服务端)(转)

    tomcat版本: tomcat-8.0.29 jdk版本: jdk1.8.0_65 cas版本: cas4.1.2 cas-client-3.4.1 参考来源: CAS实现单点登录(SSO)经典完整 ...

最新文章

  1. 转:Flutter Decoration背景设定(边框、圆角、阴影、形状、渐变、背景图像等)...
  2. 第四次作业 结对编程-黄金点游戏
  3. mysql汉化版不同_Mysql各个版本区别及官网下载
  4. oracle里面查询重复数据的方法
  5. 2020-11-23(dll注入方法)
  6. 计算机网络tcp三次握手,快速掌握_计算机网络_TCP_三次握手
  7. php读取云平台数据库,读取Read · ThinkPHP5+数据库和模型 · 看云
  8. vba 正则表达式_VBA中正则表达式与数组结合的应用案例!
  9. 数据科学和人工智能技术笔记 二、数据准备
  10. asp网上书店的代码_使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群
  11. hdu 1507 Largest Rectangle in a Histogram 动态规划计算最大面积
  12. 通用权限管理系统基类中数据库的连接
  13. 通过Properties读取配置文件
  14. 多频外差三维重建 投影仪非线性误差补偿
  15. laravel-excel 中设置列宽,单元格内容垂直和水平都居中
  16. 良好的编程习惯有哪些?
  17. 华为设备VRRP多VLAN负载分担
  18. 华为路由器联动_华为移动路由体验报告:你的随身WiFi伴侣
  19. 苹果电脑开机慢怎么办 苹果笔记本开机特别慢的处理方法
  20. javascript中的三角函数和反三角函数

热门文章

  1. 华为ec6109u当贝桌面烧录包
  2. Django之MTV
  3. 高考录取查询一直显示服务器错误什么情况,今年高考再出“屏蔽生”,查分页面没成绩,反应过来坐等985录取...
  4. Windows 10 优化方案
  5. 实测windows 7兼容软件
  6. 格式工厂的另类用法:视频下载、视频剪辑、动图GIF、屏幕录像
  7. Compose ConstraintLayout 详讲
  8. mysql查询不出来乱码_MySQL命令行查询乱码解决办法
  9. 河北工业大学计算机科学与软件,河北工业大学计算机科学与软件系介绍
  10. 德州计算机信息技术学校电话,山东省信息技术学校