使用CAS實現單點登陸功能

單點登陸

簡介

  • 單點登陸(Single Sign On),簡稱 SSO
  • 簡單來講,就是隻要一次登陸了某個子系統,就順帶登陸了其餘的子系統。
  • 其目的很簡單,就是爲了減小用戶訪問子系統的成本。

CAS服務器部署

上傳tomcat服務器壓縮到文件夾/usr/local/cas目錄下,解壓,修改tomcat文件夾名爲tomcat

mkdir /usr/local/cas
cd /usr/local/cas

修改tomcat配置文件的端口號

<server port="8010" shutdown="SHUTDOWN">

<Connector port="9100" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

關閉tomcat服務器,把CAS的war包放入tomcat的webapps目錄當中後再啓動tomcat服務器 對war包時行解壓

關閉tomcat服務器後, 刪除war包

/usr/local/cas/tomcat/bin/shutdown.sh
rm -rf /usr/local/cas/tomcat/webapps/cas.war

監聽啓動

tail -f /usr/local/cas/tomcat/logs/catalina.out

啓動tomcat服務器

/usr/local/cas/tomcat/bin/startup.sh

訪問http://192.168.1.88:9100/cas/login

輸入用戶名casuser 密碼:Mellon

CAS使用

去除https認證與設置cookie


//關閉tomcat
/usr/local/cas/tomcat/bin/shutdown.sh

//修改參數
vi /usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json
{
    "@class" : "org.apereo.cas.services.RegexRegisteredservice",
    "serviceId" : "^(https|http|imaps)://.*",
    "name" : "HTTPS and IMAPS",
    "id" : 10000001,
    "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
    "evaluationorder" : 10000
}



//添加參數
vi /usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/application.properties
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
#配置容許登出後跳轉到指定頁面
cas.logout.followServiceRedirects=true
#跳轉到指定頁面須要的參數名爲 service
cas.logout.redirectParameter=service


//重啓tomcat
/usr/local/cas/tomcat/bin/startup.sh

用戶名和密碼

數據源方法

修改Mysql遠程訪問權限

create user user_cas identified with mysql_native_password by '123456';
grant all on *.* to 'user_cas'@'%';
FLUSH PRIVILEGES;

配置用戶認證

將用戶認證配置cas.authn.accept.users=casuser::Mellon註釋掉
vi /usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/application.properties
#cas.authn.accept.users=casuser::Mellon
#設置用戶認證配置(根據實際狀況配置數據源)
cas.authn.jdbc.query[0].url=jdbc:mysql://192.168.1.2:3306/fmstore?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
#根據實際狀況配置
cas.authn.jdbc.query[0].user=user_cas
#根據實際狀況配置
cas.authn.jdbc.query[0].password=123456
cas.authn.jdbc.query[0].sql=select * from tb_user where username= ?
cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].fieldExpired=expired
#cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5

自定義登陸頁面

添加主題名稱

  • 在/usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/service/HTTPSandIMAPS-10000001.json中添加"theme" : "mypage"

放入靜態資源文件

  • 把圖片放到static/images當中
  • js放到static/js當中
  • css放到static/css當中

建立mypage.properties文件

​ 在/usr/local/cas/tomcat/webapps/cas/WEB-INF/classes目錄當中建立mypage.properties文件html

#原cas默認的css樣式,若是更改了,某些頁面樣式將丟失

cas.standard.css.file=/css/cas.css
#本身的樣式
cas.page.login.css=/css/pages-login.css
cas.webbase.css=/css/webbase.css

cas.jquery.easing.min.js=/js/jquery.easing.min.js
cas.jquery.min.js=/js/jquery.min.js
cas.jquery.placeholder.min.js=/js/jquery.placeholder.min.js
cas.login.js=/js/login.js
cas.sui.min.js=/js/sui.min.js

cas.qq.png = /images/img/qq.png
cas.weixin.png = /images/img/weixin.png

在/usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/templates中建立文件夾mypage

把頁面放進去

在application.properties當中添加主題

  • cas.theme.defaultThemeName=mypage

用戶服務添加CAS

引入pom文件

<!--spring-security-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
</dependency>
<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10</version>
</dependency>

