博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CAS 单点登出 loginout 解决方案 -- 最靠谱的方案,不是抄的--还是不靠谱大家不要抄了
阅读量:4073 次
发布时间:2019-05-25

本文共 22671 字,大约阅读时间需要 75 分钟。

mmp,从11年开始用cas,但是总是在退出的时候掉链子,各种掉线子 从2.x版本开始用现在都4.2.7版本都没解决这个退出掉链子的事情,于是自己看源码解决了此问题。

    cas 默认的基于 httpclient http 通知的,通知的时候,服务端给客户端发一个xml 里面有一个ticketid,客户端用ticketid做退出。实记使用过程中,你会碰到各种退不出来的问题,关闭浏览器在重试,又TMD好了,为了解决这个问题,我使用mq做通知,不用httpclient做通知了。

方案:

        重写服务端注销session的部分代码。

   

package org.jasig.cas;import com.alibaba.fastjson.JSONObject;import com.codahale.metrics.annotation.Counted;import com.codahale.metrics.annotation.Metered;import com.codahale.metrics.annotation.Timed;import org.jasig.cas.authentication.Authentication;import org.jasig.cas.authentication.AuthenticationBuilder;import org.jasig.cas.authentication.AuthenticationContext;import org.jasig.cas.authentication.AuthenticationException;import org.jasig.cas.authentication.DefaultAuthenticationBuilder;import org.jasig.cas.authentication.MixedPrincipalException;import org.jasig.cas.authentication.principal.Principal;import org.jasig.cas.authentication.principal.Service;import org.jasig.cas.logout.LogoutManager;import org.jasig.cas.logout.LogoutRequest;import org.jasig.cas.services.RegisteredService;import org.jasig.cas.services.RegisteredServiceAttributeReleasePolicy;import org.jasig.cas.services.ServiceContext;import org.jasig.cas.services.ServicesManager;import org.jasig.cas.services.UnauthorizedProxyingException;import org.jasig.cas.services.UnauthorizedServiceForPrincipalException;import org.jasig.cas.services.UnauthorizedSsoServiceException;import org.jasig.cas.support.events.CasProxyGrantingTicketCreatedEvent;import org.jasig.cas.support.events.CasProxyTicketGrantedEvent;import org.jasig.cas.support.events.CasServiceTicketGrantedEvent;import org.jasig.cas.support.events.CasServiceTicketValidatedEvent;import org.jasig.cas.support.events.CasTicketGrantingTicketCreatedEvent;import org.jasig.cas.support.events.CasTicketGrantingTicketDestroyedEvent;import org.jasig.cas.ticket.AbstractTicketException;import org.jasig.cas.ticket.InvalidTicketException;import org.jasig.cas.ticket.ServiceTicket;import org.jasig.cas.ticket.ServiceTicketFactory;import org.jasig.cas.ticket.TicketFactory;import org.jasig.cas.ticket.TicketGrantingTicket;import org.jasig.cas.ticket.TicketGrantingTicketFactory;import org.jasig.cas.ticket.UnrecognizableServiceForServiceTicketValidationException;import org.jasig.cas.ticket.proxy.ProxyGrantingTicket;import org.jasig.cas.ticket.proxy.ProxyGrantingTicketFactory;import org.jasig.cas.ticket.proxy.ProxyTicket;import org.jasig.cas.ticket.proxy.ProxyTicketFactory;import org.jasig.cas.ticket.registry.TicketRegistry;import org.jasig.cas.validation.Assertion;import org.jasig.cas.validation.ImmutableAssertion;import org.jasig.inspektr.audit.annotation.Audit;import org.jose4j.json.internal.json_simple.JSONArray;import org.springframework.amqp.core.AmqpTemplate;import org.springframework.amqp.rabbit.core.RabbitTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.transaction.annotation.Transactional;import javax.annotation.PostConstruct;import javax.annotation.Resource;import javax.validation.constraints.NotNull;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;/** * Concrete implementation of a {@link CentralAuthenticationService}, and also the * central, organizing component of CAS's internal implementation. * This class is threadsafe. * * @author William G. Thompson, Jr. * @author Scott Battaglia * @author Dmitry Kopylenko * @author Misagh Moayyed * @since 3.0.0 */@Component("centralAuthenticationService")@Transactional(readOnly = false, transactionManager = "ticketTransactionManager")public class CentralAuthenticationServiceImpl extends AbstractCentralAuthenticationService {    private static final long serialVersionUID = -8943828074939533986L;    /** 广播交换机名称 */    private final static String EXCHANGE_NAME = "session-destroy-notify";    @Autowired    private RabbitTemplate template;    /**     * Instantiates a new Central authentication service impl.     */    public CentralAuthenticationServiceImpl() {        super();    }    /**     * Build the central authentication service implementation.     *     * @param ticketRegistry  the tickets registry.     * @param ticketFactory   the ticket factory     * @param servicesManager the services manager.     * @param logoutManager   the logout manager.     */    public CentralAuthenticationServiceImpl(            final TicketRegistry ticketRegistry,            final TicketFactory ticketFactory,            final ServicesManager servicesManager,            final LogoutManager logoutManager) {        super(ticketRegistry, ticketFactory, servicesManager, 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")    @Timed(name = "DESTROY_TICKET_GRANTING_TICKET_TIMER")    @Metered(name = "DESTROY_TICKET_GRANTING_TICKET_METER")    @Counted(name = "DESTROY_TICKET_GRANTING_TICKET_COUNTER", monotonic = true)    @Override    public List
destroyTicketGrantingTicket(@NotNull final String ticketGrantingTicketId) { try { logger.debug("Removing ticket [{}] from registry...", ticketGrantingTicketId); final TicketGrantingTicket ticket = getTicket(ticketGrantingTicketId, TicketGrantingTicket.class); logger.debug("Ticket found. Processing logout requests and then deleting the ticket..."); final List
logoutRequests = logoutManager.performLogout(ticket); this.ticketRegistry.deleteTicket(ticketGrantingTicketId); /*------------start-------TODO-------增加向消息队列中发送ticketId--------------------------*/ JSONObject jsonObject = new JSONObject(); JSONArray ticketIds = new JSONArray(); jsonObject.put("ticketIds",ticketIds); for (LogoutRequest logoutRequest : logoutRequests) { ticketIds.add(logoutRequest.getTicketId()); } template.convertAndSend(jsonObject.toJSONString()); /*-------------end--------TODO-------增加向消息队列中发送ticketId--------------------------*/ doPublishEvent(new CasTicketGrantingTicketDestroyedEvent(this, ticket)); return logoutRequests; } catch (final InvalidTicketException e) { logger.debug("TicketGrantingTicket [{}] cannot be found in the ticket registry.", ticketGrantingTicketId); } return Collections.emptyList(); } @Audit( action = "SERVICE_TICKET", actionResolverName = "GRANT_SERVICE_TICKET_RESOLVER", resourceResolverName = "GRANT_SERVICE_TICKET_RESOURCE_RESOLVER") @Timed(name = "GRANT_SERVICE_TICKET_TIMER") @Metered(name = "GRANT_SERVICE_TICKET_METER") @Counted(name = "GRANT_SERVICE_TICKET_COUNTER", monotonic = true) @Override public ServiceTicket grantServiceTicket( final String ticketGrantingTicketId, final Service service, final AuthenticationContext context) throws AuthenticationException, AbstractTicketException { logger.debug("Attempting to get ticket id {} to create service ticket", ticketGrantingTicketId); final TicketGrantingTicket ticketGrantingTicket = getTicket(ticketGrantingTicketId, TicketGrantingTicket.class); final RegisteredService registeredService = this.servicesManager.findServiceBy(service); verifyRegisteredServiceProperties(registeredService, service); evaluatePossibilityOfMixedPrincipals(context, ticketGrantingTicket); if (ticketGrantingTicket.getCountOfUses() > 0 && !registeredService.getAccessStrategy().isServiceAccessAllowedForSso()) { logger.warn("Service [{}] is not allowed to use SSO.", service.getId()); throw new UnauthorizedSsoServiceException(); } evaluateProxiedServiceIfNeeded(service, ticketGrantingTicket, registeredService); // Perform security policy check by getting the authentication that satisfies the configured policy // This throws if no suitable policy is found logger.debug("Checking for authentication policy satisfaction..."); getAuthenticationSatisfiedByPolicy(ticketGrantingTicket.getRoot(), new ServiceContext(service, registeredService)); final List
authentications = ticketGrantingTicket.getChainedAuthentications(); final Principal principal = authentications.get(authentications.size() - 1).getPrincipal(); logger.debug("Located principal {} for service ticket creation", principal); final RegisteredServiceAttributeReleasePolicy releasePolicy = registeredService.getAttributeReleasePolicy(); final Map
principalAttrs; if (releasePolicy != null) { principalAttrs = releasePolicy.getAttributes(principal); } else { principalAttrs = new HashMap<>(); } if (!registeredService.getAccessStrategy().doPrincipalAttributesAllowServiceAccess(principal.getId(), principalAttrs)) { logger.warn("Cannot grant service ticket because Service [{}] is not authorized for use by [{}].", service.getId(), principal); throw new UnauthorizedServiceForPrincipalException(); } final ServiceTicketFactory factory = this.ticketFactory.get(ServiceTicket.class); final ServiceTicket serviceTicket = factory.create(ticketGrantingTicket, service, context != null && context.isCredentialProvided()); logger.info("Granted ticket [{}] for service [{}] and principal [{}]", serviceTicket.getId(), service.getId(), principal.getId()); this.ticketRegistry.addTicket(serviceTicket); logger.debug("Added service ticket {} to ticket registry", serviceTicket.getId()); doPublishEvent(new CasServiceTicketGrantedEvent(this, ticketGrantingTicket, serviceTicket)); return serviceTicket; } /** * Always keep track of a single authentication object, * as opposed to keeping a history of all. This helps with * memory consumption. Note that supplemental authentications * are to be removed. * * @param context authentication context * @param ticketGrantingTicket the tgt * @return the processed authentication in the current context * @throws MixedPrincipalException in case there is a principal mismatch between TGT and the current authN. */ private Authentication evaluatePossibilityOfMixedPrincipals(final AuthenticationContext context, final TicketGrantingTicket ticketGrantingTicket) throws MixedPrincipalException { Authentication currentAuthentication = null; if (context != null) { currentAuthentication = context.getAuthentication(); if (currentAuthentication != null) { final Authentication original = ticketGrantingTicket.getAuthentication(); if (!currentAuthentication.getPrincipal().equals(original.getPrincipal())) { logger.debug("Principal associated with current authentication {} does not match " + " the principal {} associated with the original authentication", currentAuthentication.getPrincipal(), original.getPrincipal()); throw new MixedPrincipalException( currentAuthentication, currentAuthentication.getPrincipal(), original.getPrincipal()); } ticketGrantingTicket.getSupplementalAuthentications().clear(); ticketGrantingTicket.getSupplementalAuthentications().add(currentAuthentication); logger.debug("Added authentication to the collection of supplemental authentications"); } } return currentAuthentication; } @Audit( action = "PROXY_TICKET", actionResolverName = "GRANT_PROXY_TICKET_RESOLVER", resourceResolverName = "GRANT_PROXY_TICKET_RESOURCE_RESOLVER") @Timed(name = "GRANT_PROXY_TICKET_TIMER") @Metered(name = "GRANT_PROXY_TICKET_METER") @Counted(name = "GRANT_PROXY_TICKET_COUNTER", monotonic = true) @Override public ProxyTicket grantProxyTicket(final String proxyGrantingTicket, final Service service) throws AbstractTicketException { final ProxyGrantingTicket proxyGrantingTicketObject = getTicket(proxyGrantingTicket, ProxyGrantingTicket.class); final RegisteredService registeredService = this.servicesManager.findServiceBy(service); verifyRegisteredServiceProperties(registeredService, service); if (!registeredService.getAccessStrategy().isServiceAccessAllowedForSso()) { logger.warn("Service [{}] is not allowed to use SSO.", service.getId()); throw new UnauthorizedSsoServiceException(); } evaluateProxiedServiceIfNeeded(service, proxyGrantingTicketObject, registeredService); // Perform security policy check by getting the authentication that satisfies the configured policy // This throws if no suitable policy is found getAuthenticationSatisfiedByPolicy(proxyGrantingTicketObject.getRoot(), new ServiceContext(service, registeredService)); final List
authentications = proxyGrantingTicketObject.getChainedAuthentications(); final Principal principal = authentications.get(authentications.size() - 1).getPrincipal(); final RegisteredServiceAttributeReleasePolicy releasePolicy = registeredService.getAttributeReleasePolicy(); final Map
principalAttrs; if (releasePolicy != null) { principalAttrs = releasePolicy.getAttributes(principal); } else { principalAttrs = new HashMap<>(); } if (!registeredService.getAccessStrategy().doPrincipalAttributesAllowServiceAccess(principal.getId(), principalAttrs)) { logger.warn("Cannot grant proxy ticket because Service [{}] is not authorized for use by [{}].", service.getId(), principal); throw new UnauthorizedServiceForPrincipalException(); } final ProxyTicketFactory factory = this.ticketFactory.get(ProxyTicket.class); final ProxyTicket proxyTicket = factory.create(proxyGrantingTicketObject, service); this.ticketRegistry.addTicket(proxyTicket); logger.info("Granted ticket [{}] for service [{}] for user [{}]", proxyTicket.getId(), service.getId(), principal.getId()); doPublishEvent(new CasProxyTicketGrantedEvent(this, proxyGrantingTicketObject, proxyTicket)); return proxyTicket; } @Audit( action = "PROXY_GRANTING_TICKET", actionResolverName = "CREATE_PROXY_GRANTING_TICKET_RESOLVER", resourceResolverName = "CREATE_PROXY_GRANTING_TICKET_RESOURCE_RESOLVER") @Timed(name = "CREATE_PROXY_GRANTING_TICKET_TIMER") @Metered(name = "CREATE_PROXY_GRANTING_TICKET_METER") @Counted(name = "CREATE_PROXY_GRANTING_TICKET_COUNTER", monotonic = true) @Override public ProxyGrantingTicket createProxyGrantingTicket(final String serviceTicketId, final AuthenticationContext context) throws AuthenticationException, AbstractTicketException { final ServiceTicket serviceTicket = this.ticketRegistry.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.getProxyPolicy().isAllowedToProxy()) { logger.warn("ServiceManagement: Service [{}] attempted to proxy, but is not allowed.", serviceTicket.getService().getId()); throw new UnauthorizedProxyingException(); } final Authentication authentication = context.getAuthentication(); final ProxyGrantingTicketFactory factory = this.ticketFactory.get(ProxyGrantingTicket.class); final ProxyGrantingTicket proxyGrantingTicket = factory.create(serviceTicket, authentication); logger.debug("Generated proxy granting ticket [{}] based off of [{}]", proxyGrantingTicket, serviceTicketId); this.ticketRegistry.addTicket(proxyGrantingTicket); doPublishEvent(new CasProxyGrantingTicketCreatedEvent(this, proxyGrantingTicket)); return proxyGrantingTicket; } @Audit( action = "SERVICE_TICKET_VALIDATE", actionResolverName = "VALIDATE_SERVICE_TICKET_RESOLVER", resourceResolverName = "VALIDATE_SERVICE_TICKET_RESOURCE_RESOLVER") @Timed(name = "VALIDATE_SERVICE_TICKET_TIMER") @Metered(name = "VALIDATE_SERVICE_TICKET_METER") @Counted(name = "VALIDATE_SERVICE_TICKET_COUNTER", monotonic = true) @Override public Assertion validateServiceTicket(final String serviceTicketId, final Service service) throws AbstractTicketException { final RegisteredService registeredService = this.servicesManager.findServiceBy(service); verifyRegisteredServiceProperties(registeredService, service); final ServiceTicket serviceTicket = this.ticketRegistry.getTicket(serviceTicketId, ServiceTicket.class); if (serviceTicket == null) { logger.info("Service ticket [{}] does not exist.", serviceTicketId); throw new InvalidTicketException(serviceTicketId); } try { synchronized (serviceTicket) { if (serviceTicket.isExpired()) { logger.info("ServiceTicket [{}] has expired.", serviceTicketId); throw new InvalidTicketException(serviceTicketId); } if (!serviceTicket.isValidFor(service)) { logger.error("Service ticket [{}] with service [{}] does not match supplied service [{}]", serviceTicketId, serviceTicket.getService().getId(), service); throw new UnrecognizableServiceForServiceTicketValidationException(serviceTicket.getService()); } } final TicketGrantingTicket root = serviceTicket.getGrantingTicket().getRoot(); final Authentication authentication = getAuthenticationSatisfiedByPolicy( root, new ServiceContext(serviceTicket.getService(), registeredService)); final Principal principal = authentication.getPrincipal(); final RegisteredServiceAttributeReleasePolicy attributePolicy = registeredService.getAttributeReleasePolicy(); logger.debug("Attribute policy [{}] is associated with service [{}]", attributePolicy, registeredService); @SuppressWarnings("unchecked") final Map
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()); doPublishEvent(new CasServiceTicketValidatedEvent(this, serviceTicket, assertion)); return assertion; } finally { if (serviceTicket.isExpired()) { this.ticketRegistry.deleteTicket(serviceTicketId); } } } @Audit( action = "TICKET_GRANTING_TICKET", actionResolverName = "CREATE_TICKET_GRANTING_TICKET_RESOLVER", resourceResolverName = "CREATE_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER") @Timed(name = "CREATE_TICKET_GRANTING_TICKET_TIMER") @Metered(name = "CREATE_TICKET_GRANTING_TICKET_METER") @Counted(name = "CREATE_TICKET_GRANTING_TICKET_COUNTER", monotonic = true) @Override public TicketGrantingTicket createTicketGrantingTicket(final AuthenticationContext context) throws AuthenticationException, AbstractTicketException { final Authentication authentication = context.getAuthentication(); final TicketGrantingTicketFactory factory = this.ticketFactory.get(TicketGrantingTicket.class); final TicketGrantingTicket ticketGrantingTicket = factory.create(authentication); this.ticketRegistry.addTicket(ticketGrantingTicket); doPublishEvent(new CasTicketGrantingTicketCreatedEvent(this, ticketGrantingTicket)); return ticketGrantingTicket; } /** * 测试rabbitMq */ /*@PostConstruct public void aaa(){ for (int i = 0; i < 10; i++) { JSONObject ticketObj = new JSONObject(); ticketObj.put("ticketId",i); template.convertAndSend(ticketObj.toJSONString()); } }*/}r

    

    然后重写客户管的销毁部分代码。

