什么是四季沐歌CAS系统?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是四季沐歌CAS系统?相关的知识,希望对你有一定的参考价值。

CAS系统即清洁能源耦合智慧操作系统(Clean energy coupled AI operating System,简称CAS),是保障一种或多种能源供应系统从设计到运营一体化的解决方案体系,结合硬件、软件、算法保障清洁能源系统应用更高效、节能、环保、经济和智能,可广泛应用于采暖制冷、热水、烘干、生产等场景。 参考技术A 是由四季沐歌自主研发的针对空气源热泵的一个控制系统,里面不仅可以针对空气源热泵提供有针对性的方案,还可以罗列全生命周期的相关经济指标,使业主有经济账可算,投资有依据,做到心中有数。在项目运行阶段又可以实现全自动控制,根据环境温度通过算法可实现机组之间变频机启停控制。在系统有故障时,可第一时间通知运维人员,快速辨别问题所在。从而快速解决问题。

SpringBoot+CAS这套单点登录通用方案,打通我司几十个系统,稳的一批!大Pass平台...

为什么很多 SpringBoot 开发者放弃了 Tomcat,选择了 Undertow?

上一篇:35岁中年博士失业,决定给找高校教职的后辈一些建议

  • 前言

  • 一、CAS是什么?

  • 二、搭建客户端系统

  1. 引入CAS

  2. 客户端后端搭建

总结

前言

什么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分,如图(不标准,只是方便理解)。

一、CAS是什么?

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点:

  • 开源的企业级单点登录解决方案。

  • CAS Server 为需要独立部署的 Web 应用。

  • CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

二、搭建客户端系统

1.引入CAS

参考:https://www.bilibili.com/video/BV1xy4y1r7BU?t=666&p=8

注意其中将证书导入jdk中,一定要注意精确到cacerts这个文件下,不然一直报拒绝写入,另外最好用管理员下的命令窗口

2.客户端后端搭建

1.添加依赖

<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.5</version>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
</dependency>

2.配置客户端

server:
  port: 1234

3.添加config(filter)文件

地址全为ip,如果用hosts映射地址,可能会出现问题

package com.casclient1.cas.config;

import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.CharacterEncodingFilter;

import javax.servlet.Filter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;