在web.xml當中添加過濾器

<!--SpringSecurity-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/spring-security.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>

  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

添加配置文件spring/spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <!-- 放行靜態資源-->
    <http pattern="/css/**" security="none"></http>
    <http pattern="/img/**" security="none"></http>
    <http pattern="/js/**" security="none"></http>
    <http pattern="/plugins/**" security="none"></http>

    <!--放行一些請求-->
    <http pattern="/register.html" security="none"></http>
    <http pattern="/user/add.do" security="none"></http>
    <http pattern="/user/sendCode.do" security="none"></http>

    <!--   entry-point-ref  入口點引用 -->
    <http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
        <!--配置攔截器, 攔截全部請求, 應該具備ROLE_USER的權限才能夠訪問咱們系統-->
        <intercept-url pattern="/**" access="ROLE_USER"/>
        <csrf disabled="true"/>
        <!-- custom-filter爲過濾器,
                position 表示將過濾器放在指定的位置上,
                before表示放在指定位置以前,
                after表示放在指定的位置以後
        -->
        <custom-filter ref="casAuthenticationFilter"  position="CAS_FILTER" />
        <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
        <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
    </http>

    <!-- CAS入口點 開始 -->
    <beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
        <!-- 單點登陸服務器登陸URL -->
        <beans:property name="loginUrl" value="http://192.168.1.88:9100/cas/login"/>
        <beans:property name="serviceProperties" ref="serviceProperties"/>
    </beans:bean>
    <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
        <!--service 配置自身工程的根地址+/login/cas   -->
        <beans:property name="service" value="http://127.0.0.1:8086/login/cas"/>
    </beans:bean>

    <!-- 認證過濾器 開始 -->
    <beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
        <beans:property name="authenticationManager" ref="authenticationManager"/>
    </beans:bean>
    <!-- 認證管理器 -->
    <authentication-manager alias="authenticationManager">
        <authentication-provider  ref="casAuthenticationProvider"></authentication-provider>
    </authentication-manager>
    <!-- 認證提供者 -->
    <beans:bean id="casAuthenticationProvider"
                class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
        <beans:property name="authenticationUserDetailsService">
            <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
                <beans:constructor-arg ref="userDetailsService" />
            </beans:bean>
        </beans:property>
        <beans:property name="serviceProperties" ref="serviceProperties"/>
        <!-- ticketValidator 爲票據驗證器 -->
        <beans:property name="ticketValidator">
            <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <beans:constructor-arg index="0" value="http://192.168.1.88:9100/cas"/>
            </beans:bean>
        </beans:property>
        <beans:property name="key" value="an_id_for_this_auth_provider_only"/>
    </beans:bean>
    <!-- 認證類 -->
    <beans:bean id="userDetailsService" class="com.itxk.core.service.UserDetailServiceImpl"/>


    <!-- 單點登出  開始  -->
    <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
    <!-- 通過此配置,當用戶在地址欄輸入本地工程 /logout/cas  -->
    <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <beans:constructor-arg value="http://192.168.1.88:9100/cas/logout?service=http://127.0.0.1:8086"/>
        <beans:constructor-arg>
            <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
        </beans:constructor-arg>
        <beans:property name="filterProcessesUrl" value="/logout/cas"/>
    </beans:bean>

</beans:beans>

建立認證類

/**
 * 自定義認證類:
 * 在以前這裏負責用戶名密碼的校驗工做, 並給給當前用戶賦予對應的訪問權限
 * 如今cas和springSecurity集成, 集成後, 用戶名密碼的校驗工做交給cas完成, 因此可以進入到
 * 這裏類的方法中的都是已經成功認證的用戶, 這裏只須要給登陸過的用戶賦予對應的訪問權限就能夠
 */
public class UserDetailServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //建立權限集合
        List<GrantedAuthority> authorityList = new ArrayList<>();
        //向權限集合中加入訪問權限
        authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(username, "", authorityList);
    }
}

前端工程添加CAS

在web.xml當中添加過濾器

<!--SpringSecurity-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/spring-security.xml</param-value>
  </context-param>
  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>

  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

