cas5.3.2單點登錄-單點登出


既然有單點登錄,肯定就要有登出,之前的整合都是只針對了登錄,對登出並沒有關注,今天我們就來講講登出。

關於單點登出原理,參考博客:

https://blog.csdn.net/u010588262/article/details/80201983
https://blog.csdn.net/gdsgdh308227363/article/details/80446168

參數說明

參考官網地址
https://apereo.github.io/cas/5.3.x/installation/Configuration-Properties.html#logout
官網共給出了以下幾個屬性值:

#配置單點登出 #配置允許登出后跳轉到指定頁面 cas.logout.followServiceRedirects=false #跳轉到指定頁面需要的參數名為 service cas.logout.redirectParameter=service #登出后需要跳轉到的地址,如果配置該參數,service將無效。 cas.logout.redirectUrl=https://www.taobao.com #在退出時是否需要 確認退出提示 true彈出確認提示框 false直接退出 cas.logout.confirmLogout=true #是否移除子系統的票據 cas.logout.removeDescendantTickets=true #禁用單點登出,默認是false不禁止 #cas.slo.disabled=true #默認異步通知客戶端,清除session #cas.slo.asynchronous=true

cas 默認登出后默認會跳轉到CASServer的登出頁,若想跳轉到其它資源,可在/logout的URL后面加上service=jumpurl,例如:https://server.cas.com:8443/cas/logout?service=https://www.github.com
但默認servcie跳轉不會生效,需要在 cas服務端的application.properties添加cas.logout.followServiceRedirects=true
這個參數也不一定非要叫 service, 可以通過cas.logout.redirectParameter 來修改它。
另外,默認退出的時候沒有任何提示,直接就退出了,若想要有彈出提示,需要添加as.logout.confirmLogout=true
再另外,有一個cas.logout.redirectUrl的屬性,可以配置默認登出之后跳轉到的連接,若 配置該屬性,service參數將無效。就算傳了service參數,也是走的該頁面,所以我們不需要配置此參數。
如果配置了cas.slo.disabled=true 將禁用單點登出。調用登出將無效。

配置過程

服務端配置

application.properties添加以下屬性
#配置允許登出后跳轉到指定頁面 cas.logout.followServiceRedirects=true #跳轉到指定頁面需要的參數名為 service cas.logout.redirectParameter=service #在退出時是否需要 確認一下 true確認 false直接退出 cas.logout.confirmLogout=true #是否移除子系統的票據 cas.logout.removeDescendantTickets=true

客戶端配置

可以直接在客戶端的登出連接寫成服務端的登出地址,不過我沒有這樣做。
我打算客戶端寫一個UserController,在各自的系統點擊登出,先進入本服務的后台方法,在該方法中重定向到服務端的登出地址。

 

 

 

環境:

jdk:1.8

cas server:5.3.14 + tomcat 8.5

cas client:3.5.1

客戶端1:springmvc 傳統web項目(使用web.xml)

客戶端2:springboot 

參考博客:https://blog.csdn.net/anumbrella/category_7765386.html

為了方便,沒配置https.如果需要參考上面博客

一.CAS 服務端搭建

1.下載源碼包:cas overlay github地址:https://github.com/apereo/cas-overlay-template/tree/5.3

2.部署:

在根目錄下執行mvn clean package,下載依賴需要一段時間,如果有的包下載失敗就要修改pom.xml比如xmlsectool的jar包要先從maven中央倉庫手動下載,然后加進去,我的放到了圖中的maven目錄下

 以下是我修改的pom.xml,對xmlselectool依賴路徑進行了更改,增加了json注冊服務,jdbc驗證等依賴包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd ">


    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apereo.cas</groupId>
    <artifactId>cas-overlay</artifactId>
    <packaging>war</packaging>
    <version>1.0</version>

    <build>
        <plugins>
            <plugin>
                <groupId>com.rimerosolutions.maven.plugins</groupId>
                <artifactId>wrapper-maven-plugin</artifactId>
                <version>0.0.5</version>
                <configuration>
                    <verifyDownload>true</verifyDownload>
                    <checksumAlgorithm>MD5</checksumAlgorithm>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${springboot.version}</version>
                <configuration>
                    <mainClass>${mainClassName}</mainClass>
                    <addResources>true</addResources>
                    <executable>${isExecutable}</executable>
                    <layout>WAR</layout>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <warName>cas</warName>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <recompressZippedFiles>false</recompressZippedFiles>
                    <archive>
                        <compress>false</compress>
                        <manifestFile>${manifestFileToUse}</manifestFile>
                    </archive>
                    <overlays>
                        <overlay>
                            <groupId>org.apereo.cas</groupId>
                            <artifactId>cas-server-webapp${app.server}</artifactId>
                        </overlay>
                    </overlays>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
            </plugin>
        </plugins>
        <finalName>cas</finalName>
    </build>

    <properties>
        <cas.version>5.3.14</cas.version>
        <springboot.version>1.5.18.RELEASE</springboot.version>
        <mybatis.spring.version>1.3.2</mybatis.spring.version>
        <mybatis.version>3.4.6</mybatis.version>
        <!-- app.server could be -jetty, -undertow, -tomcat, or blank if you plan to provide appserver -->
        <app.server>-tomcat</app.server>

        <mainClassName>org.springframework.boot.loader.WarLauncher</mainClassName>
        <isExecutable>false</isExecutable>
        <manifestFileToUse>${project.build.directory}/war/work/org.apereo.cas/cas-server-webapp${app.server}/META-INF/MANIFEST.MF</manifestFileToUse>

        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

   <dependencies>
         <dependency>
            <groupId>net.shibboleth.tool</groupId>
            <artifactId>xmlsectool</artifactId>
            <version>2.0.0</version>
             <scope>system</scope>
            <systemPath>${pom.basedir}/maven/xmlsectool-2.0.0.jar</systemPath>
         </dependency>
       <!--新增支持jdbc驗證-->
       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-support-jdbc</artifactId>
           <version>${cas.version}</version>
           <exclusions>
               <exclusion>
                   <artifactId>jul-to-slf4j</artifactId>
                   <groupId>org.slf4j</groupId>
               </exclusion>
           </exclusions>
       </dependency>


       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-core-cookie</artifactId>
           <version>${cas.version}</version>
       </dependency>

       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-support-jdbc-drivers</artifactId>
           <version>${cas.version}</version>
           <scope>runtime</scope>
       </dependency>

       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-core-configuration</artifactId>
           <version>${cas.version}</version>
       </dependency>

       <!-- Custom Authentication -->
       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-core-authentication-api</artifactId>
           <version>${cas.version}</version>
       </dependency>

       <!-- Custom Configuration -->
       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-core-configuration-api</artifactId>
           <version>${cas.version}</version>
       </dependency>


       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-support-json-service-registry</artifactId>
           <version>${cas.version}</version>
       </dependency>

       <!-- Authentication Attributes -->
       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-core-authentication-attributes</artifactId>
           <version>${cas.version}</version>
       </dependency>

       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.56</version>
       </dependency>


       <!-- lombok -->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.18.10</version>
           <scope>provided</scope>
       </dependency>

       <dependency>
           <groupId>org.apereo.cas</groupId>
           <artifactId>cas-server-core-webflow</artifactId>
           <version>${cas.version}</version>
       </dependency>




     </dependencies>

    <profiles>
        <profile>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <id>default</id>
            <dependencies>
                <dependency>
                    <groupId>org.apereo.cas</groupId>
                    <artifactId>cas-server-webapp${app.server}</artifactId>
                    <version>${cas.version}</version>
                    <type>war</type>
                    <scope>runtime</scope>
                </dependency>
                <!--
                ...Additional dependencies may be placed here...
                -->
            </dependencies>
        </profile>

        <profile>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <id>exec</id>
            <properties>
                <mainClassName>org.apereo.cas.web.CasWebApplication</mainClassName>
                <isExecutable>true</isExecutable>
                <manifestFileToUse></manifestFileToUse>
            </properties>
            <build>
                <plugins>
                    <plugin>
                        <groupId>com.soebes.maven.plugins</groupId>
                        <artifactId>echo-maven-plugin</artifactId>
                        <version>0.3.0</version>
                        <executions>
                            <execution>
                                <phase>prepare-package</phase>
                                <goals>
                                    <goal>echo</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <echos>
                                <echo>Executable profile to make the generated CAS web application executable.</echo>
                            </echos>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

        <profile>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <id>bootiful</id>
            <properties>
                <app.server>-tomcat</app.server>
                <isExecutable>false</isExecutable>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>org.apereo.cas</groupId>
                    <artifactId>cas-server-webapp${app.server}</artifactId>
                    <version>${cas.version}</version>
                    <type>war</type>
                    <scope>runtime</scope>
                </dependency>
            </dependencies>
        </profile>

        <profile>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <id>pgp</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>com.github.s4u.plugins</groupId>
                        <artifactId>pgpverify-maven-plugin</artifactId>
                        <version>1.1.0</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>check</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <pgpKeyServer>hkp://pool.sks-keyservers.net</pgpKeyServer>
                            <pgpKeysCachePath>${settings.localRepository}/pgpkeys-cache</pgpKeysCachePath>
                            <scope>test</scope>
                            <verifyPomFiles>true</verifyPomFiles>
                            <failNoSignature>false</failNoSignature>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

 