@Configuration
public class FilterConfig implements Serializable, InitializingBean 

    private static final Logger LOGGER = LoggerFactory.getLogger(FilterConfig.class);

    public static final String CAS_SIGNOUT_FILTER_NAME = "CAS Single Sign Out Filter";
    public static final String CAS_AUTH_FILTER_NAME = "CAS Filter";
    public static final String CAS_IGNOREL_SSL_FILTER_NAME = "CAS Ignore SSL Filter";
    public static final String CAS_FILTER_NAME = "CAS Validation Filter";
    public static final String CAS_WRAPPER_NAME = "CAS HttpServletRequest Wrapper Filter";
    public static final String CAS_ASSERTION_NAME = "CAS Assertion Thread Local Filter";
    public static final String CHARACTER_ENCODING_NAME = "Character encoding Filter";

 //CAS服务器退出地址
    private static String casSigntouServerUrlPrefix = "https://127.0.0.1:8443/cas/logout";
    //CAS服务器登录地址
    private static String casServerLoginUrl = "https://127.0.0.1:8443/cas/login";
    //客户端地址
    private static String clienthosturl="http://127.0.0.1:1234";
    //CAS服务器地址
    private static String casValidationServerUrlPrefix = "https://127.0.0.1:8443/cas";

    public FilterConfig() 

    

    /**
     * 单点登出功能,放在其他filter之前
     * casSigntouServerUrlPrefix为登出前缀:https://123.207.122.156:8081/cas/logout
     *
     * @return
     */
    @Bean
    @Order(0)
    public FilterRegistrationBean getCasSignoutFilterRegistrationBean() 
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(getCasSignoutFilter());
        registration.addUrlPatterns("/*", "*.html");
        registration.addInitParameter("casServerUrlPrefix", casSigntouServerUrlPrefix);
        registration.setName(CAS_SIGNOUT_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    

    @Bean(name = CAS_SIGNOUT_FILTER_NAME)
    public Filter getCasSignoutFilter() 
        return new SingleSignOutFilter();
    

    /**
     * 忽略SSL认证
     *
     * @return
     */
    @Bean
    @Order(1)
    public FilterRegistrationBean getCasSkipSSLValidationFilterRegistrationBean() 
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(getCasSkipSSLValidationFilter());
        registration.addUrlPatterns("/*", "*.html");
        registration.setName(CAS_IGNOREL_SSL_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    

    @Bean(name = CAS_IGNOREL_SSL_FILTER_NAME)
    public Filter getCasSkipSSLValidationFilter() 
        return new IgnoreSSLValidateFilter();
    

    /**
     * 负责用户的认证
     * casServerLoginUrl:https://123.207.122.156:8081/cas/login
     * casServerName:https://123.207.122.156:8080/tdw/alerts/
     *
     * @return
     */
    @Bean
    @Order(2)
    public FilterRegistrationBean getCasAuthFilterRegistrationBean() 
        FilterRegistrationBean registration = new FilterRegistrationBean();
        final Filter casAuthFilter = getCasAuthFilter();
        registration.setFilter(casAuthFilter);
        registration.addUrlPatterns("/*", "*.html");
        registration.addInitParameter("casServerLoginUrl", casServerLoginUrl);
        registration.addInitParameter("serverName", clienthosturl);
        registration.setName(CAS_AUTH_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    

    @Bean(name = CAS_AUTH_FILTER_NAME)
    public Filter getCasAuthFilter() 
        return new MyAuthenticationFilter();
    

    /**
     * 对Ticket进行校验
     * casValidationServerUrlPrefix要用内网ip
     * casValidationServerUrlPrefix:https://123.207.122.156:8081/cas
     * casServerName:https://123.207.122.156:8080/tdw/alerts/
     *
     * @return
     */
    @Bean
    @Order(3)
    public FilterRegistrationBean getCasValidationFilterRegistrationBean() 
        FilterRegistrationBean registration = new FilterRegistrationBean();
        final Filter casValidationFilter = getCasValidationFilter();
        registration.setFilter(casValidationFilter);
        registration.addUrlPatterns("/*", "*.html");
        registration.addInitParameter("casServerUrlPrefix", casValidationServerUrlPrefix);
        registration.addInitParameter("serverName", clienthosturl);
        registration.setName(CAS_FILTER_NAME);
        registration.setEnabled(true);
        return registration;
    

    @Bean(name = CAS_FILTER_NAME)
    public Filter getCasValidationFilter() 
        return new Cas20ProxyReceivingTicketValidationFilter();
    

    /**
     * 设置response的默认编码方式:UTF-8。
     *
     * @return
     */
    @Bean
    @Order(4)
    public FilterRegistrationBean getCharacterEncodingFilterRegistrationBean() 
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(getCharacterEncodingFilter());
        registration.addUrlPatterns("/*", "*.html");
        registration.setName(CHARACTER_ENCODING_NAME);
        registration.setEnabled(true);
        return registration;
    

    @Bean(name = CHARACTER_ENCODING_NAME)
    public Filter getCharacterEncodingFilter() 
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        return characterEncodingFilter;
    

    @Bean
    public FilterRegistrationBean casHttpServletRequestWrapperFilter()
        FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
        authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());
        authenticationFilter.setOrder(6);
        List<String> urlPatterns = new ArrayList<>();
        urlPatterns.add("/*");
        authenticationFilter.setUrlPatterns(urlPatterns);
        return authenticationFilter;
    

    @Override
    public void afterPropertiesSet() throws Exception 

    

4.filter类中的MyAuthenticationFilter是重写cas jar包中的AuthenticationFilter,原因是CAS源码无法认证直接重定向,而ajax请求又不能直接重定向,导致前端302,而302vue response拦截器是拦截不到的。所以就想到不让cas给我重定向,给我返回状态码,告诉前端认证失败,让前端直接跳转cas服务器登录地址。

修改cas源码过滤器,复制源码AuthenticationFilter这个过滤器,重写他,其实这里只改了重定向的代码其他都一样。上MyAuthenticationFilter代码

package com.casclient1.cas.config;

import org.jasig.cas.client.authentication.*;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
import org.jasig.cas.client.validation.Assertion;

import javax.servlet.FilterConfig;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class MyAuthenticationFilter extends AbstractCasFilter 
    private String casServerLoginUrl;
    private boolean renew = false;
    private boolean gateway = false;
    private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
    private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy();
    private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
    private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES = new HashMap();

    public MyAuthenticationFilter() 
    

    @Override
    protected void initInternal(FilterConfig filterConfig) throws ServletException 
        if (!this.isIgnoreInitConfiguration()) 
            super.initInternal(filterConfig);
            this.setCasServerLoginUrl(this.getPropertyFromInitParams(filterConfig, "casServerLoginUrl", (String)null));
            this.logger.trace("Loaded CasServerLoginUrl parameter: ", this.casServerLoginUrl);
            this.setRenew(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "renew", "false")));
            this.logger.trace("Loaded renew parameter: ", this.renew);
            this.setGateway(this.parseBoolean(this.getPropertyFromInitParams(filterConfig, "gateway", "false")));
            this.logger.trace("Loaded gateway parameter: ", this.gateway);
            String ignorePattern = this.getPropertyFromInitParams(filterConfig, "ignorePattern", (String)null);
            this.logger.trace("Loaded ignorePattern parameter: ", ignorePattern);
            String ignoreUrlPatternType = this.getPropertyFromInitParams(filterConfig, "ignoreUrlPatternType", "REGEX");
            this.logger.trace("Loaded ignoreUrlPatternType parameter: ", ignoreUrlPatternType);
            if (ignorePattern != null) 
                Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = (Class)PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
                if (ignoreUrlMatcherClass != null) 
                    this.ignoreUrlPatternMatcherStrategyClass = (UrlPatternMatcherStrategy) ReflectUtils.newInstance(ignoreUrlMatcherClass.getName(), new Object[0]);
                 else 
                    try 
                        this.logger.trace("Assuming  is a qualified class name...", ignoreUrlPatternType);
                        this.ignoreUrlPatternMatcherStrategyClass = (UrlPatternMatcherStrategy)ReflectUtils.newInstance(ignoreUrlPatternType, new Object[0]);
                     catch (IllegalArgumentException var6) 
                        this.logger.error("Could not instantiate class []", ignoreUrlPatternType, var6);
                    
                

                if (this.ignoreUrlPatternMatcherStrategyClass != null) 
                    this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern);
                
            

            String gatewayStorageClass = this.getPropertyFromInitParams(filterConfig, "gatewayStorageClass", (String)null);
            if (gatewayStorageClass != null) 
                this.gatewayStorage = (GatewayResolver)ReflectUtils.newInstance(gatewayStorageClass, new Object[0]);
            

            String authenticationRedirectStrategyClass = this.getPropertyFromInitParams(filterConfig, "authenticationRedirectStrategyClass", (String)null);
            if (authenticationRedirectStrategyClass != null) 
                this.authenticationRedirectStrategy = (AuthenticationRedirectStrategy)ReflectUtils.newInstance(authenticationRedirectStrategyClass, new Object[0]);
            
        

    

    @Override
    public void init() 
        super.init();
        CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
    

    @Override
    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException 
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        if (this.isRequestUrlExcluded(request)) 
            this.logger.debug("Request is ignored.");
            filterChain.doFilter(request, response);
         else 
            HttpSession session = request.getSession(false);
            Assertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;
            if (assertion != null) 
                filterChain.doFilter(request, response);
             else 
                String serviceUrl = this.constructServiceUrl(request, response);
                String ticket = this.retrieveTicketFromRequest(request);
                boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
                if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) 
                    this.logger.debug("no ticket and no assertion found");
                    String modifiedServiceUrl;
                    if (this.gateway) 
                        this.logger.debug("setting gateway attribute in session");
                        modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
                     else 
                        modifiedServiceUrl = serviceUrl;
                    

                    this.logger.debug("Constructed service url: ", modifiedServiceUrl);

                    String xRequested =request.getHeader("x-requested-with");
                    if("XMLHttpRequest".equals(xRequested))
                        response.getWriter().write("\\"code\\":202, \\"msg\\":\\"no ticket and no assertion found\\"");
                    else
                        String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
                        this.logger.debug("redirecting to \\"\\"", urlToRedirectTo);
                        this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
                    
                 else 
                    filterChain.doFilter(request, response);
                
            
        
    

    public final void setRenew(boolean renew) 
        this.renew = renew;
    

    public final void setGateway(boolean gateway) 
        this.gateway = gateway;
    

    public final void setCasServerLoginUrl(String casServerLoginUrl) 
        this.casServerLoginUrl = casServerLoginUrl;
    

    public final void setGatewayStorage(GatewayResolver gatewayStorage) 
        this.gatewayStorage = gatewayStorage;
    

    private boolean isRequestUrlExcluded(HttpServletRequest request) 
        if (this.ignoreUrlPatternMatcherStrategyClass == null) 
            return false;
         else 
            StringBuffer urlBuffer = request.getRequestURL();
            if (request.getQueryString() != null) 
                urlBuffer.append("?").append(request.getQueryString());
            

            String requestUri = urlBuffer.toString();
            return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri);
        
    

    static 
        PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class);
        PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
        PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
    