package com.yzb.mq;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import org.jasig.cas.client.session.SingleSignOutHandler;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import javax.servlet.http.HttpSession;/** * @author xiaoh * @version [版本号, 2018/4/26 20:18] * @Description: * @versio 1.0 * 西安优智泊物联网技术服务有限公司 * Copyright (c) 2017 All Rights Reserved. */@Componentpublic class CasQueue implements InitializingBean {    @Autowired    private MqRadioService mqRadioService;    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());    private SingleSignOutHandler singleSignOutHandler;    public void onAccessTokenDes(JSONObject message) {        JSONArray ticketIds = message.getJSONArray("ticketIds");        for (Object ticketId : ticketIds) {            final HttpSession session = singleSignOutHandler.getSessionMappingStorage().removeSessionByMappingId((String) ticketId);            if (session != null) {                final String sessionID = session.getId();                logger.debug("Invalidating session [{}] for token [{}]", sessionID, ticketId);                try {                    session.invalidate();                } catch (final IllegalStateException e) {                    logger.debug("Error invalidating session.", e);                }            }        }    }    public void setSingleSignOutHandler(SingleSignOutHandler singleSignOutHandler) {        this.singleSignOutHandler = singleSignOutHandler;    }    @Override    public void afterPropertiesSet() throws Exception {        mqRadioService.registerRadioListener(this::onAccessTokenDes, "cas_exchange");    }}

 

