经过上网查看资料发现单用户登陆主要就是涉及到票据与用户之间的关系,参考两篇博文(原文链接在文章底部),现整理如下:
管理登录时TGT与ST颁发的代码内容在
org.jasig.cas.CentralAuthenticationServiceImpl
这个类当中, 在这个类中首先先验证用户名,密码。然后进行创建TGT,ST,然后在缓存TGT,并且ST有效性的验证,以及销毁TGT都是在这个类中。
管理和记录TGT和ST并记录关联的内容在
org.jasig.cas.ticket.registry.DefaultTicketRegistry
这个类中,主要是进行管理和存储TGT和ST,以及记录TGT和ST之间的关系,及通过tickeid查询ticket,添加ticket,删除ticked等。
实现单用户登陆,对这两个类进行重写,为了省事,不修改 ticketRegistry.xml 配置文件,重写这两个类时直接在项目的 src 下面创建和这个源码路径一样的包 org.jasig.cas 和 org.jasig.cas.ticket.registry ,代码如下:
下面是重写 DefaultTicketRegistry,主要就是增加 用户名与票据ID对应关系
/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.ticket.registry;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.authentication.principal.Service;
import org.springframework.util.Assert;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Implementation of the TicketRegistry that is backed by a ConcurrentHashMap.
*
* @author Scott Battaglia
* @since 3.0.0
*/
public final class DefaultTicketRegistry extends AbstractTicketRegistry {
/** A HashMap to contain the tickets. */
private final Map<String, Ticket> cache;
/** 保存用户名与票据ID对应关系 */
private final Map<String, String> nameIdCache;
/**
* Instantiates a new default ticket registry.
*/
public DefaultTicketRegistry() {
this.cache = new ConcurrentHashMap<>();
this.nameIdCache = new ConcurrentHashMap<>();
}
/**
* Creates a new, empty registry with the specified initial capacity, load
* factor, and concurrency level.
*
* @param initialCapacity - the initial capacity. The implementation
* performs internal sizing to accommodate this many elements.
* @param loadFactor - the load factor threshold, used to control resizing.
* Resizing may be performed when the average number of elements per bin
* exceeds this threshold.
* @param concurrencyLevel - the estimated number of concurrently updating
* threads. The implementation performs internal sizing to try to
* accommodate this many threads.
*/
public DefaultTicketRegistry(final int initialCapacity, final float loadFactor, final int concurrencyLevel) {
this.cache = new ConcurrentHashMap<>(initialCapacity, loadFactor, concurrencyLevel);
this.nameIdCache = new ConcurrentHashMap<>();
}
/**
* {@inheritDoc}
* @throws IllegalArgumentException if the Ticket is null.
*/
@Override
public void addTicket(final Ticket ticket) {
Assert.notNull(ticket, "ticket cannot be null");
logger.debug("Added ticket [{}] to registry.", ticket.getId());
this.cache.put(ticket.getId(), ticket);
if(ticket instanceof TicketGrantingTicket){
String username = ((TicketGrantingTicket)ticket).getAuthentication().getPrincipal().toString().trim();
this.nameIdCache.put(username, ticket.getId());
}
}
@Override
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;
}
final String tid = this.nameIdCache.get(ticketId);
if(tid != null){
final Ticket ticketU = this.cache.get(tid);
if(ticketU != null) {
logger.debug("Ticket [{}] found in registry.", ticketId);
return ticketU;
}
}
return null;
}
@Override
public boolean deleteTicket(final String ticketId) {
if (ticketId == null) {
return false;
}
final Ticket ticket = getTicket(ticketId);
if (ticket == null) {
return false;
}
if (ticket instanceof TicketGrantingTicket) {
logger.debug("Removing children of ticket [{}] from the registry.", ticket);
deleteChildren((TicketGrantingTicket) ticket);
// 删除用户名与票据ID关联
String username = ((TicketGrantingTicket)ticket).getAuthentication().getPrincipal().toString().trim();
this.nameIdCache.remove(username);
}
logger.debug("Removing ticket [{}] from the registry.", ticket);
return (this.cache.remove(ticketId) != null);
}
/**
* Delete TGT's service tickets.
*
* @param ticket the ticket
*/
private void deleteChildren(final TicketGrantingTicket ticket) {
// delete service tickets
final Map<String, Service> services = ticket.getServices();
if (services != null && !services.isEmpty()) {
for (final Map.Entry<String, Service> entry : services.entrySet()) {
if (this.cache.remove(entry.getKey()) != null) {
logger.trace("Removed service ticket [{}]", entry.getKey());
} else {
logger.trace("Unable to remove service ticket [{}]", entry.getKey());
}
}
}
}
public Collection<Ticket> getTickets() {
return Collections.unmodifiableCollection(this.cache.values());
}
@Override
public int sessionCount() {
int count = 0;
for (final Ticket t : this.cache.values()) {
if (t instanceof TicketGrantingTicket) {
count++;
}
}
return count;
}
@Override
public int serviceTicketCount() {
int count = 0;
for (final Ticket t : this.cache.values()) {
if (t instanceof ServiceTicket) {
count++;
}
}
return count;
}
}
在下面这个类中主要就是重写这个方法createTicketGrantingTicket
org.jasig.cas.CentralAuthenticationServiceImpl
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;
import com.github.inspektr.audit.annotation.Audit;
import com.sinosoft.cas.action.LoginLogAction;
import com.sinosoft.cas.action.OnLineUser;
import com.sinosoft.cas.common.SpringContextUtils;
import com.sinosoft.cas.dao.LogManageDao;
import com.sinosoft.cas.po.LogManage;
import com.sinosoft.cas.po.query.LogManageQuery;
import org.apache.commons.lang.StringUtils;
import org.jasig.cas.authentication.AcceptAnyAuthenticationPolicyFactory;
import org.jasig.cas.authentication.Authentication;
import org.jasig.cas.authentication.AuthenticationBuilder;
import org.jasig.cas.authentication.AuthenticationException;
import org.jasig.cas.authentication.AuthenticationManager;
import org.jasig.cas.authentication.ContextualAuthenticationPolicy;
import org.jasig.cas.authentication.ContextualAuthenticationPolicyFactory;
import org.jasig.cas.authentication.MixedPrincipalException;
import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.principal.PersistentIdGenerator;
import org.jasig.cas.authentication.principal.Principal;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.authentication.principal.ShibbolethCompatiblePersistentIdGenerator;
import org.jasig.cas.authentication.principal.SimplePrincipal;
import org.jasig.cas.logout.LogoutManager;
import org.jasig.cas.logout.LogoutRequest;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.RegisteredServiceAttributeFilter;
import org.jasig.cas.services.ServiceContext;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.services.UnauthorizedProxyingException;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.jasig.cas.services.UnauthorizedSsoServiceException;
import org.jasig.cas.services.support.RegisteredServiceDefaultAttributeFilter;
import org.jasig.cas.ticket.ExpirationPolicy;
import org.jasig.cas.ticket.InvalidTicketException;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketException;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.TicketGrantingTicketImpl;
import org.jasig.cas.ticket.TicketValidationException;
import org.jasig.cas.ticket.UnsatisfiedAuthenticationPolicyException;
import org.jasig.cas.ticket.registry.TicketRegistry;
import org.jasig.cas.util.UniqueTicketIdGenerator;
import org.jasig.cas.validation.Assertion;
import org.jasig.cas.validation.ImmutableAssertion;
import org.perf4j.aop.Profiled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
/**
* Concrete implementation of a CentralAuthenticationService, and also the
* central, organizing component of CAS's internal implementation.
*
* This class is threadsafe.
*
* This class has the following properties that must be set:
*
* -
ticketRegistry
- The Ticket Registry to maintain the list
* of available tickets.
* -
serviceTicketRegistry
- Provides an alternative to configure separate registries for
* TGTs and ST in order to store them in different locations (i.e. long term memory or short-term)
* -
authenticationManager
- The service that will handle
* authentication.
* -
ticketGrantingTicketUniqueTicketIdGenerator
- Plug in to
* generate unique secure ids for TicketGrantingTickets.
* -
serviceTicketUniqueTicketIdGenerator
- Plug in to
* generate unique secure ids for ServiceTickets.
* -
ticketGrantingTicketExpirationPolicy
- The expiration
* policy for TicketGrantingTickets.
* -
serviceTicketExpirationPolicy
- The expiration policy for
* ServiceTickets.
*
*
* @author William G. Thompson, Jr.
* @author Scott Battaglia
* @author Dmitry Kopylenko
* @since 3.0
*/
public final class CentralAuthenticationServiceImpl implements CentralAuthenticationService {
/** Log instance for logging events, info, warnings, errors, etc. */
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/** TicketRegistry for storing and retrieving tickets as needed. */
@NotNull
private final TicketRegistry ticketRegistry;
/** New Ticket Registry for storing and retrieving services tickets. Can point to the same one as the ticketRegistry variable. */
@NotNull
private final TicketRegistry serviceTicketRegistry;
/**
* AuthenticationManager for authenticating credentials for purposes of
* obtaining tickets.
*/
@NotNull
private final AuthenticationManager authenticationManager;
/**
* UniqueTicketIdGenerator to generate ids for TicketGrantingTickets
* created.
*/
@NotNull
private final UniqueTicketIdGenerator ticketGrantingTicketUniqueTicketIdGenerator;
/** Map to contain the mappings of service->UniqueTicketIdGenerators. */
@NotNull
private final Map<String, UniqueTicketIdGenerator> uniqueTicketIdGeneratorsForService;
/** Implementation of Service Manager. */
@NotNull
private final ServicesManager servicesManager;
/** The logout manager. **/
@NotNull
private final LogoutManager logoutManager;
/** Expiration policy for ticket granting tickets. */
@NotNull
private ExpirationPolicy ticketGrantingTicketExpirationPolicy;
/** ExpirationPolicy for Service Tickets. */
@NotNull
private ExpirationPolicy serviceTicketExpirationPolicy;
/** Encoder to generate PseudoIds. */
@NotNull
private PersistentIdGenerator persistentIdGenerator = new ShibbolethCompatiblePersistentIdGenerator();
/** The default attribute filter to match principal attributes against that of a registered service. **/
private RegisteredServiceAttributeFilter defaultAttributeFilter = new RegisteredServiceDefaultAttributeFilter();
/**
* Authentication policy that uses a service context to produce stateful security policies to apply when
* authenticating credentials.
*/
@NotNull
private ContextualAuthenticationPolicyFactory<ServiceContext> serviceContextAuthenticationPolicyFactory =
new AcceptAnyAuthenticationPolicyFactory();
/**
* Build the central authentication service implementation.
*
* @param ticketRegistry the tickets registry.
* @param serviceTicketRegistry the service tickets registry.
* @param authenticationManager the authentication manager.
* @param ticketGrantingTicketUniqueTicketIdGenerator the TGT id generator.
* @param uniqueTicketIdGeneratorsForService the map with service and ticket id generators.
* @param ticketGrantingTicketExpirationPolicy the TGT expiration policy.
* @param serviceTicketExpirationPolicy the service ticket expiration policy.
* @param servicesManager the services manager.
* @param logoutManager the logout manager.
*/
public CentralAuthenticationServiceImpl(final TicketRegistry ticketRegistry,
final TicketRegistry serviceTicketRegistry,
final AuthenticationManager authenticationManager,
final UniqueTicketIdGenerator ticketGrantingTicketUniqueTicketIdGenerator,
final Map<String, UniqueTicketIdGenerator> uniqueTicketIdGeneratorsForService,
final ExpirationPolicy ticketGrantingTicketExpirationPolicy,
final ExpirationPolicy serviceTicketExpirationPolicy,
final ServicesManager servicesManager,
final LogoutManager logoutManager) {
this.ticketRegistry = ticketRegistry;
if (serviceTicketRegistry == null) {
this.serviceTicketRegistry = ticketRegistry;
} else {
this.serviceTicketRegistry = serviceTicketRegistry;
}
this.authenticationManager = authenticationManager;
this.ticketGrantingTicketUniqueTicketIdGenerator = ticketGrantingTicketUniqueTicketIdGenerator;
this.uniqueTicketIdGeneratorsForService = uniqueTicketIdGeneratorsForService;
this.ticketGrantingTicketExpirationPolicy = ticketGrantingTicketExpirationPolicy;
this.serviceTicketExpirationPolicy = serviceTicketExpirationPolicy;
this.servicesManager = servicesManager;
this.logoutManager = logoutManager;
}
/**
* {@inheritDoc}
* Destroy a TicketGrantingTicket and perform back channel logout. This has the effect of invalidating any
* Ticket that was derived from the TicketGrantingTicket being destroyed. May throw an
* {@link IllegalArgumentException} if the TicketGrantingTicket ID is null.
*
* @param ticketGrantingTicketId the id of the ticket we want to destroy
* @return the logout requests.
*/
@Audit(
action="TICKET_GRANTING_TICKET_DESTROYED",
actionResolverName="DESTROY_TICKET_GRANTING_TICKET_RESOLVER",
resourceResolverName="DESTROY_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
@Profiled(tag = "DESTROY_TICKET_GRANTING_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
@Override
public List<LogoutRequest> destroyTicketGrantingTicket(final String ticketGrantingTicketId) {
Assert.notNull(ticketGrantingTicketId);
logger.debug("Removing ticket [{}] from registry.", ticketGrantingTicketId);
final TicketGrantingTicket ticket = this.ticketRegistry.getTicket(ticketGrantingTicketId,
TicketGrantingTicket.class);
if (ticket == null) {
logger.debug("TicketGrantingTicket [{}] cannot be found in the ticket registry.", ticketGrantingTicketId);
return Collections.emptyList();
}
logger.debug("Ticket found. Processing logout requests and then deleting the ticket...");
final List<LogoutRequest> logoutRequests = logoutManager.performLogout(ticket);
this.ticketRegistry.deleteTicket(ticketGrantingTicketId);
return logoutRequests;
}
/**
* @throws IllegalArgumentException if ticketGrantingTicketId or service are null.
*/
@Audit(
action="SERVICE_TICKET",
actionResolverName="GRANT_SERVICE_TICKET_RESOLVER",
resourceResolverName="GRANT_SERVICE_TICKET_RESOURCE_RESOLVER")
@Profiled(tag="GRANT_SERVICE_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public String grantServiceTicket(
final String ticketGrantingTicketId, final Service service, final Credential... credentials)
throws AuthenticationException, TicketException {
Assert.notNull(ticketGrantingTicketId, "ticketGrantingticketId cannot be null");
Assert.notNull(service, "service cannot be null");
final TicketGrantingTicket ticketGrantingTicket = this.ticketRegistry.getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);
if (ticketGrantingTicket == null) {
logger.debug("TicketGrantingTicket [{}] cannot be found in the ticket registry.", ticketGrantingTicketId);
throw new InvalidTicketException(ticketGrantingTicketId);
}
synchronized (ticketGrantingTicket) {
if (ticketGrantingTicket.isExpired()) {
this.ticketRegistry.deleteTicket(ticketGrantingTicketId);
logger.debug("TicketGrantingTicket[{}] has expired and is now deleted from the ticket registry.", ticketGrantingTicketId);
throw new InvalidTicketException(ticketGrantingTicketId);
}
}
final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
verifyRegisteredServiceProperties(registeredService, service);
if (!registeredService.isSsoEnabled() && credentials == null
&& ticketGrantingTicket.getCountOfUses() > 0) {
logger.warn("ServiceManagement: Service [{}] is not allowed to use SSO.", service.getId());
throw new UnauthorizedSsoServiceException();
}
//CAS-1019
final List<Authentication> authns = ticketGrantingTicket.getChainedAuthentications();
if(authns.size() > 1) {
if (!registeredService.isAllowedToProxy()) {
final String message = String.
format("ServiceManagement: Proxy attempt by service [%s] (registered service [%s]) is not allowed.",
service.getId(), registeredService.toString());
logger.warn(message);
throw new UnauthorizedProxyingException(message);
}
}
if (credentials != null) {
final Authentication current = this.authenticationManager.authenticate(credentials);
final Authentication original = ticketGrantingTicket.getAuthentication();
if (!current.getPrincipal().equals(original.getPrincipal())) {
throw new MixedPrincipalException(current, current.getPrincipal(), original.getPrincipal());
}
ticketGrantingTicket.getSupplementalAuthentications().add(current);
}
// Perform security policy check by getting the authentication that satisfies the configured policy
// This throws if no suitable policy is found
getAuthenticationSatisfiedByPolicy(ticketGrantingTicket, new ServiceContext(service, registeredService));
final String uniqueTicketIdGenKey = service.getClass().getName();
if (!this.uniqueTicketIdGeneratorsForService.containsKey(uniqueTicketIdGenKey)) {
logger.warn("Cannot create service ticket because the key [{}] for service [{}] is not linked to a ticket id generator",
uniqueTicketIdGenKey, service.getId());
throw new UnauthorizedSsoServiceException();
}
final UniqueTicketIdGenerator serviceTicketUniqueTicketIdGenerator =
this.uniqueTicketIdGeneratorsForService.get(uniqueTicketIdGenKey);
final String generatedServiceTicketId = serviceTicketUniqueTicketIdGenerator.getNewTicketId(ServiceTicket.PREFIX);
logger.debug("Generated service ticket id [{}] for ticket granting ticket [{}]",
generatedServiceTicketId, ticketGrantingTicket.getId());
final ServiceTicket serviceTicket = ticketGrantingTicket.grantServiceTicket(generatedServiceTicketId, service,
this.serviceTicketExpirationPolicy, credentials != null);
this.serviceTicketRegistry.addTicket(serviceTicket);
if (logger.isInfoEnabled()) {
final List<Authentication> authentications = serviceTicket.getGrantingTicket().getChainedAuthentications();
final String formatString = "Granted %s ticket [%s] for service [%s] for user [%s]";
final String type;
final String principalId = authentications.get(authentications.size() - 1).getPrincipal().getId();
if (authentications.size() == 1) {
type = "service";
} else {
type = "proxy";
}
logger.info(String.format(formatString, type, serviceTicket.getId(), service.getId(), principalId));
}
return serviceTicket.getId();
}
@Audit(
action="SERVICE_TICKET",
actionResolverName="GRANT_SERVICE_TICKET_RESOLVER",
resourceResolverName="GRANT_SERVICE_TICKET_RESOURCE_RESOLVER")
@Profiled(tag = "GRANT_SERVICE_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public String grantServiceTicket(final String ticketGrantingTicketId,
final Service service) throws TicketException {
try {
return this.grantServiceTicket(ticketGrantingTicketId, service, null);
} catch (final AuthenticationException e) {
throw new IllegalStateException("Unexpected authentication exception", e);
}
}
/**
* @throws IllegalArgumentException if the ServiceTicketId or the
* Credential are null.
*/
@Audit(
action="PROXY_GRANTING_TICKET",
actionResolverName="GRANT_PROXY_GRANTING_TICKET_RESOLVER",
resourceResolverName="GRANT_PROXY_GRANTING_TICKET_RESOURCE_RESOLVER")
@Profiled(tag="GRANT_PROXY_GRANTING_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public String delegateTicketGrantingTicket(final String serviceTicketId, final Credential... credentials)
throws AuthenticationException, TicketException {
Assert.notNull(serviceTicketId, "serviceTicketId cannot be null");
Assert.notNull(credentials, "credentials cannot be null");
final ServiceTicket serviceTicket = this.serviceTicketRegistry.getTicket(serviceTicketId, ServiceTicket.class);
if (serviceTicket == null || serviceTicket.isExpired()) {
logger.debug("ServiceTicket [{}] has expired or cannot be found in the ticket registry", serviceTicketId);
throw new InvalidTicketException(serviceTicketId);
}
final RegisteredService registeredService = this.servicesManager
.findServiceBy(serviceTicket.getService());
verifyRegisteredServiceProperties(registeredService, serviceTicket.getService());
if (!registeredService.isAllowedToProxy()) {
logger.warn("ServiceManagement: Service [{}] attempted to proxy, but is not allowed.", serviceTicket.getService().getId());
throw new UnauthorizedProxyingException();
}
final Authentication authentication = this.authenticationManager.authenticate(credentials);
final TicketGrantingTicket ticketGrantingTicket = serviceTicket
.grantTicketGrantingTicket(
this.ticketGrantingTicketUniqueTicketIdGenerator
.getNewTicketId(TicketGrantingTicket.PREFIX),
authentication, this.ticketGrantingTicketExpirationPolicy);
this.ticketRegistry.addTicket(ticketGrantingTicket);
return ticketGrantingTicket.getId();
}
/**
* @throws IllegalArgumentException if the ServiceTicketId or the Service
* are null.
*/
@Audit(
action="SERVICE_TICKET_VALIDATE",
actionResolverName="VALIDATE_SERVICE_TICKET_RESOLVER",
resourceResolverName="VALIDATE_SERVICE_TICKET_RESOURCE_RESOLVER")
@Profiled(tag="VALIDATE_SERVICE_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public Assertion validateServiceTicket(final String serviceTicketId, final Service service) throws TicketException {
Assert.notNull(serviceTicketId, "serviceTicketId cannot be null");
Assert.notNull(service, "service cannot be null");
final ServiceTicket serviceTicket = this.serviceTicketRegistry.getTicket(serviceTicketId, ServiceTicket.class);
if (serviceTicket == null) {
logger.info("ServiceTicket [{}] does not exist.", serviceTicketId);
throw new InvalidTicketException(serviceTicketId);
}
final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
verifyRegisteredServiceProperties(registeredService, serviceTicket.getService());
try {
synchronized (serviceTicket) {
if (serviceTicket.isExpired()) {
logger.info("ServiceTicket [{}] has expired.", serviceTicketId);
throw new InvalidTicketException(serviceTicketId);
}
if (!serviceTicket.isValidFor(service)) {
logger.error("ServiceTicket [{}] with service [{}] does not match supplied service [{}]",
serviceTicketId, serviceTicket.getService().getId(), service);
throw new TicketValidationException(serviceTicket.getService());
}
}
final TicketGrantingTicket root = serviceTicket.getGrantingTicket().getRoot();
final Authentication authentication = getAuthenticationSatisfiedByPolicy(
root, new ServiceContext(serviceTicket.getService(), registeredService));
final Principal principal = authentication.getPrincipal();
Map<String, Object> attributesToRelease = this.defaultAttributeFilter.filter(principal.getId(),
principal.getAttributes(), registeredService);
if (registeredService.getAttributeFilter() != null) {
attributesToRelease = registeredService.getAttributeFilter().filter(principal.getId(),
attributesToRelease, registeredService);
}
final String principalId = determinePrincipalIdForRegisteredService(principal, registeredService, serviceTicket);
final Principal modifiedPrincipal = new SimplePrincipal(principalId, attributesToRelease);
final AuthenticationBuilder builder = AuthenticationBuilder.newInstance(authentication);
builder.setPrincipal(modifiedPrincipal);
return new ImmutableAssertion(
builder.build(),
serviceTicket.getGrantingTicket().getChainedAuthentications(),
serviceTicket.getService(),
serviceTicket.isFromNewLogin());
} finally {
if (serviceTicket.isExpired()) {
this.serviceTicketRegistry.deleteTicket(serviceTicketId);
}
}
}
/**
* Determines the principal id to use for a {@link RegisteredService} using the following rules:
*
*
* - If the service is marked to allow anonymous access, a persistent id is returned.
* - If the {@link org.jasig.cas.services.RegisteredService#getUsernameAttribute()} is blank, then the default
* principal id is returned.
* - If the username attribute is available as part of the principal's attributes,
* the corresponding attribute value will be returned.
*
* - Otherwise, the default principal's id is returned as the username attribute
* with an additional warning.
*
*
* @param principal The principal object to be validated and constructed
* @param registeredService Requesting service for which a principal is being validated.
* @param serviceTicket An instance of the service ticket used for validation
*
* @return The principal id to use for the requesting registered service
*/
private String determinePrincipalIdForRegisteredService(final Principal principal,
final RegisteredService registeredService,
final ServiceTicket serviceTicket) {
String principalId = null;
final String serviceUsernameAttribute = registeredService.getUsernameAttribute();
if (registeredService.isAnonymousAccess()) {
principalId = this.persistentIdGenerator.generate(principal, serviceTicket.getService());
} else if (StringUtils.isBlank(serviceUsernameAttribute)) {
principalId = principal.getId();
} else {
if (principal.getAttributes().containsKey(serviceUsernameAttribute)) {
principalId = principal.getAttributes().get(serviceUsernameAttribute).toString();
} else {
principalId = principal.getId();
final Object[] errorLogParameters = new Object[] {
principalId,
registeredService.getUsernameAttribute(),
principal.getAttributes(),
registeredService.getServiceId(),
principalId };
logger.warn("Principal [{}] did not have attribute [{}] among attributes [{}] so CAS cannot "
+ "provide on the validation response the user attribute the registered service [{}] expects. "
+ "CAS will instead return the default username attribute [{}]", errorLogParameters);
}
}
logger.debug("Principal id to return for service [{}] is [{}]. The default principal id is [{}].",
new Object[]{
registeredService.getName(), principal.getId(), principalId});
return principalId;
}
/**
* @throws IllegalArgumentException if the credentials are null.
* 这个类中主要就是重写这个方法,其他的不动
*/
@Audit(
action="TICKET_GRANTING_TICKET",
actionResolverName="CREATE_TICKET_GRANTING_TICKET_RESOLVER",
resourceResolverName="CREATE_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
@Profiled(tag = "CREATE_TICKET_GRANTING_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public String createTicketGrantingTicket(final Credential... credentials)
throws AuthenticationException, TicketException {
Assert.notNull(credentials, "credentials cannot be null");
final Authentication authentication = this.authenticationManager.authenticate(credentials);
final TicketGrantingTicket ticketGrantingTicket = new TicketGrantingTicketImpl(
this.ticketGrantingTicketUniqueTicketIdGenerator
.getNewTicketId(TicketGrantingTicket.PREFIX),
authentication, this.ticketGrantingTicketExpirationPolicy);
//增加单用户登录限制
//找出用户id,并且不为当前tgt的,这里应当考虑数据性能,直接筛选用户再筛选tgt
String id=ticketGrantingTicket.getAuthentication().getPrincipal().getId();
String tgt=ticketGrantingTicket.getId();
Collection<Ticket> tickets = this.ticketRegistry.getTickets();
//进行循环比对tgt,筛选并注销当前username的非当前用户的TGT
for (Ticket ticket : tickets) {
if(ticket instanceof TicketGrantingTicket){
TicketGrantingTicket t = ((TicketGrantingTicket)ticket).getRoot();
Authentication authentications=t.getAuthentication();
if( t != null && authentication != null
&& authentication.getPrincipal() != null && id.equals(authentications.getPrincipal().getId())
&& !tgt.equals(t.getId())){
this.destroyTicketGrantingTicket(ticket.getId());
}
}
}
//在使用过程中发现,因为jar包依赖版本问题,可能导致该方法无法获取到ticket,如果获取不到ticket,可以使用上面的方法进行获取并注销ticket
/* Ticket ticket = this.ticketRegistry.getTicket(credentials[0].getId());
if(ticket != null){
this.destroyTicketGrantingTicket(ticket.getId());
}*/
// 增加当用户登录限制↑
this.ticketRegistry.addTicket(ticketGrantingTicket);
return ticketGrantingTicket.getId();
}
public void setPersistentIdGenerator(
final PersistentIdGenerator persistentIdGenerator) {
this.persistentIdGenerator = persistentIdGenerator;
}
public void setServiceContextAuthenticationPolicyFactory(final ContextualAuthenticationPolicyFactory<ServiceContext> policy) {
this.serviceContextAuthenticationPolicyFactory = policy;
}
/**
* @param ticketGrantingTicketExpirationPolicy a TGT expiration policy.
*/
public void setTicketGrantingTicketExpirationPolicy(final ExpirationPolicy ticketGrantingTicketExpirationPolicy) {
this.ticketGrantingTicketExpirationPolicy = ticketGrantingTicketExpirationPolicy;
}
/**
* @param serviceTicketExpirationPolicy a ST expiration policy.
*/
public void setServiceTicketExpirationPolicy(final ExpirationPolicy serviceTicketExpirationPolicy) {
this.serviceTicketExpirationPolicy = serviceTicketExpirationPolicy;
}
private Authentication getAuthenticationSatisfiedByPolicy(
final TicketGrantingTicket ticket, final ServiceContext context) throws TicketException {
final ContextualAuthenticationPolicy<ServiceContext> policy =
serviceContextAuthenticationPolicyFactory.createPolicy(context);
if (policy.isSatisfiedBy(ticket.getAuthentication())) {
return ticket.getAuthentication();
}
for (final Authentication auth : ticket.getSupplementalAuthentications()) {
if (policy.isSatisfiedBy(auth)) {
return auth;
}
}
throw new UnsatisfiedAuthenticationPolicyException(policy);
}
/**
* Ensure that the service is found and enabled in the service registry.
* @param registeredService the located entry in the registry
* @param service authenticating service
* @throws UnauthorizedServiceException
*/
private void verifyRegisteredServiceProperties(final RegisteredService registeredService, final Service service) {
if (registeredService == null) {
final String msg = String.format("ServiceManagement: Unauthorized Service Access. "
+ "Service [%s] is not found in service registry.", service.getId());
logger.warn(msg);
throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
}
if (!registeredService.isEnabled()) {
final String msg = String.format("ServiceManagement: Unauthorized Service Access. "
+ "Service %s] is not enabled in service registry.", service.getId());
logger.warn(msg);
throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
}
}
}
这样,在新的TGT被加入缓存之前先进行一次验证,注销掉同一用户已有的TGT。由于CAS本身就对TGT和ST做了关联,所以TGT注销后通过它生成的ST会统统自动注销,完成对上一个用户的踢出。
本文档只是对 CAS4.0.0 版本的 createTicketGrantingTicket 这个方法进行一个展现,其他的参考的下面的两片文章,如下面两篇文章的博主感觉有侵权什么的,请联系我,我会将本文章进行删除:
原文1:https://blog.csdn.net/qq_34125349/article/details/80272826
原文2:https://blog.csdn.net/tian3559060/article/details/80166877