测试Controller

package com.casclient1.cas.controller;

import com.casclient1.cas.domain.UserDomain;
import com.casclient1.cas.tools.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Console;
import java.io.IOException;

@Controller
public class TestController 
    /**
     * 测试
     * @return
     */
    @GetMapping("/test")
    @ResponseBody
    public Result<UserDomain>  login(HttpServletRequest httpServletRequest)
        System.out.println("sss");
        return new Result<>(new UserDomain(httpServletRequest.getRemoteUser()));
    

    @GetMapping("/checkTicket")
    public void index(HttpServletResponse response) throws IOException 
        // 前端页面地址
        response.sendRedirect("http://127.0.0.1:8088/Home");
    
        /**
     * 注销
     * @return
     */
    @RequestMapping("/logout")
    public String logout()
        return "redirect:https://127.0.0.1:8443/cas/logout";
    
    

3.前端

<template xmlns="http://www.w3.org/1999/html">
  <div >

    <header style="height: 60px">
      <span>客户端2验证:name</span>
        <button @click="logout">安全退出</button>
    </header>
    <router-view></router-view>
    <!--

  <my-vue v-bind:lineID="lineID"></my-vue>-->
  </div>
</template>
<style lang="scss">
</style>
<script type="text/ecmascript-6">

  export default 

    data() 

      return 
            name:'ss'
      

    ,
