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<Authentication> 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<Authentication> 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<String, Object> authenticationAttributes = new HashMap<>(5);
|
final Map<String, Object> principalAttributes = new HashMap<>(5);
|
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<Authentication> authentications,
|
final Map<String, Object> authenticationAttributes,
|
final Map<String, Object> 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<Object> 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<Authentication> authentications, final Map<String, Object> principalAttributes) {
|
return this.principalElectionStrategy.nominate(ImmutableSet.copyOf(authentications), principalAttributes);
|
}
|
|
public void setPrincipalElectionStrategy(final PrincipalElectionStrategy principalElectionStrategy) {
|
this.principalElectionStrategy = principalElectionStrategy;
|
}
|
}
|