SSO單點(diǎn)登錄------系統(tǒng)搭建

3 搭建過程

3.1 搭建環(huán)境

??本文搭建過程以cas-server-4.2.7,cas-client-3.4.1為例。構(gòu)建環(huán)境為Idea 2017.1, Gradle3.5。

3.2 服務(wù)端搭建

3.2.1 安裝配置Gradle
3.2.1.1 下載安裝gradle

cas-server-4.2.7所使用構(gòu)建工具為Gradle,所以需要先安裝好Gradle。

3.2.1.2 配置gradle本地倉庫

??配置環(huán)境變量GRADLE_USER_HOME,并指向你的一個本地目錄,用來保存Gradle下載的依賴包。


image.png
3.2.1.3 遠(yuǎn)程倉庫配置

??一般Gradle、maven從中央倉庫mavenCentral下載依賴包,但是在國內(nèi)下載速度巨慢,我們只能使用國內(nèi)的鏡像。 我們可以在cas-server項(xiàng)目的根目錄下,對build.gradle做如下配置:

repositories {
    maven {
        url 'http://maven.aliyun.com/nexus/content/groups/public/'
    }
    mavenCentral()
}
3.2.2構(gòu)建項(xiàng)目

??將下載好的cas-4.2.7解壓到cas-server目錄,在Idea中打開項(xiàng)目。


image.png

選擇使用本地Gradle版本,接下來就是漫長的等待項(xiàng)目加載過程。

3.2.3 實(shí)現(xiàn)自定義用戶登錄

??cas原始登陸驗(yàn)證十分簡單,只需在cas.properties中配置accept.authn.users字段即可,初始登錄名密碼為casuser,Mellon。為了實(shí)現(xiàn)真正的登陸驗(yàn)證,我們需要實(shí)現(xiàn)自己的登錄驗(yàn)證流程。

3.2.3.1 建立自定義賬號密碼處理類:

??在cas-server-core-authentication模塊,org.jasig.cas.authentication包下新建自定義賬號密碼處理類。以建立sunlandsAuthenticationHandler為例:

 @Component("sunlandsAuthenticationHandler")public class SunlandsAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
    /** The default separator in the file. */
    private static final String DEFAULT_SEPARATOR = "::";
    private static final Pattern USERS_PASSWORDS_SPLITTER_PATTERN = Pattern.compile(DEFAULT_SEPARATOR);

    /** The list of users we will accept. */
    private Map<String, String> users;

    @Value("${accept.authn.users:}")
    private String acceptedUsers;

    @Autowired
    private SdfPasswordValidatorImpl sdfPasswordValidatorImpl;  //驗(yàn)證賬號密碼的實(shí)現(xiàn)類
    @Override
    protected HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential) throws GeneralSecurityException, PreventedException {
        if (users == null || users.isEmpty()) {
            throw new FailedLoginException("No user can be accepted because none is defined");
        }
        final String username = credential.getUsername();
        final String password = credential.getPassword();

        try {
            boolean isValidUser = sdfPasswordValidatorImpl.authenticate(username,
                    password);  //返回true則驗(yàn)證通過
            if (!isValidUser) {
                throw new FailedLoginException("Password does not match value on record.");
            }
        } catch (Exception e) {
            throw new FailedLoginException("login failed.");
        }
        return createHandlerResult(credential, this.principalFactory.createPrincipal(username), null);

    }

    /**
     * @param users The users to set.
     */
    public final void setUsers(@NotNull final Map<String, String> users) {
        this.users = Collections.unmodifiableMap(users);
    }
    @PostConstruct
    public void init() {
        if (StringUtils.isNotBlank(this.acceptedUsers) && this.users == null) {
            final Set<String> usersPasswords = org.springframework.util.StringUtils.commaDelimitedListToSet(this.acceptedUsers);
            final Map<String, String> parsedUsers = new HashMap<>();
            for (final String usersPassword : usersPasswords) {
                final String[] splitArray = USERS_PASSWORDS_SPLITTER_PATTERN.split(usersPassword);
                parsedUsers.put(splitArray[0], splitArray[1]);
            }
            setUsers(parsedUsers);
        }
    }
}