mounted()
    var _this = this;
    this.$http.get('/test', headers: 'x-requested-with': 'XMLHttpRequest')
            .then(function (response) 
                console.log("sss");
                if (response.data.code === 202) 
                    debugger
                    console.log("sss");
                    window.location.href = "http://127.0.0.1:1235/checkTicket"
                 else if (response.data.code === 200) 
                    console.log("sss");
                    _this.name = response.data.data.name
                
                console.log(response);
            )
            .catch(function (error) 
                console.log(error);
            );
        ,
      methods: 
          logout() 
              window.location.href = "http://127.0.0.1:1234/logout"

          ,
      
  
</script>

5.效果

未登录:

点击客户端1超链接

登录成功

点击客户端2超链接,直接进入,无需登录。

退出

总结

网上有很多CAS单点登录的demo,但是对于前后端分离讲的比较详细的很少,前后端分离,必定会出现跨域,导致CAS登录无法重定向等等原因,结合和网上一些想法和部门代码后,大致做了一个比较完善,但很基础的单点登录系统,当然单点登录不光有CAS,还有JWT(1.所有服务靠约定来生成token,2.要么集中生成集中判断,所有服务都能生成都认这个,要么一个服务管控全局),OAuth2等等。

来自:blog.csdn.net/weixin_43483911/article/details/117811270

 · END ·

热门推荐:

PS:如果觉得我的分享不错,欢迎大家随手点赞、转发、在看

以上是关于什么是四季沐歌CAS系统?的主要内容,如果未能解决你的问题,请参考以下文章

关于CAS实现单点登录

开源单点登录系统CAS入门

第四季-专题16-触摸屏驱动程序设计

第四季-专题3-嵌入式Linux内核制作

CAS单点登录系统入门--分布式登录验证

第四季-专题6-Linux内核子系统