@Override
    protected String obtainValueFromCompoundCookie(final String value, final HttpServletRequest request) {
        val cookieParts = Splitter.on(String.valueOf(COOKIE_FIELD_SEPARATOR)).splitToList(value);

        val cookieValue = cookieParts.getFirst();
        if (!cookieProperties.isPinToSession()) {
            LOGGER.trace("Cookie session-pinning is disabled. Returning cookie value as it was provided");
            return cookieValue;
        }

        if (cookieParts.size() != COOKIE_FIELDS_LENGTH) {
            throw new InvalidCookieException("Invalid cookie. Required fields are missing");
        }
        val cookieClientLocationOrIp = cookieParts.get(1);
        val cookieUserAgent = cookieParts.get(2);

        if (Stream.of(cookieValue, cookieClientLocationOrIp, cookieUserAgent).anyMatch(StringUtils::isBlank)) {
            throw new InvalidCookieException("Invalid cookie. Required fields are empty");
        }

        val clientInfo = ClientInfoHolder.getClientInfo();
        if (clientInfo == null) {
            val message = "Unable to match required remote address %s because client ip at time of cookie creation is unknown".formatted(cookieClientLocationOrIp);
            LOGGER.warn(message);
            throw new InvalidCookieException(message);
        }

        if (cookieProperties.isGeoLocateClientSession()) {
            val clientLocationOrIp = getClientGeoLocation(clientInfo);
            if (!cookieClientLocationOrIp.equals(clientLocationOrIp)) {
                val message = "Invalid cookie. Required remote address %s does not match %s".formatted(cookieClientLocationOrIp, clientLocationOrIp);
                LOGGER.warn(message);
                throw new InvalidCookieException(message);
            }
        } else {
            val clientIpAddress = clientInfo.getClientIpAddress();
            if (!cookieClientLocationOrIp.equals(clientIpAddress)) {
                if (StringUtils.isBlank(cookieProperties.getAllowedIpAddressesPattern())
                    || !RegexUtils.find(cookieProperties.getAllowedIpAddressesPattern(), clientIpAddress)) {
                    val message = "Invalid cookie. Required remote address %s does not match %s".formatted(cookieClientLocationOrIp, clientIpAddress);
                    LOGGER.warn(message);
                    throw new InvalidCookieException(message);
                }
                LOGGER.debug("Required remote address [{}] does not match [{}], but it's authorized to proceed",
                    cookieClientLocationOrIp, clientIpAddress);
            }
        }

        val agent = HttpRequestUtils.getHttpServletRequestUserAgent(request);
        if (!cookieUserAgent.equals(agent)) {
            val message = "Invalid cookie. Required user-agent %s does not match %s".formatted(cookieUserAgent, agent);
            LOGGER.warn(message);
            throw new InvalidCookieException(message);
        }
        return cookieValue;
    }
The {@link DefaultCasCookieValueManager} is responsible for creating
the CAS SSO cookie and encrypting and signing its value.
<p>
This class by default ({@code CookieProperties.isPinToSession=true}) ensures the cookie is used on a
request from same IP and with the same user-agent as when cookie was created.
The client info (with original client ip) may be null if cluster failover occurs and session replication not working.

@author Misagh Moayyed
@since 4.1
core/cas-server-core-cookie-api/src/main/java/org/apereo/cas/web/support/mgmr/DefaultCasCookieValueManager.java