package org.apereo.cas.authentication; import com.google.common.collect.ImmutableSet; import eu.bitwalker.useragentutils.Browser; import eu.bitwalker.useragentutils.UserAgent; import eu.bitwalker.useragentutils.Version; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.authentication.principal.Service; import org.apereo.cas.util.CollectionUtils; import org.apereo.cas.web.support.WebUtils; import org.apereo.inspektr.common.web.ClientInfoHolder; import org.jasig.cas.client.validation.AbstractTicketValidationFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.ZonedDateTime; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.servlet.http.HttpServletRequest; /** * This is {@link DefaultAuthenticationResultBuilder}. * * @author Misagh Moayyed * @since 4.2.0 */ public class DefaultAuthenticationResultBuilder implements AuthenticationResultBuilder { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAuthenticationResultBuilder.class); private static final long serialVersionUID = 6180465589526463843L; private Credential providedCredential; private Set authentications = Collections.synchronizedSet(new LinkedHashSet<>()); private PrincipalElectionStrategy principalElectionStrategy; /** * Instantiates a new default authentication result builder. * * @param principalElectionStrategy the principal election strategy */ public DefaultAuthenticationResultBuilder(final PrincipalElectionStrategy principalElectionStrategy) { this.principalElectionStrategy = principalElectionStrategy; } @Override public Optional getInitialAuthentication() { if (this.authentications.isEmpty()) { LOGGER.warn("Authentication chain is empty as no authentications have been collected"); } return this.authentications.stream().findFirst(); } @Override public AuthenticationResultBuilder collect(final Authentication authentication) { this.authentications.add(authentication); return this; } @Override public AuthenticationResultBuilder collect(final Credential credential) { this.providedCredential = credential; return this; } @Override public AuthenticationResult build() { return build(null); } @Override public AuthenticationResult build(final Service service) { final Authentication authentication = buildAuthentication(); if (authentication == null) { LOGGER.info("Authentication result cannot be produced because no authentication is recorded into in the chain. Returning " + "null"); return null; } LOGGER.debug("Building an authentication result for authentication {} and service {}", authentication, service); final DefaultAuthenticationResult res = new DefaultAuthenticationResult(authentication, service); res.setCredentialProvided(this.providedCredential != null); return res; } private boolean isEmpty() { return this.authentications.isEmpty(); } /** * * @return */ private Authentication buildAuthentication() { if (isEmpty()) { LOGGER.warn("No authentication event has been recorded; CAS cannot finalize the authentication result"); return null; } final Map authenticationAttributes = new HashMap<>(); final Map principalAttributes = new HashMap<>(); final AuthenticationBuilder authenticationBuilder = DefaultAuthenticationBuilder.newInstance(); buildAuthenticationHistory(this.authentications, authenticationAttributes, principalAttributes, authenticationBuilder); final Principal primaryPrincipal = getPrimaryPrincipal(this.authentications, principalAttributes); authenticationBuilder.setPrincipal(primaryPrincipal); LOGGER.debug("Determined primary authentication principal to be [{}]", primaryPrincipal); //====== 保存客户端ip到验证信息里 Add bY Tanbin ======= HttpServletRequest request = WebUtils.getHttpServletRequest(); if(null != request) { String uaStr = request.getHeader("User-Agent"); UserAgent ua = UserAgent.parseUserAgentString(uaStr); if(null != ua) { Browser browser=ua.getBrowser(); Version broVersion=browser.getVersion(uaStr); final String browserInfo=browser.getName()+"/"+broVersion.getVersion(); final String clientIp = ClientInfoHolder.getClientInfo().getClientIpAddress(); String serviceUrl = request.getParameter("service"); LOGGER.debug("##### client IP address is [{}], browser is [{}], serviceUrl is [{}]", clientIp, browserInfo, serviceUrl); authenticationAttributes.put("clientIp", clientIp); authenticationAttributes.put("browserInfo", browserInfo); authenticationAttributes.put("serviceUrl", serviceUrl); } } //====== Add bY Tanbin END ======= authenticationBuilder.setAttributes(authenticationAttributes); LOGGER.debug("Collected authentication attributes for this result are [{}]", authenticationAttributes); authenticationBuilder.setAuthenticationDate(ZonedDateTime.now()); final Authentication auth = authenticationBuilder.build(); LOGGER.debug("Authentication result commenced at [{}]", auth.getAuthenticationDate()); return auth; } private static void buildAuthenticationHistory(final Set authentications, final Map authenticationAttributes, final Map principalAttributes, final AuthenticationBuilder authenticationBuilder) { LOGGER.debug("Collecting authentication history based on [{}] authentication events", authentications.size()); authentications.stream().forEach(authn -> { final Principal authenticatedPrincipal = authn.getPrincipal(); LOGGER.debug("Evaluating authentication principal [{}] for inclusion in result", authenticatedPrincipal); principalAttributes.putAll(authenticatedPrincipal.getAttributes()); LOGGER.debug("Collected principal attributes [{}] for inclusion in this result for principal [{}]", principalAttributes, authenticatedPrincipal.getId()); authn.getAttributes().keySet().stream().forEach(attrName -> { if (authenticationAttributes.containsKey(attrName)) { LOGGER.debug("Collecting multi-valued authentication attribute [{}]", attrName); final Object oldValue = authenticationAttributes.remove(attrName); LOGGER.debug("Converting authentication attribute [{}] to a collection of values", attrName); final Collection listOfValues = CollectionUtils.convertValueToCollection(oldValue); final Object newValue = authn.getAttributes().get(attrName); listOfValues.addAll(CollectionUtils.convertValueToCollection(newValue)); authenticationAttributes.put(attrName, listOfValues); LOGGER.debug("Collected multi-valued authentication attribute [{}] -> [{}]", attrName, listOfValues); } else { final Object value = authn.getAttributes().get(attrName); if (value != null) { authenticationAttributes.put(attrName, value); LOGGER.debug("Collected single authentication attribute [{}] -> [{}]", attrName, value); } else { LOGGER.warn("Authentication attribute [{}] has no value and is not collected", attrName); } } }); LOGGER.debug("Finalized authentication attributes [{}] for inclusion in this authentication result", authenticationAttributes); authenticationBuilder.addSuccesses(authn.getSuccesses()) .addFailures(authn.getFailures()) .addCredentials(authn.getCredentials()); }); } /** * Principal id is and must be enforced to be the same for all authentications. * Based on that restriction, it's safe to simply grab the first principal id in the chain * when composing the authentication chain for the caller. */ private Principal getPrimaryPrincipal(final Set authentications, final Map principalAttributes) { return this.principalElectionStrategy.nominate(ImmutableSet.copyOf(authentications), principalAttributes); } public void setPrincipalElectionStrategy(final PrincipalElectionStrategy principalElectionStrategy) { this.principalElectionStrategy = principalElectionStrategy; } }