??在authenticateUsernamePasswordInternal()方法中實(shí)現(xiàn)賬號和密碼的驗(yàn)證邏輯, 我這里是引入并調(diào)用了公司原有系統(tǒng)的密碼賬號驗(yàn)證類SdfPasswordValidatorImpl。
??在類SdfPasswordValidatorImpl 的authenticate()方法中,用戶自定義賬號密碼驗(yàn)證邏輯, 返回true則驗(yàn)證通過。

3.2.3.2配置自定義驗(yàn)證處理類:

在deployerConfigContext中將
···
<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />
···
替換為
···
<alias name="sunlandsAuthenticationHandler"
alias="primaryAuthenticationHandler" />
···
3.2.4 自定義登錄頁面

3.2.5 實(shí)現(xiàn)ST與TGC存入redis

3.2.6 實(shí)現(xiàn)單點(diǎn)登錄跳轉(zhuǎn)地址限制

??從應(yīng)用系統(tǒng)跳轉(zhuǎn)到cas登錄頁面時,會在登錄地址后附帶應(yīng)用系統(tǒng)的地址,如
<u>http://</u><u>www.casserver.com</u><u>/serviceValidate?service=http://</u><u>www.client1.com</u><u>/</u><u>index</u>,登陸之后就會跳轉(zhuǎn)回應(yīng)用系統(tǒng)頁面<u>http://</u><u>www.client1.com</u><u>/</u><u>index。</u>
Service地址在cas服務(wù)器后端并沒有進(jìn)行驗(yàn)證, service為任意網(wǎng)址均能跳轉(zhuǎn)。在遭遇網(wǎng)絡(luò)劫持或其他情況下,可能會從cas登錄頁面跳轉(zhuǎn)到非公司網(wǎng)址,造成安全隱患,因此需要對service后的跳轉(zhuǎn)地址進(jìn)行驗(yàn)證。在service地址不符合要求的情況下,跳轉(zhuǎn)到登陸成功的主頁,而不是service地址。

3.2.6.1 修改cas.properties

??在cas.properties文件中新增允許跳轉(zhuǎn)的service地址變量,用 “| ”符號分隔,支持正則形式。
···
//# 允許跳轉(zhuǎn)的service地址
service.allowed=xxx.cn|yyy.com
···

3.2.6.2 修改GenerateServiceTicketAction

修改GenerateServiceTicketAction類:
添加私有屬性 serviceAllowed以及是否可以跳轉(zhuǎn)標(biāo)記常量

 private static final Integer REGISTERED_SERVICE_FLAG = 1;
    private static final Integer UNREGISTERED_SERVICE_FLAG = 0; 
@Value("${service.allowed}")
    private String serviceAllowed;

修改doExecute方法

 final ServiceTicket serviceTicketId = this.centralAuthenticationService
                    .grantServiceTicket(ticketGrantingTicket, service, authenticationContext);
            WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
// 新增開始
            //判斷service是否注冊
            Integer serviceFlag = UNREGISTERED_SERVICE_FLAG;
            if(serviceAllowed !=null && !serviceAllowed.isEmpty()){
                try {
                    String url = new URL(service.toString()).getHost();
                    String[] services = serviceAllowed.split("\\|");
for(String ser : services){
                        Pattern pattern = Pattern.compile(ser);
                        if (!pattern.matcher(url).matches()) continue;
                        serviceFlag = REGISTERED_SERVICE_FLAG;
                    }
                    if(serviceFlag==0) return new Event(this,"unregisteredService");
                } catch (Exception e) {
                    logger.error("識別service是否注冊失敗",e);
                    return new Event(this,"unregisteredService");
                }
            }
           //新增結(jié)束
            return success();
3.6.2.3修改log-webflow.xml
 <action-state id="generateServiceTicket">
        <evaluate expression="generateServiceTicketAction"/>
        <transition on="success" to="warn"/>
<!--  新增開始     -->
        <transition on="unregisteredService" to="viewGenericLoginSuccess"/>
<!--  新增結(jié)束     -->
        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
        <transition on="error" to="initializeLogin"/>
        <transition on="gateway" to="gatewayServicesManagementCheck"/>
    </action-state>

