• Springboot security cas整合方案-原理篇


    前言:网络中关于Spring security整合cas的方案有很多例,对于Springboot security整合cas方案则比较少,且有些仿制下来运行也有些错误,所以博主在此篇详细的分析cas原理以及Springboot如何正确的配置cas环境

    CAS原理

    首先整合cas方案的话,无疑理解cas的原理是迫在眉睫的,这在后面对理解代码也有很好的帮助,此处可查看别人写的文章>>>CAS实现SSO单点登录原理,博主只在这里针对springboot cas整合罗列出了其中的逻辑
    Spring security cas原理图

    以上的逻辑看起来比较抽象,下面我们结合源码部分对其作补充

    CAS代码逻辑

    我们需要熟悉下以下这几个Filter类

    - SingleSignOutFilter 单点注销Filter类,接收cas服务端发出的注销session请求
    - LogoutFilter 登录退出Filter类,转发至cas服务端进行注销
    - CasAuthenticationFilter cas校验Filter处理类,包括对含有token的请求或者指定的路径请求处理
    - ExceptionTranslationFilter 异常Filter处理类,主要是接受AccessDeniedException/AuthenticationException这两个异常,其中涉及转发请求至cas服务端登录页面
    - FilterSecurityInterceptor 权限验证处理类
    

    SingleSignOutFilter

    主要涉及session的创建以及销毁,响应token请求、SLO的前后通道请求,源码如下

    	//最终处理请求响应类
    	private static final SingleSignOutHandler HANDLER = new SingleSignOutHandler();
    	//是否已初始化,默认为false
        private AtomicBoolean handlerInitialized = new AtomicBoolean(false);
    	
    	//复写Filter类的init方法,主要是初始化参数
        public void init(final FilterConfig filterConfig) throws ServletException {
    	    //初始化ConfigurationStrategy策略类,默认为LegacyConfigurationStrategyImpl实现类
            super.init(filterConfig);
            //ignoreInitConfiguration是否为true,false则采用ConfigurationStrategy的相应参数名
            if (!isIgnoreInitConfiguration()) {
                //设置凭证参数,默认为ticket            
                setArtifactParameterName(getString(ConfigurationKeys.ARTIFACT_PARAMETER_NAME));
                //设置登录退出参数,默认为logoutRequest            
                setLogoutParameterName(getString(ConfigurationKeys.LOGOUT_PARAMETER_NAME));
                //设置前台通道参数,默认为SAMLRequest,基于SAML实现,此处可自行查阅                                 
                setFrontLogoutParameterName(getString(ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME));
                //设置RelayState参数,默认为RelayState           
                setRelayStateParameterName(getString(ConfigurationKeys.RELAY_STATE_PARAMETER_NAME));
                //设置cas服务端前缀,比如https://example.cas.com/cas            
                setCasServerUrlPrefix(getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX));
    
                HANDLER.setArtifactParameterOverPost(getBoolean(ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST));
                HANDLER.setEagerlyCreateSessions(getBoolean(ConfigurationKeys.EAGERLY_CREATE_SESSIONS));
            }
            //主要设置safeParameters参数,默认只有logoutParameterName,即logoutRequest
            HANDLER.init();
            //设置为已初始化
            handlerInitialized.set(true);
        }
    

    进而继续查看SingleSignOutFilter#doFilter方法,看其中的处理逻辑

    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
                final FilterChain filterChain) throws IOException, ServletException {
            final HttpServletRequest request = (HttpServletRequest) servletRequest;
            final HttpServletResponse response = (HttpServletResponse) servletResponse;
    
    	    //判断有无初始化
            if (!this.handlerInitialized.getAndSet(true)) {
                HANDLER.init();
            }
    		//通过SingleSignOutHandler类处理请求,只有返回true才放行
            if (HANDLER.process(request, response)) {
                filterChain.doFilter(servletRequest, servletResponse);
            }
        }
    

    核心处理类SingleSignOutHandler#process()的代码如下

    public boolean process(final HttpServletRequest request, final HttpServletResponse response) {
    		//判断是否是token请求,即request对象中是否含有ticket属性
            if (isTokenRequest(request)) {
                logger.trace("Received a token request");
                //保存当前的会话
                recordSession(request);
                
                return true;
    
            }
            //POST请求&非文件上传请求&request对象含有logoutRequest属性 
            else if (isBackChannelLogoutRequest(request)) {
                logger.trace("Received a back channel logout request");
                //销毁会话
                destroySession(request);
                return false;
    
            }
    	    //GET请求&casServerUrlPrefix已设置&request对象含有SAMLRequest属性 
    		else if (isFrontChannelLogoutRequest(request)) {
                logger.trace("Received a front channel logout request");
                destroySession(request);
                // redirection url to the CAS server 拼装至cas服务端的logout请求
                final String redirectionUrl = computeRedirectionToServer(request);
                if (redirectionUrl != null) {
                    CommonUtils.sendRedirect(response, redirectionUrl);
                }
                return false;
    
            } else {
                //对非logout请求都进行放行
                return true;
            }
        }
    
    1. SingleSignOutFilter主要响应的是对cas服务端注销后对客户端应用的注销请求,其需要LogoutFilter的配合。这里涉及到SLO/SAML的概念,有兴趣的可自行查阅
    2. 放行策略:对已含有tokenticket参数的请求放行;非SLO logout请求放行

    LogoutFilter

    登录退出过滤类,是比较简单的Filter类,实现上也比较简单,简单看下

    • 构造函数
    	public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler,
    			LogoutHandler... handlers) {
    		//两个参数都不能为空
    		Assert.notEmpty(handlers, "LogoutHandlers are required");
    		this.handlers = Arrays.asList(handlers);
    		Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null");
    		this.logoutSuccessHandler = logoutSuccessHandler;
    		//默认接受的登录退出请求为 /logout
    		setFilterProcessesUrl("/logout");
    	}
    
            //设置退出操作成功后跳转的url:logoutSuccessUrl,此处一般为跳转至cas服务端退出路径并携带service回调路径
            public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
    		    Assert.notEmpty(handlers, "LogoutHandlers are required");
    		    this.handlers = Arrays.asList(handlers);
    		    Assert.isTrue(
    				!StringUtils.hasLength(logoutSuccessUrl)
    						|| UrlUtils.isValidRedirectUrl(logoutSuccessUrl),
    				logoutSuccessUrl + " isn't a valid redirect URL");
    		    SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
    		    if (StringUtils.hasText(logoutSuccessUrl)) {
    			    urlLogoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);
    		    }
    		    logoutSuccessHandler = urlLogoutSuccessHandler;
    		    setFilterProcessesUrl("/logout");
    	}
    
    • doFilter()逻辑
    	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
    			throws IOException, ServletException {
    		HttpServletRequest request = (HttpServletRequest) req;
    		HttpServletResponse response = (HttpServletResponse) res;
    		//即匹配当前的请求是否为指定的响应请求,默认判断是否为/logout
    		if (requiresLogout(request, response)) {
    			//获取上下文中的Authentication 凭证信息
    			Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    
    			if (logger.isDebugEnabled()) {
    				logger.debug("Logging out user '" + auth
    						+ "' and transferring to logout destination");
    			}
    			//一般是销毁session和清除Authentication 凭证信息,比如SecurityContextLogoutHandler
    			for (LogoutHandler handler : handlers) {
    				handler.logout(request, response, auth);
    			}
    			//跳转至cas服务端注销页面
    			logoutSuccessHandler.onLogoutSuccess(request, response, auth);
    
    			return;
    		}
    		//非logout请求放行
    		chain.doFilter(request, response);
    	}
    

    LogoutFilter的逻辑比较简单,主要是对logout请求进行响应,具体作用是

    1. 销毁session以及安全上下文的Authentication 凭证对象

    2. 跳转至cas服务端注销页面,这里可以配置跳转路径为 casServerUrlPrefix+casServerLogoutUrl+"?service="+casAppServiceUrl

    3. cas服务端回调service来销毁客户端缓存的session,即SingleSignOutFilter

    CasAuthenticationFilter

    CasAuthenticationFilter涉及的篇幅较长,可点击>>>Springboot security cas源码陶冶-CasAuthenticationFilter

    ExceptionTranslationFilter

    异常处理类,主要涉及对AuthenticationExceptionAcessDeniedException的响应,在cas中主要应用为出现授权/校验错误则转发路径到casServerLoginUrl供用户统一登录,具体的可查看>>>Springboot security cas源码陶冶-ExceptionTranslationFilter

    FilterSecurityInterceptor

    授权拦截器,可点击>>>Springboot security cas源码陶冶-FilterSecurityInterceptor

    spring security cas逻辑示意图

    通过此图再结合以上的代码分析,便可以深入理解spring security是如何整合cas了
    spring-security-cas

    下节预告

    Springboot security cas整合方案-实践篇

  • 相关阅读:
    Newbit 启用淘宝店域名
    Ninja构建系统入门
    异想家Golang学习笔记
    Webpack学习
    JavaFx图形界面开发
    异想家Win10常用的软件推荐
    Java Swing图形界面开发
    优雅写Java之四(类与对象)
    优雅写Java之三(IO与文本解析)
    优雅写Java之二(数组集合流)
  • 原文地址:https://www.cnblogs.com/question-sky/p/7061522.html
Copyright © 2020-2023  润新知