全面介绍SSO(单点登陆)

SSO英文全称Single SignOn,单点登陆。SSO是在多个应用系统中,用户只须要登陆一次就能够访问全部相互信任的应用系统。它包括能够将此次主要的登陆映射到其余应用中用于同一个用户的登陆的机制。它是目前比较流行的企业业务整合的解决方案之一。

1、 实现机制

当用户第一次访问应用系统1的时候,由于尚未登陆,会被引导到认证系统中进行登陆;根据用户提供的登陆信息,认证系统进行身份校验,若是经过校验,应该返回给用户一个认证的凭据--ticket;用户再访问别的应用的时候就会将这个ticket带上,做为本身认证的凭据,应用系统接受到请求以后会把ticket送到认证系统进行校验,检查ticket的合法性。若是经过校验,用户就能够在不用再次登陆的状况下访问应用系统2和应用系统3了。前端

2、使用SSO的好处

  • 方便用户 用户使用应用系统时,可以一次登陆,屡次使用。用户再也不须要每次输入用户名称和用户密码,也不须要牢记多套用户名称和用户密码。单点登陆平台可以改善用户使用应用系统的体验。
  • 方便管理员 系统管理员只须要维护一套统一的用户帐号,方便、简单。相比之下,系统管理员之前须要管理不少套的用户帐号。每个应用系统就有一套用户帐号,不只给管理上带来不方便,并且,也容易出现管理漏洞。
  • 简化应用系统开发 开发新的应用系统时,能够直接使用单点登陆平台的用户认证服务,简化开发流程。单点登陆平台经过提供统一的认证平台,实现单点登陆。所以,应用系统并不须要开发用户认证程序。

3、SSO实现方案

常见的跨域登陆问题

一、对于同一个根域下的登陆问题

若是咱们的站点有不止一个业务,那么他们可能部署在不一样的机器上,也每每须要不一样的域名进行区分。可是全部的业务又都是依赖于一套帐户体系,那么咱们这时候须要经过一次登陆解决全部站点的登陆问题,那么咱们这个时候可使用一个最笨的方法:那就是一次登陆成功,将Cookie写到根域下,那么这样全部的站点就能实现,同一个根域下的Cookie共享,天然实现了“单点登陆”。java

二、对于多个根域下的登陆问题

若是是多个根域名,那么这种状况下上面的机制就不能实现“单点登陆”了。由于之因此上面能够实现“单点登陆”的效果。是由于浏览器和Http协议的支持。可是对于跨根域的站点之间进行Cookie的共享是比较复杂的。git

  • 方法1:登陆成功以后将Cookie回写到多个域名下。

这种办法可能十分简单,你能够经过后端的response写,也能够用前端js去写,可是必须有对全部须要“单点登陆”的站点进行逐一的写入。用脚想这种办法也是行不通的,由于你须要维护一个站点的列表,维护工做十分复杂,同时对于增长站点也会特别痛苦。对于Cookie的销毁也是十分复杂的,由于仍是要对全部域名下的Cookie进行删除。也就是说将原来须要作的工做增长了n倍。对于小型站点这种办法是可取的。github

  • 方法2:jsonp

搞过前端的可能都知道用jsonp能够作跨域的请求,而咱们解决的就是多个域下的统一登陆的问题,好像很瓜熟蒂落的样子。可是,登陆是Server端作的吧?咱们在Client端作跨域的处理,这怎么看也不是很合理。同时这种办法须要很大的维护成本,每一次请求都要去固定的域下取相应的Cookie以后再作请求。想一想维护有头疼。web

  • 方法3 :引入一个中间态的Server

这种办法算是一个简化版的SSO,实现思想也十分的“狡猾”。可是对于小网站作跨域登陆的处理却十分的有用,具体思路以下:spring