3.3 應(yīng)用系統(tǒng)端搭建

3.3.1 應(yīng)用系統(tǒng)接入單點(diǎn)登錄配置

??若對應(yīng)用系統(tǒng)端沒有個性化需求, 應(yīng)用系統(tǒng)直接引用cas-client jar包。并進(jìn)行配置即可實(shí)現(xiàn)單點(diǎn)登錄。

3.3.1.1 引入cas-client統(tǒng)一登錄jar包

maven項(xiàng)目引入:
??在pom文件中添加以下代碼

<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <version>3.4.1</version>
</dependency>
<!--如果原本沒有引入slf4j, 還需引入slf4j-->
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.7.10</version>
</dependency>
3.3.1.2 配置文件

修改web.xml
??在項(xiàng)目web.xml 中引入以下代碼。引入內(nèi)容應(yīng)在系統(tǒng)原有字符串過濾filter之后,以免造成系統(tǒng)原有字符串過濾filter不能使用。

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:casFilter.xml</param-value>
</context-param>

<listener>
    <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<!-- 單點(diǎn)登出 -->
<filter>
    <filter-name>CAS Single Sign Out Filter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>targetBeanName</param-name>
        <param-value>singleSignOutFilter</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CAS Single Sign Out Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!--用戶認(rèn)證過濾器-->
<filter>
    <filter-name>CAS Authentication Filter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  <init-param>
    <param-name>targetFilterLifecycle</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
        <param-name>targetBeanName</param-name>
        <param-value>authenticationFilter</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CAS Authentication Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Ticket的校驗(yàn)過濾器 -->
<filter>
    <filter-name>CAS Validation Filter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>targetBeanName</param-name>
        <param-value>ticketValidationFilter</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CAS Validation Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
    <filter-class>
        org.jasig.cas.client.util.HttpServletRequestWrapperFilter
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
    <filter-name>CAS Assertion Thread Local Filter</filter-name>
    <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CAS Assertion Thread Local Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

添加文件
??在項(xiàng)目/src/main/resources/下添加cas-client.properties及casFilter.xml文件.
cas-client.properties

#線上cas服務(wù)器

#casServer=http://login.xxx.com

#測試cas服務(wù)器, 不驗(yàn)證263密碼, 任何密碼可登陸

casServer=[http://172.16.116.136:9091/cas](http://172.16.116.136:9091/cas/login)

#本應(yīng)用的服務(wù)地址,需修改  serverName=http://172.16.103.226:9000

#設(shè)置不被不需要被攔截的地址,支持正則  ignoreAddress=/a.do|/*.html

casFilter.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:/cas-client.properties"/>

    <bean name="singleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter">
        <property name="casServerUrlPrefix" value="${casServer}"/>
    </bean>

    <bean name="authenticationFilter"
          class="org.jasig.cas.client.authentication.AuthenticationFilter">
        <property name="casServerLoginUrl" value="${casServer}/login"/>
        <property name="serverName" value="${serverName}"/>
        <property name="ignorePattern" value="${ignoreAddress}"/>
    </bean>

    <bean name="ticketValidationFilter"
          class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter">
        <property name="serverName" value="${serverName}" />
      <property name="casServerUrlPrefix" value="${casServer}"/>
    </bean>
</beans>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,255評論 6 342
  • 【環(huán)境說明】:本文演示過程在同一個機(jī)器上的(也可以在三臺實(shí)體機(jī)器或者三個的虛擬機(jī)上),環(huán)境如下: windows7...
    黃海佳閱讀 9,057評論 2 15
  • 第二天的陽光依舊明媚,好像不知道生氣的孩子,嘟著嘴吹著泡泡。 一宵今天沒來上課,但是給懷紂打了電話,說是薛桀陪她出...
    許在南閱讀 182評論 0 0
  • 孩子為什么要上學(xué) 你是怎么生活過來的 小 海豹的故事 你想成為什么樣的人 愛抄寫書的孩子 孩子的戰(zhàn)斗方式 新加坡的...
    煜煙閱讀 460評論 0 0

友情鏈接更多精彩內(nèi)容