转载地址:http://wgwni.baihongyu.com/

你可能感兴趣的文章
MouseEvent的e.stageX是Number型,可见as3作者的考虑
查看>>
在mc中直接加aswing组件,该组件还需最后用validate()方法
查看>>
社区设计细节 : 用户可选是否在新窗口中打开主题
查看>>
Memcache是什么?
查看>>
Eclipse和FlexBuilder中设置编辑代码高亮
查看>>
移植Vim配色方案到Eclipse
查看>>
xml解析
查看>>
告诉你到底什么是crossdomain.xml
查看>>
flexBuilder3中生成的模板页不支持flash全屏的修改办法
查看>>
aswing学习笔记
查看>>
aswing学习笔记2-不规则外框-请教思路
查看>>
aswing学习笔记3-在JPanel中,如何将.png格式的图片设置为背景?
查看>>
aswing学习笔记4-通过调用面板中的按钮实现主界面动态切换皮肤的问题!
查看>>
飞机移动缓动类,深藏于心的精华
查看>>
Eclipse中有趣的设置
查看>>
[AS3]使用RSL进行AS瘦身编程
查看>>
哈希结构是如何找到相对应的键-值对?
查看>>
IE中的注释:saved from url
查看>>
很囧的实验:一辆奥迪究竟值多少女大学生? 阅读 3056 回复 12 [回复] [编辑] [修改]...
查看>>
跟我一起写 Makefile
查看>>