zoukankan      html  css  js  c++  java
  • cas4.2.7 集群服务搭建

    cas服务端集群,网上资料很多,无非就是session共享,ticket共享。 但是session共享是必须的吗?或者能实现集群吗?

    实践:

    1. ticket共享,直接上代码

    package org.jasig.cas.ticket;
    
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.concurrent.TimeUnit;
    
    import org.jasig.cas.ticket.registry.AbstractDistributedTicketRegistry;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    
    /**
     * @author zlx
     * @Description: redis管理ticket
     * @date 2018年4月9日 下午3:28:12
     */
    @Component("redisTicketRegistry")
    public class RedisTicketRegistry extends AbstractDistributedTicketRegistry implements DisposableBean {
        /**管理ticket key,避免使用redis keys命令*/
        private final String TICKET_KEY_MANAGER = "ticket_key_manager";
        @Autowired
        private RedisTemplate<String, Ticket> redisTemplate;
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
        //默认失效时间 /小时
        private int timeout = 8;
    
        @Override
        public void updateTicket(final Ticket ticket) {
            logger.debug("Updating ticket {}", ticket);
            try {
                this.redisTemplate.boundValueOps(ticket.getId()).set(ticket);
                this.redisTemplate.expire(ticket.getId(), timeout, TimeUnit.HOURS);
            } catch (final Exception e) {
                logger.error("Failed updating {}", ticket, e);
            }
        }
    
        @Override
        public void addTicket(final Ticket ticket) {
            logger.debug("Adding ticket {}", ticket);
            try {
                this.redisTemplate.boundValueOps(ticket.getId()).set(ticket);
                this.redisTemplate.expire(ticket.getId(), timeout, TimeUnit.HOURS);
                this.stringRedisTemplate.boundSetOps(TICKET_KEY_MANAGER).add(ticket.getId());
            } catch (final Exception e) {
                logger.error("Failed adding {}", ticket, e);
            }
        }
    
        @Override
        public int deleteTicket(final String ticketId) {
            int count = 0;
            logger.debug("Deleting ticket {}", ticketId);
            try {
                this.redisTemplate.delete(ticketId);
                this.stringRedisTemplate.boundSetOps(TICKET_KEY_MANAGER).remove(ticketId);
                count++;
            } catch (final Exception e) {
                logger.error("Failed deleting {}", ticketId, e);
            }
            return count;
        }
    
        @Override
        public Ticket getTicket(final String ticketId) {
            try {
                final Ticket t = this.redisTemplate.boundValueOps(ticketId).get();
                if (t != null) {
                    return getProxiedTicketInstance(t);
                }
            } catch (final Exception e) {
                logger.error("Failed fetching {} ", ticketId, e);
            }
            return null;
        }
    
        @Override
        public boolean deleteSingleTicket(String ticketId) {
            logger.debug("Deleting Single Ticket {}", ticketId);
            try {
                this.redisTemplate.delete(ticketId);
                this.stringRedisTemplate.boundSetOps(TICKET_KEY_MANAGER).remove(ticketId);
                return true;
            } catch (final Exception e) {
                logger.error("Failed deleting {}", ticketId, e);
            }
            return false;
        }
    
        @Override
        public Collection<Ticket> getTickets() {
            Set<Ticket> tickets = new HashSet<Ticket>();
    
            Set<String> keys = this.stringRedisTemplate.boundSetOps(TICKET_KEY_MANAGER).members();
            for (String key : keys) {
                Ticket ticket = this.redisTemplate.boundValueOps(key).get();
                if (ticket == null) {
                    this.stringRedisTemplate.boundSetOps(TICKET_KEY_MANAGER).remove(key);
                } else {
                    tickets.add(ticket);
                }
            }
            return tickets;
        }
        
        @Override
        public void destroy() throws Exception {
            // TODO Auto-generated method stub
            
        }
    
        @Override
        protected boolean needsCallback() {
            // TODO Auto-generated method stub
            return false;
        }
        
    }

    deployerConfigContext.xml中变更:

        <!-- <alias name="defaultTicketRegistry" alias="ticketRegistry" /> -->
        <alias name="redisTicketRegistry" alias="ticketRegistry" />

    ticket共享配好后,发布代码。 测试发现有时候cas集群有效。有时候报错。错误如下:

    2018-05-14 16:48:11,841 ERROR [org.jasig.cas.util.WebflowCipherExecutor] - <Unable to correctly extract the Initialization Vector or ciphertext.>
    org.apache.shiro.crypto.CryptoException: Unable to correctly extract the Initialization Vector or ciphertext.
        at org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:378) ~[shiro-core-1.2.6.jar:1.2.6]
        at org.jasig.cas.util.BinaryCipherExecutor.decode(BinaryCipherExecutor.java:102) ~[cas-server-core-util-4.2.7.jar:4.2.7]
        at org.jasig.cas.util.BinaryCipherExecutor.decode(BinaryCipherExecutor.java:1) ~[cas-server-core-util-4.2.7.jar:4.2.7]
        at org.jasig.cas.web.flow.CasWebflowCipherBean.decrypt(CasWebflowCipherBean.java:44) ~[cas-server-webapp-support-4.2.7.jar:4.2.7]
        at org.jasig.spring.webflow.plugin.EncryptedTranscoder.decode(EncryptedTranscoder.java:105) ~[spring-webflow-client-repo-1.0.0.jar:1.0.0]
        at org.jasig.spring.webflow.plugin.ClientFlowExecutionRepository.getFlowExecution(ClientFlowExecutionRepository.java:90) ~[spring-webflow-client-repo-1.0.0.jar:1.0.0]
        at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:168) ~[spring-webflow-2.4.2.RELEASE.jar:2.4.2.RELEASE]
        at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:228) ~[spring-webflow-2.4.2.RELEASE.jar:2.4.2.RELEASE]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) ~[spring-webmvc-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) ~[spring-webmvc-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869) ~[spring-webmvc-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) ~[servlet-api.jar:?]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[servlet-api.jar:?]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[catalina.jar:8.5.0]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-websocket.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[catalina.jar:8.5.0]
        at org.apereo.cas.security.ResponseHeadersEnforcementFilter.doFilter(ResponseHeadersEnforcementFilter.java:238) ~[cas-server-security-filter-2.0.6.jar:2.0.6]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[catalina.jar:8.5.0]
        at org.apereo.cas.security.RequestParameterPolicyEnforcementFilter.doFilter(RequestParameterPolicyEnforcementFilter.java:261) ~[cas-server-security-filter-2.0.6.jar:2.0.6]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[catalina.jar:8.5.0]
        at org.jasig.inspektr.common.web.ClientInfoThreadLocalFilter.doFilter(ClientInfoThreadLocalFilter.java:62) ~[inspektr-common-1.3.GA.jar:1.3.GA]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[catalina.jar:8.5.0]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[catalina.jar:8.5.0]
        at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:164) ~[spring-session-1.2.2.RELEASE.jar:?]
        at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:80) ~[spring-session-1.2.2.RELEASE.jar:?]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.2.8.RELEASE.jar:4.2.8.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) ~[catalina.jar:8.5.0]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:522) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) ~[catalina.jar:8.5.0]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) ~[catalina.jar:8.5.0]
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620) ~[catalina.jar:8.5.0]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) ~[catalina.jar:8.5.0]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[catalina.jar:8.5.0]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1096) ~[tomcat-coyote.jar:8.5.0]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-coyote.jar:8.5.0]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:760) ~[tomcat-coyote.jar:8.5.0]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1480) ~[tomcat-coyote.jar:8.5.0]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_77]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_77]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-util.jar:8.5.0]
        at java.lang.Thread.run(Thread.java:745) [?:1.8.0_77]
    Caused by: java.lang.NullPointerException
        at java.lang.System.arraycopy(Native Method) ~[?:1.8.0_77]
        at org.apache.shiro.crypto.JcaCipherService.decrypt(JcaCipherService.java:370) ~[shiro-core-1.2.6.jar:1.2.6]
        ... 60 more

    查资料都说要用tomcat redis session manager实现集群tomcat的session共享

    那就根据资料配置好tomcat redis session manager,使用压力测试(集群切换分发更频繁)还是一样一部分成功一部分失败。也使用spring redis session试过了还是一样。

    后来发现了一篇文章:https://blog.csdn.net/eguid_1/article/details/51444009

    仔细分析了下spring web flow的源码,确定了问题就是出在webflow流程控制上,使用组播方式实现session复制也无济于事(也配过了,确实没有效果),修改spring webflow的源码难度太大,绕过webflow登录流程代码改动太大。

    为了寻找到更好的解决方案,继续分析、跟踪cas源码,从最底层错误开始分析

    1. JcaCipherService.java:370

    2. BinaryCipherExecutor.java:102

    3. CasWebflowCipherBean.java:44

      这一步发现调用的CipherExecutor是可以选择的,默认使用的为BinaryCipherExecutor。既然BinaryCipherExecutor里报错那就换NoOpCipherExecutor试试看。因为NoOpCipherExecutor的encode和decode方法的参数和返回值是String类型

    而CasWebflowCipherBean调用时传的是byte[]:

    @Override
    public byte[] decrypt(final byte[] bytes) {
        return webflowCipherExecutor.decode(bytes);
    }

    那么就自定义一个CipherExecutor,代码如下:

    package org.jasig.cas.util;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    /**
     * No-Op cipher executor that does nothing for encryption/decryption.
     * @author Misagh Moayyed
     * @since 4.1
     */
    @Component("noOpByteCipherExecutor")
    public final class NoOpByteCipherExecutor extends AbstractCipherExecutor<byte[], byte[]> {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(NoOpByteCipherExecutor.class);
    
        /**
         * Instantiates a new No-Op cipher executor.
         * Issues a warning on safety.
         */
        public NoOpByteCipherExecutor() {
            super(NoOpByteCipherExecutor.class.getName());
            LOGGER.warn("[{}] does no encryption and may NOT be safe in a production environment. "
                    + "Consider using other choices, such as [{}] that handle encryption, signing and verification of "
                    + "all appropriate values.", this.getClass().getName(), BaseStringCipherExecutor.class.getName());
        }
    
        @Override
        public byte[] encode(final byte[] value) {
            return value;
        }
    
        @Override
        public byte[] decode(final byte[] value) {
            return value;
        }
    }

    然后在cas-servlet.xml中配置:

    <!-- 自定义CipherExecutor -->
    <bean id="loginFlowCipherBean" class="org.jasig.cas.web.flow.CasWebflowCipherBean" >
      <constructor-arg ref="noOpByteCipherExecutor" />
    </bean>

    要实现集群单点登录还需要在deployerConfigContext.xml增加如下配置:

    <!-- 注入noOpCookieValueManager -->
    <bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.TGCCookieRetrievingCookieGenerator">
        <constructor-arg  ref="noOpCookieValueManager" />
    </bean>

    至此实现了集群下的单点登录功能。

    版权声明:本文为博主原创文章,转载需注明出处。http://www.cnblogs.com/bryanx/p/9044473.html

  • 相关阅读:
    要求两个条件都为假时,执行某些操作
    Celery + RabbitMq 示意图
    关于消息队列的好文章
    django related_name, on_delete
    Celery 图,[转]
    django model 中 meta子类详解
    django 自定义app
    python __dict__
    Python SQLAlchemy基本操作和常用技巧(包含大量实例,非常好)【转】
    scala private
  • 原文地址:https://www.cnblogs.com/bryanx/p/9044473.html
Copyright © 2011-2022 走看看