SSO单点登录------系统搭建

3 搭建过程

3.1 搭建环境

  本文搭建过程以cas-server-4.2.7,cas-client-3.4.1为例。构建环境为Idea 2017.1, Gradle3.5。

3.2 服务端搭建

3.2.1 安装配置Gradle
3.2.1.1 下载安装gradle

cas-server-4.2.7所使用构建工具为Gradle,所以需要先安装好Gradle。

3.2.1.2 配置gradle本地仓库

  配置环境变量GRADLE_USER_HOME,并指向你的一个本地目录,用来保存Gradle下载的依赖包。


image.png
3.2.1.3 远程仓库配置

  一般Gradle、maven从中央仓库mavenCentral下载依赖包,但是在国内下载速度巨慢,我们只能使用国内的镜像。 我们可以在cas-server项目的根目录下,对build.gradle做如下配置:

repositories {
    maven {
        url 'http://maven.aliyun.com/nexus/content/groups/public/'
    }
    mavenCentral()
}
3.2.2构建项目

  将下载好的cas-4.2.7解压到cas-server目录,在Idea中打开项目。


image.png

选择使用本地Gradle版本,接下来就是漫长的等待项目加载过程。

3.2.3 实现自定义用户登录

  cas原始登陆验证十分简单,只需在cas.properties中配置accept.authn.users字段即可,初始登录名密码为casuser,Mellon。为了实现真正的登陆验证,我们需要实现自己的登录验证流程。

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;  //验证账号密码的实现类
    @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则验证通过
            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()方法中实现账号和密码的验证逻辑, 我这里是引入并调用了公司原有系统的密码账号验证类SdfPasswordValidatorImpl。
  在类SdfPasswordValidatorImpl 的authenticate()方法中,用户自定义账号密码验证逻辑, 返回true则验证通过。

3.2.3.2配置自定义验证处理类:

在deployerConfigContext中将
···
<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />
···
替换为
···
<alias name="sunlandsAuthenticationHandler"
alias="primaryAuthenticationHandler" />
···
3.2.4 自定义登录页面

3.2.5 实现ST与TGC存入redis

3.2.6 实现单点登录跳转地址限制

  从应用系统跳转到cas登录页面时,会在登录地址后附带应用系统的地址,如
<u>http://</u><u>www.casserver.com</u><u>/serviceValidate?service=http://</u><u>www.client1.com</u><u>/</u><u>index</u>,登陆之后就会跳转回应用系统页面<u>http://</u><u>www.client1.com</u><u>/</u><u>index。</u>
Service地址在cas服务器后端并没有进行验证, service为任意网址均能跳转。在遭遇网络劫持或其他情况下,可能会从cas登录页面跳转到非公司网址,造成安全隐患,因此需要对service后的跳转地址进行验证。在service地址不符合要求的情况下,跳转到登陆成功的主页,而不是service地址。

3.2.6.1 修改cas.properties

  在cas.properties文件中新增允许跳转的service地址变量,用 “| ”符号分隔,支持正则形式。
···
//# 允许跳转的service地址
service.allowed=xxx.cn|yyy.com
···

3.2.6.2 修改GenerateServiceTicketAction

修改GenerateServiceTicketAction类:
添加私有属性 serviceAllowed以及是否可以跳转标记常量

 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");
                }
            }
           //新增结束
            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"/>
<!--  新增结束     -->
        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
        <transition on="error" to="initializeLogin"/>
        <transition on="gateway" to="gatewayServicesManagementCheck"/>
    </action-state>

3.3 应用系统端搭建

3.3.1 应用系统接入单点登录配置

  若对应用系统端没有个性化需求, 应用系统直接引用cas-client jar包。并进行配置即可实现单点登录。

3.3.1.1 引入cas-client统一登录jar包

maven项目引入:
  在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
  在项目web.xml 中引入以下代码。引入内容应在系统原有字符串过滤filter之后,以免造成系统原有字符串过滤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>
<!-- 单点登出 -->
<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>

<!--用户认证过滤器-->
<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的校验过滤器 -->
<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>

添加文件
  在项目/src/main/resources/下添加cas-client.properties及casFilter.xml文件.
cas-client.properties

#线上cas服务器

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

#测试cas服务器, 不验证263密码, 任何密码可登陆

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

#本应用的服务地址,需修改  serverName=http://172.16.103.226:9000

#设置不被不需要被拦截的地址,支持正则  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>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,510评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,680评论 6 342
  • 【环境说明】:本文演示过程在同一个机器上的(也可以在三台实体机器或者三个的虚拟机上),环境如下: windows7...
    黄海佳阅读 8,761评论 2 15
  • 第二天的阳光依旧明媚,好像不知道生气的孩子,嘟着嘴吹着泡泡。 一宵今天没来上课,但是给怀纣打了电话,说是薛桀陪她出...
    许在南阅读 147评论 0 0
  • 孩子为什么要上学 你是怎么生活过来的 小 海豹的故事 你想成为什么样的人 爱抄写书的孩子 孩子的战斗方式 新加坡的...
    煜烟阅读 376评论 0 0