2.接下來我們要做一些定制化,如果想先看下效果參考https://blog.csdn.net/Anumbrella/article/details/81045885

導入idea,導入的時候可以看到實際導入的是

新建src/main/java和resources,鼠標放到項目上,然后F4,把java目錄標記為sources,resources標記為resources

從overlays目錄下的WEB-INF拷貝services,META-INF文件夾已經application.properties到我們新建的resources目錄下

先說下services文件夾,該文件夾下的json文件定義了哪些應用要接入cas,命名格式為 app-id.json如

重點講下兩個屬性:1.serviceId定義哪些服務可以接入,可以寫統配符,也可以用項目名2.attributeReleaseStrategy定義返回哪些屬性,比如登錄后返回的用戶信息,其他的參考

https://apereo.github.io/cas/5.3.x/installation/Service-Management.html,提醒下logouturl謹慎使用,一定概率造成無法單點退出

 

 application.properties,端口號,項目名,開啟json服務識別

復制代碼
  1 ##
  2 # CAS Server Context Configuration
  3 #
  4 server.context-path=/cas
  5 #server.port=8443
  6 server.port=8090
  7 server.ssl.enabled=false
  8 #server.ssl.key-store=file:/etc/cas/thekeystore
  9 #server.ssl.key-store-password=changeit
 10 #server.ssl.key-password=changeit
 11         
 12 cas.tgc.secure=false
 13 
 14 spring.devtools.restart.enabled=true
 15 
 16 
 17 server.max-http-header-size=2097152
 18 server.use-forward-headers=true
 19 server.connection-timeout=20000
 20 server.error.include-stacktrace=ALWAYS
 21 
 22 server.compression.enabled=true
 23 server.compression.mime-types=application/javascript,application/json,application/xml,text/html,text/xml,text/plain
 24 
 25 server.tomcat.max-http-post-size=2097152
 26 server.tomcat.basedir=build/tomcat
 27 server.tomcat.accesslog.enabled=true
 28 server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)
 29 server.tomcat.accesslog.suffix=.log
 30 server.tomcat.min-spare-threads=10
 31 server.tomcat.max-threads=200
 32 server.tomcat.port-header=X-Forwarded-Port
 33 server.tomcat.protocol-header=X-Forwarded-Proto
 34 server.tomcat.protocol-header-https-value=https
 35 server.tomcat.remote-ip-header=X-FORWARDED-FOR
 36 server.tomcat.uri-encoding=UTF-8
 37 
 38 spring.http.encoding.charset=UTF-8
 39 spring.http.encoding.enabled=true
 40 spring.http.encoding.force=true
 41 
 42 ##
 43 # CAS Cloud Bus Configuration
 44 #
 45 spring.cloud.bus.enabled=false
 46 
 47 # Indicates that systemPropertiesOverride can be used.
 48 # Set to false to prevent users from changing the default accidentally. Default true.
 49 spring.cloud.config.allow-override=true
 50 
 51 # External properties should override system properties.
 52 spring.cloud.config.override-system-properties=false
 53 
 54 # When allowOverride is true, external properties should take lowest priority, and not override any
 55 # existing property sources (including local config files).
 56 spring.cloud.config.override-none=false
 57 
 58 # spring.cloud.bus.refresh.enabled=true
 59 # spring.cloud.bus.env.enabled=true
 60 # spring.cloud.bus.destination=CasCloudBus
 61 # spring.cloud.bus.ack.enabled=true
 62 
 63 endpoints.enabled=false
 64 endpoints.sensitive=true
 65 
 66 endpoints.restart.enabled=false
 67 endpoints.shutdown.enabled=false
 68 
 69 # Control the security of the management/actuator endpoints
 70 # The 'enabled' flag below here controls the rendering of details for the health endpoint amongst other things.
 71 management.security.enabled=true
 72 management.security.roles=ACTUATOR,ADMIN
 73 management.security.sessions=if_required
 74 management.context-path=/status
 75 management.add-application-context-header=false
 76 
 77 # Define a CAS-specific "WARN" status code and its order
 78 management.health.status.order=WARN, DOWN, OUT_OF_SERVICE, UNKNOWN, UP
 79 
 80 # Control the security of the management/actuator endpoints
 81 # With basic authentication, assuming Spring Security and/or relevant modules are on the classpath.
 82 security.basic.authorize-mode=role
 83 security.basic.path=/cas/status/**
 84 security.basic.enabled=true
 85 security.user.name=casuser
 86 security.user.password=123
 87 
 88 ##
 89 # CAS Web Application Session Configuration
 90 #
 91 server.session.timeout=3000
 92 server.session.cookie.http-only=false
 93 server.session.tracking-modes=COOKIE
 94 
 95 ##
 96 # CAS Thymeleaf View Configuration
 97 #
 98 spring.thymeleaf.encoding=UTF-8
 99 spring.thymeleaf.cache=true
100 spring.thymeleaf.mode=HTML
101 spring.thymeleaf.template-resolver-order=100
102 ##
103 # CAS Log4j Configuration
104 #
105 # logging.config=file:/etc/cas/log4j2.xml
106 server.context-parameters.isLog4jAutoInitializationDisabled=true
107 
108 ##
109 # CAS AspectJ Configuration
110 #
111 spring.aop.auto=true
112 spring.aop.proxy-target-class=true
113 
114 ##
115 # CAS Authentication Credentials
116 #
117 #cas.authn.accept.users=wangyuancheng::Baizhu7958
118 
119 
120 
121 ##
122 # Service Registry(服務注冊)
123 #
124 # 開啟識別Json文件,默認false
125 cas.serviceRegistry.initFromJson=true
126 
127 #自動掃描服務配置,默認開啟
128 #cas.serviceRegistry.watcherEnabled=true
129 
130 #120秒掃描一遍
131 cas.serviceRegistry.schedule.repeatInterval=120000
132 
133 #延遲15秒開啟
134 # cas.serviceRegistry.schedule.startDelay=15000
135 
136 ##
137 # Json配置
138 cas.serviceRegistry.json.location=classpath:/services
139 
140 
141 cas.ticket.tgt.maxTimeToLiveInSeconds=28800
142 cas.ticket.tgt.timeToKillInSeconds=7200
143 cas.ticket.tgt.rememberMe.enabled=true
144 # 使用次數
145 cas.ticket.st.numberOfUses=1  
146 # 過期時間100秒
147 cas.ticket.st.timeToKillInSeconds=100 
148 
149 cas.httpClient.allowLocalLogoutUrls=true
150 
151 
152 cas.slo.disabled=false
153 cas.logout.followServiceRedirects=true
154 cas.logout.removeDescendantTickets=true
155 cas.slo.asynchronous=true
復制代碼

默認情況下cas會提供一個jdbc的驗證方式,把sql寫在application.properties,但這種方式無法提供返回的用戶信息,因此需要定義驗證處理器及配置信息

package cn.bz.bzsso.authentication;

import cn.bz.bzsso.entity.User;
import cn.bz.bzsso.handler.MyAuthenticationHandler;
import cn.bz.bzsso.mapper.UserMapper;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author tele
 * @Description
 * @create 2019-12-04
 */
