单点登录前奏

1.单点登录认识及环境搭建

1.单点登录(Single Sign On)

简称SSO。定义是在多个应用系统中,用户只需要登录一次就可以访问所有互相信任的应用系统。一般用于公司内部产品或某个产品系列的所有子系统中。比如公司的oa系统、icode系统、邮箱系统等等;再比如豆瓣系列的豆瓣FM、豆瓣读书、豆瓣电影、豆瓣日记等等,只需要登录一次,访问其他系统就没有必要再登录了。

背景:

  1. 公司内部现存系统较多,各个系统分布在不同服务器上,用户使用过程中需要多次重复登陆,使用麻烦
  2. 各系统实现技术不相同,但都是基于Web,客户端语言可以是非java语言
  3. 各个系统用户数据不尽相同,缺乏统一管理,用户需要维护多套密码,管理员也需要维护多套用户信息
  4. SSO服务端仅负责认证,权限管理可以交由各系统自己完成

想了解更多单点登录原理进入单点登录原理与简单实现【转】

2.创建证书

证书是单点登录认证系统中很重要的一把钥匙,客户端与服务器的交互安全靠的就是证书;本次由于是演示所以就用JDK自带的keytool工具生成证书;如果以后真正在产品环境中使用肯定要去证书提供商去购买,证书认证一般都是由VeriSign认证,中文网站:http://www.verisign.com/cn/

JDK自带的keytool工具生成证书过程如下:
注意:以管理员的身份打开cmd

1.生成证书

    keytool -genkey -alias tomcat -keyalg RSA -keystore D:/software/keys/keystore

需要设置密码:这里设置成123456

注:www.wp.com是域名,以后配置CAS客户端时需要用到且必须一致。

2.导出证书

    keytool -export -trustcacerts -alias tomcat -file D:/software/keys/tomcat.cer -keystore D:/software/keys/keystore

3.将证书导入JDK信任库

    keytool -import -trustcacerts -alias tomcat -file D:/software/keys/tomcat.cer -keystore "D:\Program Files\Java\jdk1.8.0_131\jre\lib\security/cacerts"

密码是默认的changeit,而不是之前设置的密码。
https://www.cnblogs.com/hamfy/archive/2012/07/31/2616805.html

4.其他命令
查看证书列表

    keytool -list -v -keystore "D:\Program Files\Java\jdk1.8.0_131\jre\lib\security/cacerts"

删除证书

    keytool -delete -trustcacerts -alias tomcat -keystore  "D:\Program Files\Java\jdk1.8.0_131\jre\lib\security/cacerts"

3.Tomcat配置

启用We服务器的SSL,也就是HTTPS加密协议。
配置tomcat的server.xml

    <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="150" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" 
			   keystoreFile="D:/software/keys/keystore"
			   keystorePass="123456" />

keystoreFile:创建的key存放的位置
keystorePass:创建证书时的密码

4. 配置www.wp.com域名指向本地


编辑host文件,添加下面内容,保存即可。

5.启动tomcat,浏览器输入https:www.wp.com:8443/ 是否正常访问

如果正常访问到该地址,那么表示配置成功。

若还不能访问,则在server.xml中将下面内容注释掉。

 <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />

2.单点登录实现

1.服务端cas-server

下载cas-server-3.5.0-release
https://www.apereo.org/projects/cas/news
下载完成后解压,并改名为cas,复制到tomcat/webapp目录下,启动tomcat,并访问地址:
https:www.wp.com:8443/cas/login 用户名/密码:admin/admin点击登录。cas默认的验证规则是只要用户名和密码相同就可以通过。

2.客户端

新建springboot web项目,使用maven管理jar包,pom.xml文件加入包

<dependencies>
    <!--web应用基本环境配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.jasig.cas.client</groupId>
        <artifactId>cas-client-core</artifactId>
        <version>${cas.version}</version>
    </dependency>
</dependencies>

application.yaml配置文件

server:
  port: 8081
spring:
  profiles:
    active: dev

---
# 开发环境配置
spring:
  profiles: dev
cas:
  casServerUrlPrefix: https://www.wp.com:8443/cas
  casServerLoginUrl: https://www.wp.com:8443/cas/login
  serverName: http://localhost:8081
  encoding: UTF-8
  urlPatterns: /*

新建一个配置类,配置单点登录客户端过滤器CasConfig

package com.baidu.bpit.uuap.conf;

import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
@ConfigurationProperties(prefix = "cas", ignoreUnknownFields = true)
public class CasConfig {

    private String casServerUrlPrefix;  // sso验证地址
    private String casServerLoginUrl;   // sso登录地址
    private String serverName;  // sso验证或者登陆后返回的地址
    private String urlPatterns; // sso过滤匹配
    private String encoding;    // sso编码

    /**
     * 注册CAS SSO 验证用户是否存在Filter
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean cas20ValidateRegistration() {
        FilterRegistrationBean cas20 = new FilterRegistrationBean();
        cas20.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
        cas20.addUrlPatterns(urlPatterns);
        cas20.addInitParameter("casServerUrlPrefix", casServerUrlPrefix);
        cas20.addInitParameter("serverName", serverName);
        cas20.addInitParameter("encoding", encoding);
        return cas20;
    }

    /**
     * 注册CAS SSO 用户登录Filter
     *
     * @return
     */
    @Bean
    public FilterRegistrationBean cas20LoginRegistration() {
        FilterRegistrationBean cas20 = new FilterRegistrationBean();
        cas20.setFilter(new AuthenticationFilter());
        cas20.addUrlPatterns(urlPatterns);
        cas20.addInitParameter("casServerLoginUrl", casServerLoginUrl);
        cas20.addInitParameter("serverName", serverName);
        cas20.addInitParameter("encoding", encoding);
        return cas20;
    }

    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 String getUrlPatterns() {
        return urlPatterns;
    }

    public void setUrlPatterns(String urlPatterns) {
        this.urlPatterns = urlPatterns;
    }

    public String getEncoding() {
        return encoding;
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }
}