首先,咱们有两个域名要实现单点登陆,同时咱们须要一个中间的Server。json

  1. 咱们有一个系统域名为xulingbo.net,当咱们登陆的时候访问xulingbo.net/wp-login进行登陆,登陆成功以后将Cookie回写到xulingbo这个域名下。后端

  2. 咱们还有一个系统域名为javaWeb.com,当咱们访问inside-javaWeb的时候,咱们没有Cookie,那么请求跳转到中间系统jump。此时须要将当前域名带到参数中便于jump校验。这个jump系统是在xulingbo域下的即:jump.xulingbo.net。这时候就能拿到以前写在xulingbo域下的Cookie。跨域

  3. jump系统在收到了xulingbo域下的Cookie以后,取出xulingbo域下的Cookie,并redirect请求jump.inside-javaWeb.net,这个接口也是在jump系统中,请求后jump系统将Cookie回写到inside-javaWeb域名下,这样就实现了简易的单点登陆。 以下图所示:浏览器

可是这种方式不是很灵活,对于数据传输的安全性没有保障,而且在销毁Cookie的时候无能为力,只能所有遍历的销毁。

  • 方法4:基于CAS的SSO系统

CAS全称为:Central Authentication Service,是一款不错的针对web应用的单点登陆框架,包括java,.net,PHP,Prel,Apache,uPortal,Ruby等。。实现的机制不算复杂可是思想十分灵巧。用CAS也能够快速实现单点登陆。盗图一张说明sso单个域的登陆和验证流程:

CAS主要分为CAS Client 和CAS Server ,其中Client主要是内嵌在须要SSO登陆站点的拦截器或过滤器上。

  1. 首先浏览器向站点1发起请求。
  2. 站点1发现当前请求没有合法的Cookie,那么重定向到CAS Server上,也就是SSO Server。
  3. CAS Server展现登陆界面,要求用户登陆。
  4. 用户登陆后,会写CAS Server的Cookie到浏览器,同时生产ticket,利用一个302跳转到CASClient。这样能保证用户无感知。
  5. CAS Client利用生成的ticket发送到CAS Server进行验证,验证经过后,站点1生成本身的Cookie并回写到用户浏览器,而后进行登陆成功的跳转。

这样就能保证当前浏览器在站点1的域名下,有站点1的Cookie,同时当前浏览器也有CAS Server的Cookie。 接下来看下站点2的登陆:

站点2,在进行登陆时和站点1初次登陆流程一致,可是在访问CAS Server的时候,因为当前浏览器已经有了CAS Server的Cookie,那么直接校验经过返回ticket。 ticket经过302跳转跳转到CAS Client上,以后的流程就和站点1是同样的了。若是此时认证失败,那么须要从新走一次登陆的过程。

注意的问题: 一、CAS Server的Cookie劫持问题,若是CAS Server的Cookie被劫持掉,那么就至关于拿到了一切,因此必需要用HTTPS实现这个过程。 二、ticket的使用,ticket只能被使用一次,一次校验后当即失效。同时须要有时效性,通常5分钟。最后ticket生成规则要随机,不能被碰撞出来。 三、对于各自系统本身的Session,也能够依赖于SSO,这样就能保证全部的Session规则一致,便于集中控制。

注:运营系统也是采用这种方式处理的 基于当前现状,单点登陆系统能够有一下处理

  • 调用Soa服务实现登陆登出 缺点:得本身实现判断用户是否登陆、session是否过时;缺少权限控制,得本身实现 优势:实现简单
  • 本身在web服务实现登陆登陆功能 安全性偏低
  • 使用Spring Security + Oauth2实现登陆登陆功能 优势:企业级,安全性高 提供了多种安全验证方式 能够处理复杂的权限控制

4、Spring Security 介绍

  • Spring Security是什么?

Spring security 是一个强大的和高度可定制的身份验证和访问控制框架,它提供了基于javaEE的企业应有个你软件全面的安全服务。它是保护基于Spring的应用程序的事实上的标准。主要做用是“认证”和“受权”(或者访问控制)。 在身份验证层,Spring Security 的支持多种认证模式。

  • 核心功能 一、认证(你是谁) 二、受权(你能干什么) 三、攻击防御(防止伪造身份)
  • 什么是Spring Security验证?
  1. 提示用户输入用户名和密码进行登陆。
  2. 该系统 (成功) 验证该用户名的密码正确。
  3. 获取该用户的环境信息 (他们的角色列表等).
  4. 为用户创建安全的环境。
  5. 用户进行,可能执行一些操做,这是潜在的保护的访问控制机制,检查所需权限,对当前的安全的环境信息的操做。

验证流程介绍

  1. 用户名和密码进行组合成一个实例UsernamePasswordAuthenticationToken (一个Authentication接口的实例, 咱们以前看到的).
  2. 令牌传递到AuthenticationManager实例进行验证。
  3. 该AuthenticationManager彻底填充Authentication实例返回成功验证。
  4. 安全环境是经过调用 SecurityContextHolder.getContext().setAuthentication(…​), 传递到返回的验证对象创建的。

5、OAUTH2

  • 名词定义
  1. Third-party application:第三方应用程序,本文中又称"客户端"(client)
  2. HTTP service:HTTP服务提供商,本文中简称"服务提供商"
  3. Resource Owner:资源全部者,本文中又称"用户"(user)。
  4. User Agent:用户代理,一般就是指浏览器。
  5. Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
  6. Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,能够是同一台服务器,也能够是不一样的服务器。
  • OAuth的思路

OAuth在"客户端"与"服务提供商"之间,设置了一个受权层(authorization layer)。"客户端"不能直接登陆"服务提供商",只能登陆受权层,以此将用户与客户端区分开来。"客户端"登陆受权层所用的令牌(token),与用户的密码不一样。用户能够在登陆的时候,指定受权层令牌的权限范围和有效期。 "客户端"登陆受权层之后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。

  • 运行流程

(A)用户打开客户端之后,客户端要求用户给予受权。 (B)用户赞成给予客户端受权。 (C)客户端使用上一步得到的受权,向认证服务器申请令牌。 (D)认证服务器对客户端进行认证之后,确认无误,赞成发放令牌。 (E)客户端使用令牌,向资源服务器申请获取资源。 (F)资源服务器确认令牌无误,赞成向客户端开放资源。 不难看出来,上面六个步骤之中,B是关键,即用户怎样才能给于客户端受权。有了这个受权之后,客户端就能够获取令牌,进而凭令牌获取资源。

结合上图,使用oauth2保护你的应用,能够分为简易的分为三个步骤

  1. 配置资源服务器 Authorization Service.
  2. 配置认证服务器 Resource Service.
  3. 配置spring security

全部获取令牌的请求都将会在Spring MVC controller endpoints中进行处理,而且访问受保护 的资源服务的处理流程将会放在标准的Spring Security请求过滤器中(filters)。 下面是配置一个受权服务必需要实现的endpoints: -AuthorizationEndpoint:用来做为请求者得到受权的服务,默认的URL是/oauth/authorize. -TokenEndpoint:用来做为请求者得到令牌(Token)的服务,默认的URL是/oauth/token.

  • 客户端的受权模式

客户端必须获得用户的受权(authorization grant),才能得到令牌(access token)。OAuth 2.0定义了四种受权方式。

  1. 受权码模式(authorization code)
  2. 简化模式(implicit)
  3. 密码模式(resource owner password credentials)
  4. 客户端模式(client credentials)

受权模式讲解

  • 受权码模式

受权码模式(authorization code)是功能最完整、流程最严密的受权模式。它的特色就是经过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。