添加配置文件spring/spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <!-- 放行靜態資源-->
    <http pattern="/css/**" security="none"></http>
    <http pattern="/img/**" security="none"></http>
    <http pattern="/js/**" security="none"></http>
    <http pattern="/plugins/**" security="none"></http>

    <!--放行一些請求-->
    <http pattern="/index.html" security="none"></http>
    <http pattern="/search.html" security="none"></http>
    <http pattern="/cart.html" security="none"></http>

    <!--   entry-point-ref  入口點引用 -->
    <http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
        <!-- 匿名角色 IS_AUTHENTICATED_ANONYMOUSLY -->
        <intercept-url pattern="/cart/*.do" access="IS_AUTHENTICATED_ANONYMOUSLY"></intercept-url>
        <intercept-url pattern="/itemsearch/*.do" access="IS_AUTHENTICATED_ANONYMOUSLY"></intercept-url>
        <!--配置攔截器, 攔截全部請求, 應該具備ROLE_USER的權限才能夠訪問咱們系統-->
        <intercept-url pattern="/**" access="ROLE_USER"/>
        <csrf disabled="true"/>
        <!-- custom-filter爲過濾器,
        position 表示將過濾器放在指定的位置上,
        before表示放在指定位置以前  ,
        after表示放在指定的位置以後  -->
        <custom-filter ref="casAuthenticationFilter"  position="CAS_FILTER" />
        <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
        <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
    </http>

    <!-- CAS入口點 開始 -->
    <beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
        <!-- 單點登陸服務器登陸URL -->
        <beans:property name="loginUrl" value="http://192.168.1.88:9100/cas/login"/>
        <beans:property name="serviceProperties" ref="serviceProperties"/>
    </beans:bean>
    <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
        <!--service 配置自身工程的根地址+/login/cas   -->
        <beans:property name="service" value="http://127.0.0.1:8083/login/cas"/>
    </beans:bean>

    <!-- 認證過濾器 開始 -->
    <beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
        <beans:property name="authenticationManager" ref="authenticationManager"/>
    </beans:bean>
    <!-- 認證管理器 -->
    <authentication-manager alias="authenticationManager">
        <authentication-provider  ref="casAuthenticationProvider"></authentication-provider>
    </authentication-manager>
    <!-- 認證提供者 -->
    <beans:bean id="casAuthenticationProvider"
                class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
        <beans:property name="authenticationUserDetailsService">
            <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
                <beans:constructor-arg ref="userDetailsService" />
            </beans:bean>
        </beans:property>
        <beans:property name="serviceProperties" ref="serviceProperties"/>
        <!-- ticketValidator 爲票據驗證器 -->
        <beans:property name="ticketValidator">
            <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <beans:constructor-arg index="0" value="http://192.168.1.88:9100/cas"/>
            </beans:bean>
        </beans:property>
        <beans:property name="key" value="an_id_for_this_auth_provider_only"/>
    </beans:bean>
    <!-- 認證類 -->
    <beans:bean id="userDetailsService" class="com.itxk.core.service.UserDetailServiceImpl"/>


    <!-- 單點登出  開始  -->
    <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
    <!-- 通過此配置,當用戶在地址欄輸入本地工程 /logout/cas  -->
    <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <beans:constructor-arg value="http://192.168.1.88:9100/cas/logout?service=http://127.0.0.1:8083"/>
        <beans:constructor-arg>
            <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
        </beans:constructor-arg>
        <beans:property name="filterProcessesUrl" value="/logout/cas"/>
    </beans:bean>

</beans:beans>

建立認證類

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.ArrayList;
import java.util.List;
/**
 * 自定義認證類:
 * 在以前這裏負責用戶名密碼的校驗工做, 並給給當前用戶賦予對應的訪問權限
 * 如今cas和springSecurity集成, 集成後, 用戶名密碼的校驗工做交給cas完成, 因此可以進入到
 * 這裏類的方法中的都是已經成功認證的用戶, 這裏只須要給登陸過的用戶賦予對應的訪問權限就能夠
 */
public class UserDetailServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //建立權限集合
        List<GrantedAuthority> authorityList = new ArrayList<>();
        //向權限集合中加入訪問權限
        authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(username, "", authorityList);
    }
}

相關文章
相關標籤/搜索