CAS流程简析 服务端校验Ticket
相关阅读
- 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
方法来源于父类AbstractServiceValidateController
的handleRequestInternal
方法,代码如下:
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);}
}
主要逻辑如下:
- 获取并校验
Service
信息和ST ID; - 获取pgtUrl;
- 校验ST ID;
- 校验认证结果;
- 获取proxyIou;
- 创建视图;
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
继承自父类AbstractArgumentExtractor
,AbstractArgumentExtractor
实现了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
创建的Service
为SimpleWebApplicationServiceImpl
,支持单点登出;
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);}}
}
主要逻辑如下:
- 根据Service信息获取已注册Service信息并校验;
- 根据ST ID获取ST;
- 校验ST;
- 创建认证结果;
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
对应的是DefaultServicesManagerImpl
,findServiceBy
方法的代码如下:
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是否过期
ServiceTicketImpl
的isExpired
方法继承自父类AbstractTicket
,AbstractTicket
实现了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.timeToKillInSeconds
和st.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
方法继承自父类AbstractCasProtocolValidationSpecification
,AbstractCasProtocolValidationSpecification
实现了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.successView
和this.failureView
被V3ServiceValidateController
重写,代码如下:
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相关推荐
- 简析服务端通过geotools导入SHP至PG的方法
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 项目中需要在浏览器端直接上传SHP后服务端进行数据的自动入PG ...
- facebook登录服务端校验,Facebook oidc,Meta oidc服务端校验
相对应的客户端文档可以先阅读这里: https://developers.facebook.com/docs/facebook-login/limited-login/ios 如果你选择的是 http ...
- Android开机启动流程简析
Android开机启动流程简析 (一) 文章目录 Android开机启动流程简析 (一) 前言 一.开机启动的流程概述 二.Android的启动过程分析 (1).总体流程 init简述 Zygote简 ...
- Linux的启动流程简析(以Debian为例)
Linux的启动流程简析(以Debian为例) 正文: 前面的文章探讨BIOS和主引导记录的作用.那篇文章不涉及操作系统,只与主板的板载程序有关.今天,我想接着往下写,探讨操作系统接管硬件以后发生的事 ...
- 魔方APP项目-07-客户端提交登录信息、在APICloud中集成防水墙验证码,前端获取显示并校验验证码、服务端校验验证码、保存用户登录状态,APICloud提供的数据存储、客户端保存用户登陆数据
用户登录 一.客户端提交登录信息 html/login.html,代码: <!DOCTYPE html> <html> <head><title>登录& ...
- uboot源码分析(1)uboot 命令解析流程简析
uboot 命令解析流程简析 uboot正常启动后,会调用main_loop(void)函数,进入main_loop()之后,如果在规定的时间(CONFIG_BOOTDELAY)内,没有检查到任何按键 ...
- 微信APP支付接入流程(含服务端,非第三方)
此文章与简书内容同步更新https://www.jianshu.com/p/3dac1db27247 首先是微信APP的支付流程,看着很多流程,其实主要的大致分为以下几个步骤 1.客户端选择商品下单( ...
- Python源码学习:启动流程简析
Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> Python简介: python主要是动态语言,虽然Python语言也有编译,生成中 ...
- 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)经典完整 ...
最新文章
- 转:Flutter Decoration背景设定(边框、圆角、阴影、形状、渐变、背景图像等)...
- 第四次作业 结对编程-黄金点游戏
- mysql汉化版不同_Mysql各个版本区别及官网下载
- oracle里面查询重复数据的方法
- 2020-11-23(dll注入方法)
- 计算机网络tcp三次握手,快速掌握_计算机网络_TCP_三次握手
- php读取云平台数据库,读取Read · ThinkPHP5+数据库和模型 · 看云
- vba 正则表达式_VBA中正则表达式与数组结合的应用案例!
- 数据科学和人工智能技术笔记 二、数据准备
- asp网上书店的代码_使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群
- hdu 1507 Largest Rectangle in a Histogram 动态规划计算最大面积
- 通用权限管理系统基类中数据库的连接
- 通过Properties读取配置文件
- 多频外差三维重建 投影仪非线性误差补偿
- laravel-excel 中设置列宽,单元格内容垂直和水平都居中
- 良好的编程习惯有哪些?
- 华为设备VRRP多VLAN负载分担
- 华为路由器联动_华为移动路由体验报告:你的随身WiFi伴侣
- 苹果电脑开机慢怎么办 苹果笔记本开机特别慢的处理方法
- javascript中的三角函数和反三角函数
热门文章
- 华为ec6109u当贝桌面烧录包
- Django之MTV
- 高考录取查询一直显示服务器错误什么情况,今年高考再出“屏蔽生”,查分页面没成绩,反应过来坐等985录取...
- Windows 10 优化方案
- 实测windows 7兼容软件
- 格式工厂的另类用法:视频下载、视频剪辑、动图GIF、屏幕录像
- Compose ConstraintLayout 详讲
- mysql查询不出来乱码_MySQL命令行查询乱码解决办法
- 河北工业大学计算机科学与软件,河北工业大学计算机科学与软件系介绍
- 德州计算机信息技术学校电话,山东省信息技术学校