创建访问首页controller,获取到session中的用户心

@ResponseBody
@RequestMapping("/demo")
public String getLoginUser(HttpServletRequest httpServletRequest) {

    // 获取session里面的用户信息
    HttpSession session = httpServletRequest.getSession();
    String userName = "-";
    if (null != session) {
        Assertion assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
        if (null != assertion) {
            userName = assertion.getPrincipal().getName();
        }
    }
    logger.info("get current user: {}", userName);
    return "Welcome you : " + "demo2";
}

然后启动项目,浏览器访问http://localhost:8081/demo

输入用户名和密码,回车

3.单点登录流程解析

1.理解几个概念

1、TGC (Ticket-granting cookie):存放用户身份认证凭证的cookie,在浏览器和SSO server之间通讯时使用,并且只能用HTTPS传输,说白了,就是你登陆SSO系统后,SSO server会在你的浏览器Cookie里面植入一段Cookie,这样在Cookie的有效期内你访问任何接入SSO的系统都不需要再次输入密码,只需要校验该Cookie的有效性,因为基于HTTPS传输,所以说能够保证传输过程中的安全性,但是注意保管你自己电脑的不被别人窃取该信息,不然别人就能伪造以你的身份登陆。也称为全局会话。
查看你的cookie就能看到该信息,就是叫CASTGC的cookie,可以看到该cookie的有效期为浏览会话结束时。可以设置一个月时间。

2、TGT(Ticket Granting ticket):票据授权票据。该票据存在Server端,其实TGC是TGT的id而已,在你完成认证后服务端生成TGT存起来,然后把TGT的id下发给用户保存在cookie中,这样下次用户来就可以通过TGC找到TGT,然后校验该TGT的一系列信息,认证通过就可以免密码登录。

3、ST(Service Ticket):这个ST便是浏览器中的ticket参数,sso认证通过后携带该参数让用户重定向到下游系统,下游系统拿到该参数便去sso server验证该参数的有效性,通过验证便可以让用户进入下游系统。

怎样才能保证ticket参数的安全性呢?

(1)、https传输。
(2)、ticket只能使用一次,不管认证成功或者失败
(3)、ticket有有效期,默认5分钟
(4)、ticket是随机生成的,具有不可伪造性

2.流程图及流程解析

单点登录常用的操作有三个:login、serviceValidate、logout,最复杂的就是login接口。
单点登录服务端使用spring-webflow技术实现了单点登录的login流程,整理login逻辑流程图如下:

1.用户访问系统1的受保护资源,系统1发现用户未登录,跳转到SSO认证中心,并将用户访问系统1的地址作为参数传递过去
2.SSO认证中心接收到请求后,走login-webflow流程,配置信息在cas-servlet.xml中,

首先会进入初始化操作流程 initialFlowSetupAction,执行初始化数据等操作。
然后响应给浏览器casLoginView.jsp页面
用户提交用户名和密码后去SSO认证中心校验
校验success后,去生成全局会话,将id保存到cookie中;sendTicketGrantingTicketAction
然后去创建系统1的授权令牌ticket;generateServiceTicketAction
最后,SSO以之前传过来的参数地址为地址并且拼接ticket参数,重定向到系统1中。

系统1拿到ticket参数后,去SSO认证中心校验ticket的有效性,有效,再重定向到系统1,系统1会建立用户和系统1的局部会话,证明用户是否登录系统1,最后返回系统1访问的资源。
注意:全局会话TGT的id TGC保存在cookie中CASTGC;若该cookie存在,那么其他客户端也是登录状态,不会再验证用户密码信息了,然后生成新的ticket。
ticket完成客户端service在SSO认证中心的校验。

cas client、cas server、浏览器之间的流程

在图中第3步用户认证成功后,cas server会生成Ticket Granting Ticket(票据授权票据,简称TGT),同时将TGT值以CASTGC为名保存到浏览器的cookie中,之后生成Service Ticket(服务票据,简称ST)并缓存,在第4步时将ST通过浏览器重定向的URL传给cas client。

当客户访问另一个cas client时,同样会被重定向到cas server,而此时我们并不希望再次让用户输入用户密码登陆,名为CASTGC的cookie这时就体现出作用来了,cas server发现存在名为CASTGC的cookie就将其值在已保存的TGT中查找,若存在,则说明已存在合法的TGT,cas server就根据该TGT生成新的ST,接下来的流程就和以前一样了。

当然,在实际产品中不能直接使用从官网下载下来的cas-server,需要对其进行相关的改造,比如登陆页面需要改成体现产品特色的,添加验证码,用户登录信息验证方案,TGT、serviceTicket缓存方案和持久化到数据库、加入域登录等等诸多地方。那么下一篇单点登录cas-server改造是很有必要了解的。

posted @ 2018-03-06 19:47  展云  阅读(807)  评论(0编辑  收藏  举报