@Configuration("myAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class MyAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {

    @Autowired
    private CasConfigurationProperties casProperties;

    @Autowired
    @Qualifier("servicesManager")
    private ServicesManager servicesManager;


    /**
     * 將自定義驗證器注冊為Bean
     * @return
     */
    @Bean
    public AuthenticationHandler myAuthenticationHandler() {
        MyAuthenticationHandler handler = new MyAuthenticationHandler(MyAuthenticationHandler.class.getSimpleName(), servicesManager, new DefaultPrincipalFactory(), 1);
        return handler;
    }

    /**
     * 注冊驗證器
     * @param plan
     */
    @Override
    public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
        plan.registerAuthenticationHandler(myAuthenticationHandler());
    }
}
package cn.bz.bzsso.handler;

import cn.bz.bzsso.entity.LoginCode;
import cn.bz.bzsso.entity.LoginStatus;
import cn.bz.bzsso.entity.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * @author tele
 * @Description
 * @create 2019-12-04
 */
public class MyAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {


    public MyAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
        super(name, servicesManager, principalFactory, order);
    }

    @Override
    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {
        String username = credential.getUsername();
        String password = credential.getPassword();


        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("xx");
        dataSource.setUsername("xx");
        dataSource.setPassword("xx");

        // 創建JDBC模板
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);

        String sql = "select id,username,nickname,password from tb_user where username = ?";

        User user = (User) jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper(User.class));

        LoginStatus loginStatus = new LoginStatus();
        loginStatus.setId(user.getId());
        loginStatus.setUsername(user.getUserName());
        loginStatus.setNickname(user.getNickName());

        if(user.getPassword().equals(password)) {
            loginStatus.setCode(LoginCode.LOGIN_SUCCESS);
            loginStatus.setMessage(LoginCode.MSG_LOGIN_SUCCESS);
        }else {
            loginStatus.setCode(LoginCode.ERROR_OF_USER_PWD);
            loginStatus.setMessage(LoginCode.MSG_ERROR_OF_USER_PWD);
        }

        Map<String,Object> resultMap = new HashMap<>(4);

        resultMap.put("loginStatus", JSON.toJSONString(loginStatus,SerializerFeature.WriteNullStringAsEmpty));

        return createHandlerResult(credential, this.principalFactory.createPrincipal(credential.getUsername(),resultMap), new ArrayList<>(0));

    }
}

 