它的步骤以下:
(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端受权。
(C)假设用户给予受权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个受权码。
(D)客户端收到受权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
(E)认证服务器核对了受权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

下面是上面这些步骤所须要的参数。 A步骤中,客户端申请认证的URI,包含如下参数:

  • --response_type:表示受权类型,必选项,此处的值固定为"code"
  • --client_id:表示客户端的ID,必选项
  • --redirect_uri:表示重定向URI,可选项
  • --scope:表示申请的权限范围,可选项
  • --state:表示客户端的当前状态,能够指定任意值,认证服务器会原封不动地返回这个值。
demo:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
复制代码

C步骤中,服务器回应客户端的URI,包含如下参数: --code:表示受权码,必选项。该码的有效期应该很短,一般设为10分钟,客户端只能使用该码一次,不然会被受权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。 --state:若是客户端的请求中包含这个参数,认证服务器的回应也必须如出一辙包含这个参数。

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz
复制代码

D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含如下参数:

  • --grant_type:表示使用的受权模式,必选项,此处的值固定为"authorization_code"。
  • --code:表示上一步得到的受权码,必选项。
  • --redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
  • --client_id:表示客户端ID,必选项。

E步骤中,认证服务器发送的HTTP回复,包含如下参数:

  • --access_token:表示访问令牌,必选项。
  • --token_type:表示令牌类型,该值大小写不敏感,必选项,能够是bearer类型或mac类型。
  • --expires_in:表示过时时间,单位为秒。若是省略该参数,必须其余方式设置过时时间。
  • --refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
  • --scope:表示权限范围,若是与客户端申请的范围一致,此项可省略。

常见的第三方登陆也是采用这种方法,先请求获得code,而后再经过code去获取令牌。

  • 简化模式 简化模式(implicit grant type)不经过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"受权码"这个步骤,所以得名。全部步骤在浏览器中完成,令牌对访问者是可见的,且客户端不须要认证。

它的步骤以下: (A)客户端将用户导向认证服务器。
(B)用户决定是否给于客户端受权。
(C)假设用户给予受权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码能够获取Hash值中的令牌。
(F)浏览器执行上一步得到的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。

下面是上面这些步骤所须要的参数。
A步骤中,客户端发出的HTTP请求,包含如下参数:

  • -response_type:表示受权类型,此处的值固定为"token",必选项。
  • -client_id:表示客户端的ID,必选项。
  • -redirect_uri:表示重定向的URI,可选项。
  • -scope:表示权限范围,可选项。
  • -state:表示客户端的当前状态,能够指定任意值,认证服务器会原封不动地返回这个值。

下面是一个例子:

GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
    Host: server.example.com
复制代码

C步骤中,认证服务器回应客户端的URI,包含如下参数:

  • -access_token:表示访问令牌,必选项。
  • -token_type:表示令牌类型,该值大小写不敏感,必选项。
  • -expires_in:表示过时时间,单位为秒。若是省略该参数,必须其余方式设置过时时间。
  • -scope:表示权限范围,若是与客户端申请的范围一致,此项可省略。
  • -state:若是客户端的请求中包含这个参数,认证服务器的回应也必须如出一辙包含这个参数。

下面是一个例子:

HTTP/1.1 302 Found
     Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
               &state=xyz&token_type=example&expires_in=3600
复制代码
  • 密码模式

密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供本身的用户名和密码。客户端使用这些信息,向"服务商提供商"索要受权。 在这种模式中,用户必须把本身的密码给客户端,可是客户端不得储存密码。这一般用在用户对客户端高度信任的状况下,好比客户端是操做系统的一部分,或者由一个著名公司出品。而认证服务器只有在其余受权模式没法执行的状况下,才能考虑使用这种模式。

它的步骤以下:
(A)用户向客户端提供用户名和密码。
(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。
(C)认证服务器确认无误后,向客户端提供访问令牌。

B步骤中,客户端发出的HTTP请求,包含如下参数:
-grant_type:表示受权类型,此处的值固定为"password",必选项。
-username:表示用户名,必选项。
-password:表示用户的密码,必选项。
-scope:表示权限范围,可选项。

下面是一个例子:

POST /token HTTP/1.1
     Host: server.example.com
     Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
     Content-Type: application/x-www-form-urlencoded

     grant_type=password&username=johndoe&password=A3ddj3w
复制代码

C步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。

HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache

     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }
复制代码

其它的认证介绍参考下面参考地址。

  • 客户端模式 客户端模式(Client Credentials Grant)指客户端以本身的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以本身的名义要求"服务提供商"提供服务,其实不存在受权问题。

它的步骤以下:
(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。
(B)认证服务器确认无误后,向客户端提供访问令牌。

A步骤中,客户端发出的HTTP请求,包含如下参数:
-granttype:表示受权类型,此处的值固定为"clientcredentials",必选项。
-scope:表示权限范围,可选项。

POST /token HTTP/1.1
     Host: server.example.com
     Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
     Content-Type: application/x-www-form-urlencoded

     grant_type=client_credentials
复制代码

认证服务器必须以某种方式,验证客户端身份。 B步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。

HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache

     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "example_parameter":"example_value"
     }
复制代码

附上我本身写的一个spring-security+oauth2项目,可参考使用,反正我本身的项目用上这个了。github.com/jiezaizone/…
另外一种单点登陆方式:Keycloak单点登陆

关于我

持续输出原创文章,这是是刘荣杰的第1篇原创文章

相关文章
相关标签/搜索