修改MATA-INF下的spring.factoriesorg.springframework.boot.autoconfigure.EnableAutoConfiguration=xx.MyAuthenticationConfiguration

ok,服務端配置到此結束,你可以自定義返回的信息,狀態碼等

二.客戶端接入

既然接入了cas,那么所有的客戶端都應該關閉登錄與退出接口,扒了下官網,看到一句話大概意思是cas不是一個session管理器,每個應用應當對自己的session負責,cas只會在退出時給接入的應用發通知,然后移除內部維護的tgt對象

,換句話說,每個應用內部應當調用session.invalidate()來銷毀各自的session

1.傳統web項目接入.這種指的是帶有web.xml的web

加入cas-client 版本3.5.1依賴

復制代碼
 1 <!--CAS Client-->
 2 <dependency>
 3     <groupId>org.jasig.cas.client</groupId>
 4     <artifactId>cas-client-core</artifactId>
 5     <version>${cas-client.version}</version>
 6 </dependency>
 7 
 8 <dependency>
 9     <groupId>org.jasig.cas.client</groupId>
10     <artifactId>cas-client-integration-tomcat-common</artifactId>
11     <version>${cas-client.version}</version>
12 </dependency>
復制代碼

在web.xml中加入如下配置,本機環境不建議使用localhost,使用127.0.0.1(5.1版本使用localhost會有退出失效無法通信的問題,瀏覽器存儲cookie時127.0.0.1和localhost是兩個不同的文件夾)

servername指的是客戶端地址

復制代碼
 1 <listener>
 2         <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
 3     </listener>
 4 
 5 
 6    <!-- 單點登出過濾器-->
 7     <filter>
 8         <filter-name>CAS Single Sign Out Filter</filter-name>
 9         <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
10         <init-param>
11             <param-name>casServerUrlPrefix</param-name>
12             <param-value>http://127.0.0.1:8090/cas/</param-value>
13         </init-param>
14     </filter>
15     <filter-mapping>
16         <filter-name>CAS Single Sign Out Filter</filter-name>
17         <url-pattern>/*</url-pattern>
18     </filter-mapping> 
19     
20    
21 
22     <!--用來跳轉登錄-->
23     <filter>
24         <filter-name>CAS Authentication Filter</filter-name>
25         <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
26         <init-param>
27             <param-name>casServerLoginUrl</param-name>
28             <param-value>http://127.0.0.1:8090/cas/login</param-value>
29         </init-param>
30         <init-param>
31             <param-name>serverName</param-name>
32             <!--這是客戶端的部署地址,認證時會帶着這個地址,認證成功后會跳轉到這個地址-->
33             <param-value>http://127.0.0.1:8080</param-value>
34         </init-param>
35     </filter>
36     <filter-mapping>
37         <filter-name>CAS Authentication Filter</filter-name>
38         <url-pattern>/*</url-pattern>
39     </filter-mapping>
40     
41 
42     <!--Ticket校驗過濾器-->
43     <filter>
44         <filter-name>CAS Validation Filter</filter-name>
45         <filter-class>org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter</filter-class>
46         <init-param>
47             <param-name>casServerUrlPrefix</param-name>
48             <param-value>http://127.0.0.1:8090/cas/</param-value>
49         </init-param>
50         <init-param>
51             <param-name>serverName</param-name>
52            <param-value>http://127.0.0.1:8080</param-value>
53         </init-param>
54         <init-param>
55             <param-name>redirectAfterValidation</param-name>
56             <param-value>true</param-value>
57         </init-param>
58         <init-param>
59             <param-name>useSession</param-name>
60             <param-value>true</param-value>
61         </init-param>
62         <init-param>
63             <param-name>authn_method</param-name>
64             <param-value>mfa-duo</param-value>
65         </init-param>
66     </filter>
67     <filter-mapping>
68         <filter-name>CAS Validation Filter</filter-name>
69         <url-pattern>/*</url-pattern>
70     </filter-mapping>
71     
72      <!-- 該過濾器使得開發者可以通過org.jasig.cas.client.util.AssertionHolder來獲取用戶的登錄名。-->
73     <filter>
74         <filter-name>CASAssertion Thread LocalFilter</filter-name>
75         <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
76     </filter>
77     <filter-mapping>
78         <filter-name>CASAssertion Thread LocalFilter</filter-name>
79         <url-pattern>/*</url-pattern>
80     </filter-mapping>
81     
82 
83     <!-- 該過濾器負責實現HttpServletRequest請求包裝-->
84     <filter>
85         <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
86         <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
87     </filter>
88     
89      <filter-mapping>
90         <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
91         <url-pattern>/*</url-pattern>
92     </filter-mapping>
復制代碼

客戶端獲得cas返回的登錄信息的api,或者使用AssertionHolder.getAssertion().getPrincipal().getAttributes("xx");,嚴謹一點可以先判空,避免NPE

復制代碼
1 @RequestMapping("/login")
2 @ResponseBody
3 public String login() {
4     AttributePrincipal userPrincipal = (AttributePrincipal)request.getUserPrincipal();
5     return userPrincipal.getAttributes().get("loginStatus").toString();
6 }
復制代碼

下面講下退出

@RequestMapping(value = "logout")
public String logout(HttpSession session) throws InterruptedException {
session.invalidate();
  return "redirect:" + CASConstant.LOGOUT_URL;
}

我在做的時候發現退出請求總是無法觸發,后來發現是項目中有攔截器,於是加了一個excluedUrl,然后在對應的攔截器init的時候filterConfig.getInitParameter("excludedUrl");接下來dofilter時判斷如果含有該路徑重定向即可,但這樣實際相當於重定向兩次

 還有一個問題是上面redirect的地址如果需要spring靜態注入,(當然寫死可以),在applicationContext.xml中引入對應的url.properties,然后需要注入的常量類提供set方法即可,需要注意的是spring的注入是基於對象的.所以該set方法不能有static

 

2.springboot 項目接入cas.springboot內置tomcat,不再需要加入tomcat

pom.xml

<properties>
        <java.version>1.8</java.version>
        <cas.client.version>3.5.1</cas.client.version>
</properties>

<dependencies>

<!--cas的客戶端 -->
<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>${cas.client.version}</version>
</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>
</dependencies>

 

在你的application.properties加入如下配置,依然建議使用127.0.0.1

復制代碼
 1 # 監聽退出的接口,即所有接口都會進行監聽
 2 spring.cas.sign-out-filters=/*
 3 # 需要攔截的認證的接口
 4 spring.cas.auth-filters=/*
 5 spring.cas.validate-filters=/*
 6 spring.cas.request-wrapper-filters=/*
 7 spring.cas.assertion-filters=/*
 8 # 表示忽略攔截的接口,也就是不用進行攔截
 9 spring.cas.ignore-filters=/test
10 spring.cas.cas-server-login-url=http://127.0.0.1:8090/cas/login
11 spring.cas.cas-server-url-prefix=http://127.0.0.1:8090/cas/
12 spring.cas.redirect-after-validation=true
13 spring.cas.use-session=true
14 spring.cas.redirectAfterValidation=true
15 # 客戶端地址
16 spring.cas.server-name=http://127.0.0.1:8081
復制代碼

下面要注入之前在web.xml中配置的各種攔截器

代碼與參考的博客類似,做了些修改,@ConfigurationProperties依賴spring-boot-configuration-processor

@Configuration
public class CasCustomConfig {
    @Autowired
    SpringCasAutoconfig autoconfig;

    private static boolean casEnabled = true;

    public CasCustomConfig() {
    }

    @Bean
    public SpringCasAutoconfig getSpringCasAutoconfig() {
        return new SpringCasAutoconfig();
    }

    @Bean
    public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() {
        ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listener = new ServletListenerRegistrationBean<SingleSignOutHttpSessionListener>();
        listener.setEnabled(casEnabled);
        listener.setListener(new SingleSignOutHttpSessionListener());
        listener.setOrder(1);
        return listener;
    }



    /**
     * 該過濾器用於實現單點登出功能,單點退出配置,一定要放在其他filter之前
     * @return
     */
    @Bean
    public FilterRegistrationBean singleSignOutFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new SingleSignOutFilter());
        filterRegistration.setEnabled(casEnabled);
        if (autoconfig.getSignOutFilters().size() > 0) {
            filterRegistration.setUrlPatterns(autoconfig.getSignOutFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.addInitParameter("casServerUrlPrefix", autoconfig.getCasServerUrlPrefix());
        filterRegistration.addInitParameter("serverName",autoconfig.getServerName());
        filterRegistration.setOrder(1);
        return filterRegistration;
    }


    /**
     * 該過濾器負責用戶的認證工作
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean authenticationFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new AuthenticationFilter());
        filterRegistration.setEnabled(casEnabled);
        if (autoconfig.getAuthFilters().size() > 0) {
            filterRegistration.setUrlPatterns(autoconfig.getAuthFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        if (autoconfig.getIgnoreFilters() != null) {
            filterRegistration.addInitParameter("ignorePattern", autoconfig.getIgnoreFilters());
        }
        filterRegistration.addInitParameter("casServerLoginUrl", autoconfig.getCasServerLoginUrl());
        filterRegistration.addInitParameter("serverName", autoconfig.getServerName());
     //   filterRegistration.addInitParameter("useSession", autoconfig.isUseSession() ? "true" : "false");
     //   filterRegistration.addInitParameter("redirectAfterValidation", autoconfig.isRedirectAfterValidation() ? "true" : "false");
        filterRegistration.setOrder(2);
        return filterRegistration;
    }

    /**
     * 該過濾器負責對Ticket的校驗工作,使用CAS 3.0協議
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean cas30ProxyReceivingTicketValidationFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
        filterRegistration.setEnabled(casEnabled);
        if (autoconfig.getValidateFilters().size() > 0) {
            filterRegistration.setUrlPatterns(autoconfig.getValidateFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.addInitParameter("casServerUrlPrefix", autoconfig.getCasServerUrlPrefix());
        filterRegistration.addInitParameter("serverName", autoconfig.getServerName());
        filterRegistration.addInitParameter("useSession", autoconfig.isUseSession() ? "true" : "false");
        filterRegistration.addInitParameter("redirectAfterValidation", autoconfig.isRedirectAfterValidation() ? "true" : "false");
        filterRegistration.addInitParameter("authn_method", "mfa-duo");
        filterRegistration.setOrder(3);
        return filterRegistration;
    }


    /**
     * 該過濾器使得可以通過org.jasig.cas.client.util.AssertionHolder來獲取用戶的登錄名。
     * 比如AssertionHolder.getAssertion().getPrincipal().getName()。
     * 這個類把Assertion信息放在ThreadLocal變量中,這樣應用程序不在web層也能夠獲取到當前登錄信息
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean assertionThreadLocalFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new AssertionThreadLocalFilter());
        filterRegistration.setEnabled(true);
        if (autoconfig.getAssertionFilters().size() > 0) {
            filterRegistration.setUrlPatterns(autoconfig.getAssertionFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.setOrder(4);
        return filterRegistration;
    }


    @Bean
    public FilterRegistrationBean httpServletRequestWrapperFilter() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new HttpServletRequestWrapperFilter());
        filterRegistration.setEnabled(true);
        if (autoconfig.getRequestWrapperFilters().size() > 0) {
            filterRegistration.setUrlPatterns(autoconfig.getRequestWrapperFilters());
        } else {
            filterRegistration.addUrlPatterns("/*");
        }
        filterRegistration.setOrder(5);
        return filterRegistration;
    }
}
@ConfigurationProperties(prefix = "spring.cas")
public class SpringCasAutoconfig {

    static final String separator = ",";

    private String validateFilters;
    private String signOutFilters;
    private String authFilters;
    private String assertionFilters;
    private String requestWrapperFilters;
    private String ignoreFilters; //需要放行的url,多個可以使用|分隔,遵循正則

    private String casServerUrlPrefix;
    private String casServerLoginUrl;
    private String serverName;
    private boolean useSession = true;
    private boolean redirectAfterValidation = true;

    public String getIgnoreFilters() {
        return ignoreFilters;
    }

    public void setIgnoreFilters(String ignoreFilters) {
        this.ignoreFilters = ignoreFilters;
    }

    public List<String> getValidateFilters() {
        return Arrays.asList(validateFilters.split(separator));
    }

    public void setValidateFilters(String validateFilters) {
        this.validateFilters = validateFilters;
    }

    public List<String> getSignOutFilters() {
        return Arrays.asList(signOutFilters.split(separator));
    }

    public void setSignOutFilters(String signOutFilters) {
        this.signOutFilters = signOutFilters;
    }

    public List<String> getAuthFilters() {
        return Arrays.asList(authFilters.split(separator));
    }

    public void setAuthFilters(String authFilters) {
        this.authFilters = authFilters;
    }

    public List<String> getAssertionFilters() {
        return Arrays.asList(assertionFilters.split(separator));
    }

    public void setAssertionFilters(String assertionFilters) {
        this.assertionFilters = assertionFilters;
    }

    public List<String> getRequestWrapperFilters() {
        return Arrays.asList(requestWrapperFilters.split(separator));
    }

    public void setRequestWrapperFilters(String requestWrapperFilters) {
        this.requestWrapperFilters = requestWrapperFilters;
    }

    public String getCasServerUrlPrefix() {
        return casServerUrlPrefix;
    }

    public void setCasServerUrlPrefix(String casServerUrlPrefix) {
        this.casServerUrlPrefix = casServerUrlPrefix;
    }

    public String getCasServerLoginUrl() {
        return casServerLoginUrl;
    }

    public void setCasServerLoginUrl(String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        this.serverName = serverName;
    }

    public boolean isRedirectAfterValidation() {
        return redirectAfterValidation;
    }

    public void setRedirectAfterValidation(boolean redirectAfterValidation) {
        this.redirectAfterValidation = redirectAfterValidation;
    }

    public boolean isUseSession() {
        return useSession;
    }

    public void setUseSession(boolean useSession) {
        this.useSession = useSession;
    }
}

 

接下來時登錄和登出,不要直接丟個@RestController就完事了,這個主解會讓redirect失效,當成字符串轉成json返回了,最好的方式是使用@Controller,需要轉json的加上@ResponseBody

復制代碼
 1 @Controller
 2 public class LoginController {
 3 
 4     @Autowired
 5     private UserMapper userMapper;
 6 
 7     @Autowired
 8     private HttpServletRequest request;
 9 
10     @RequestMapping("/login")
11     @ResponseBody
12     public String login() {
13         AttributePrincipal userPrincipal = (AttributePrincipal)request.getUserPrincipal();
14         return userPrincipal.getAttributes().get("loginStatus").toString();
15     }
16 
17     @RequestMapping("/logout")
18     public String logout(HttpSession session) {
19         session.removeAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
20         session.invalidate();
21         return "redirect:http://127.0.0.1:8090/cas/logout?service=http://127.0.0.1:8081/spring_boot/login/";
22     }
23 
24     private void println(Object object) {
25         System.out.println(object);
26     }
27 }
復制代碼

三.驗證

 

 

 三.關於源碼

源碼看了下客戶端的攔截器與處理器,大概流程如下,在瀏覽器輸入http://127.0.0.1:8081/spring_boot/login/會被重定向到http://127.0.0.1:8090/cas/login?service=http://127.0.0.1:8081/spring_boot/login/,也就是cas的登錄頁,點擊登錄之后,cas server進行驗證,之后會進入客戶端的攔截器SingleSignOutFilter,攔截器判斷請求類型,如果是tokenrequest(帶token的請求),保存session和st,如果是登出請求,將st,session從map中移除,在destroySession中session.invalidate(),但是其他接入的客戶端並沒有銷毀session,退出時,cas只會給接入的客戶端發通知,當然從哪個客戶端發起的退出該客戶端的session會被destroy,但其他客戶端要在登出接口中session.invalidate(),

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM