Back to top

编辑模块

统一身份认证中心

新手指南

要使用单点登录,首先需要了解什么是单点登录?单点登录能做什么?

单点登录是什么

单点登录是一种多应用业务整合的解决方案,用户只要使用一个账号在一处登录,即可在多个站点,甚至是多个终端在不输入账号的情况下获取用户信息,这大大降低了用户的使用门槛。

单点登录能做什么

单点登录,不同于登录,除了提供对用户密码信息的校验之外,主要提供了多站点、多终端之间用户信息安全传递的功能。接入单点登录后,配合开放平台可以实现数据层面的整合和互通。

我们提供了什么

我们提供了单点登录的整套解决方案,无论你是web端、PC端还是移动端,我们均提供了SDK以实现快速接入。另外,只要和单点登录相关的需求都会提供合理的解决方案供参考。

单点登录简介

单点登录原理

单点登录实现的原理为:首次访问某个网站时会被引导到统一登录页进行用户名密码的校验,用户登录验证通过后,SSO服务器会生成认证cookie(TGC)并写入浏览器,同时生成一个TGT对象放入自己的缓存,该TGT对象的id即为cookie的值。下一次该浏览器下如果有应用需要前往SSO服务器去检查是否有用户登录时,会自动携带该TGC,认证服务器会在自己的缓存中查找是否有TGT对应该cookie值,如果有的话说明该用户已经登录过,否则用户需要重新登录。

SSO服务器确认用户已经登录过后,会使用该TGT值为用户签发访问某一service的票据,即ST,用户凭借ST去访问对应的service,会再一次去SSO服务器验证ST的合法性,验证通过后返回该TGT对应的用户信息,配合开放平台接口可以获取用户的详细信息。

单点登出原理

当应用系统中有一个应用退出,服务端会发送logout请求到所有客户端,同时携带之前登录的ticket,客户端一看请求中参数有logoutRequest的时候,客户端的singleSignOutFilter根据传过来的这个ticket来将对应的用户session干掉,即完成了单点登出的功能。

如果客户端需要集成单点登出功能,B/S模式下需要做单点登出的必要配置。

流程图

应用接入指南

B/S方式接入【推荐】

这种情况下,系统的用户凭证借助cookie和URL参数方式实现;在B/S环境中,大多数情况,我们配置CAS Filter就可以实现验证客户端。下述配置厂商地址以www.lezhiyun.com为例,统一认证以cas.lezhiyun.com为例,说明如何接入统一认证。

其他业务系统集成需要修改成自己应用服务地址和对应环境的统一认证地址

引入依赖jar包(必须)

拷贝cas-client-core-3.2.1.jar到项目的WEB-INF/lib目录下。

点此下载cas-client-core-3.2.1.jar,访问密码:eKrD

单点退出配置(可选)

  • 在web.xml文件中加入CAS Server退出地址
<context-param>
    <!-- 配置统一认证退出地址,一般由项目交付人员提供 -->
    <param-name>casServerLogoutUrl</param-name>
    <param-value>http://cas.lezhiyun.com/logout</param-value>
</context-param>
  • 在web.xml文件中配置业务系统访问地址
<context-param>
    <!-- 配置厂商应用服务地址,仅包含schema,主机域名或ip:端口,不需要上下文及url部分 -->
    <param-name>serverName</param-name>
    <param-value>http://www.lezhiyun.com</param-value>
</context-param>
  • 在web.xml文件中配置业务系统退出监听器
<listener>
  <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
  • 在web.xml文件中配置业务系统退出过滤器
<filter>
  <filter-name>CAS Single Sign Out Filter</filter-name>
  <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>CAS Single Sign Out Filter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

注意:单点退出配置需要在web.xml中配置到其他CAS过滤器之前。

单点登录配置(必须)

  • web.xml配置业务系统认证过滤器(必须)
<filter>
    <filter-name>CAS Authentication Filter</filter-name>
    <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
    <init-param>
        <!-- 配置统一认证访问地址,一般由项目交付人员提供 -->
        <param-name>casServerLoginUrl</param-name>
        <param-value>http://cas.lezhiyun.com</param-value>
    </init-param>
    <init-param>
        <!-- 配置厂商应用服务地址,仅包含schema,主机域名或ip:端口,不需要上下文及url部分 -->
        <param-name>serverName</param-name>
        <param-value>http://www.lezhiyun.com</param-value>
    </init-param>
    <init-param>
        <!-- 默认参数,不用动 -->
        <param-name>renew</param-name> 
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <!-- 默认参数,不用动 -->
        <param-name>gateway</param-name>
        <param-value>false</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CAS Authentication Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  • web.xml配置业务系统票据验证过滤器(必须)
<filter>
    <filter-name>CAS Validation Filter</filter-name>
    <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
    <init-param>
        <!-- 配置统一认证访问地址,一般由项目交付人员提供 -->
        <param-name>casServerUrlPrefix</param-name>
        <param-value>http://cas.lezhiyun.com</param-value>
    </init-param>
    <init-param>
        <!-- 配置厂商应用服务地址,仅包含schema,主机域名或ip:端口,不需要上下文及url部分 -->
        <param-name>serverName</param-name>
        <param-value>http://www.lezhiyun.com</param-value>
    </init-param>
    <init-param>
        <!-- 防止cas登录时中文名乱码,设置编码格式为utf-8 -->
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CAS Validation Filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  • web.xml配置CAS HttpServletRequest过滤器(必须)
<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>
  • web.xml配置CAS Assertion过滤器(必须)
<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>

web.xml字段说明

具体常用配置的参数如下(具体参数详见源码):

context-param配置项:

  • 如果客户端配置了context-param,则如下参数必填
字段名称 必填 字段含义 示例
casServerLogoutUrl 统一认证服务退出地址 http://cas.lezhiyun.com/logout
serverName 厂商应用服务地址,仅包含schema,主机域名或ip:端口,不需要上下文及url部分 http://www.lezhiyun.com

CAS Authentication Filter配置项:

字段名称 必填 字段含义 示例
casServerLoginUrl 统一认证访问地址,一般由项目交付人员提供 http://cas.lezhiyun.com
serverName 厂商应用服务地址,仅包含schema,主机域名或ip:端口,不需要上下文及url部分 http://www.lezhiyun.com

CAS Validation Filter配置项:

字段名称 必填 字段含义 示例
casServerUrlPrefix 统一认证访问地址,一般由项目交付人员提供 http://cas.lezhiyun.com
serverName 厂商应用服务地址,仅包含schema,主机域名或ip:端口,不需要上下文及url部分 http://www.lezhiyun.com

获取当前用户信息

单点登录和单点退出配置完成后,在CAS认证通过后会重定向到业务系统中,此时业务系统可以通过request.getRemoteUser()获取登录用户名。 除了通过request.getRemoteUser()获取到登陆用户名之外,可以在Attributes中获取到其他一些用户信息

字段名称 字段含义 示例
id 用户唯一标识,全区唯一,32位uuid 0e9b61eac0cb4c6cb9bf4e135ccbb194
username 用户登录名 exampleuser
name 用户姓名 示例用户
usertel 用户手机号 13xxxxxxxxx
useremail 用户邮箱 xx@xx.com
usertype 用户类型,2代表教师,3代表家长,4代表学生 2
schoolId 用户所属学校id 0e9b61eac0cb4c6cb9bf4e135ccbb194

获取代码如下

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
Map attributes = principal.getAttributes();
if(attributes != null){
    String name= (String)attributes.get("name");
}

至此,B/S结构的业务系统与CAS集成完毕。

C/S方式接入

c/s方式接入适用于业务本身是作为登录入口来集成统一认证服务的场景,用户通过在业务方的登录入口输入帐密登录,而业务方本身不知道帐密的正确性,即可使用c/s方式进行校验

登录示例

以java程序为例,使用apache的httpclient工具模拟客户端网络请求。

  • 提交用户名和密码获取TGT
// 构造httpclient对象
HttpClient client = newHttpClient();  
// 构造postmethod对象,设置TGT获取地址
PostMethodpost=newPostMethod("http://cas.lezhiyun.com/v1/tickets");  
// 设置请求参数,用户名和密码,假设用户名zhangsan,密码123456
post.setRequestBody(newNameValuePair[] {  
    newNameValuePair("username", "zhangsan"),  
    newNameValuePair("password", "123456") });  
try{
    // 发送请求
    client.executeMethod(post);  
    // 获取请求返回
    String response = post.getResponseBodyAsString();  
    // 处理请求返回
    switch (post.getStatusCode()) {  
        case 201: {  
            final Matcher matcher = Pattern.compile(".*action=\".*/(.*?)\".*").matcher(response);  
            // 获取CAS Server返回的TGT,红色标注的是TGT。
            if (matcher.matches())  { 
                System.out.println(matcher.group(1));  
            } else {
                System.out.println("Successful ticket granting request, but no ticket found!");  
                System.out.println("Response (1k): " 
                    + response.substring(0, Math.min(1024, response.length())));  
                break;  
            }
        }  
        default:  
            System.out.println("Invalid response code (" 
                + post.getStatusCode()+ ") from CAS server!");  
            System.out.println("Response (1k): "  
                + response.substring(0, Math.min(1024, response.length())));  
            break;  
    }
}  catch (finalIOException e)  {
    System.out.println(e.getMessage());
}  finally  {  
        // 释放链接
    post.releaseConnection();
}
  • 提交TGT获取ST
// 构造httpclient对象
HttpClient client = new HttpClient();  
//构造postmethod对象,设置ST获取地址,其中红色标注的为上一步生成的TGT
PostMethod post = newPostMethod("http://cas.lezhiyun.com/v1/tickets/" + TGT);
// 设置请求参数,业务系统地址,假设用户名http://www.lezhiyun.com /
post.setRequestBody(newNameValuePair[] { 
    newNameValuePair("service", "http://www.lezhiyun.com/ main/v4/index.jsp ") });  
try {
    // 发送请求
    client.executeMethod(post);  
    // 获取请求返回
    String response = post.getResponseBodyAsString(); 
    // 处理请求返回
    switch (post.getStatusCode()) {  
        case 200:  
            // 获取CAS Server返回的ST,红色标注的是ST。
            System.out.println(response);  
        default:
            System.out.println("Invalid response code (" +
                post.getStatusCode()+ ") from CAS server!");
            System.out.println("Response (1k): "
            + response.substring(0, Math.min(1024, response.length())));  
            break;  
    }  
}catch (finalIOException e) {  
    System.out.println(e.getMessage());  
} finally  {  
    // 释放链接
    post.releaseConnection();  
}
  • 传递ST单点登录业务系统

例如:登录 www.lezhiyun.com,打开浏览器,输入地址http://www.lezhiyun.com/main/v4/index.jsp?ticket=ST即可单点登录乐知云系统,其中红色标注的是上一步生成的ST,如下代码模拟登录业务系统:

// 构造httpclient对象
HttpClient client = new HttpClient();
//构造getmethod对象,设置业务系统访问地址
GetMethod method = newGetMethod("http://www.lezhiyun.com/main/v4/index.jsp/");
//构造请求参数,设置ticket参数,其中红色标注的为上一步生成的ST
NameValuePair[] valuePair = newNameValuePair[]{newNameValuePair("ticket", ST)};
method.setQueryString(valuePair);
try{
    // 发送请求
    client.executeMethod(method);
    // 获取请求返回
    String response = method.getResponseBodyAsString();
    // 处理请求返回
    switch (method.getStatusCode()){
        case 200:
            // 获取业务系统登录成功返回HTML代码。
            System.out.println("Response: " + response);
            break;
        default:
            System.out.println("Invalid response code (" 
            + method.getStatusCode() + ") from CAS server!");
            System.out.println("Response: " + response);
        break;
    }
}catch (finalIOException e){
    System.out.println(e.getMessage());
}finally{
    // 释放链接
    method.releaseConnection();
}

登出示例

  • 提交TGT注销用户
// 构造httpclient对象
HttpClient client = newHttpClient();
//构造deletemethod对象,设置TGT注销地址,其中红色标注的为单点登录时提交用户名和密码获取的TGT
DeleteMethod method = new DeleteMethod("http://cas.lezhiyun.com/v1/tickets/" + TGT);
try{
    // 发送请求
    client.executeMethod(method);
    // 获取请求返回
    String response = method.getResponseBodyAsString();
    // 处理请求返回
    switch (method.getStatusCode()){
        case 200:
            System.out.println("Logged out");
            break;
        default:
            System.out.println("Invalid response code (" + method.getStatusCode() + ") from CAS server!");
            System.out.println("Response: " + response);
            break;
    }
}catch (finalIOException e){
    System.out.println(e.getMessage());
}finally{
    // 释放链接
    method.releaseConnection();
}

至此,C/S结构的业务系统与CAS集成完毕。

oauth 2.0协议接入

oauth2.0协议主要提供三个接口,也是认证的三个核心部分:

  1. authorize:CAS Client(客户端)根据客户端id和密钥等信息获取授权码;
  2. accessToken:CAS Client(客户端)根据授权码等信息申请令牌;
  3. profile:CAS Client(客户端)根据申请的令牌获取当前用户的登录信息。

authorize协议

  • 请求参数
参数 必选 类型 说明
client_id true string 客户端id,由开发者/第三方向乐知行申请
redirect_uri true string 申请客户端信息时填写的回调地址,必须保持一致
response_type true string 常量,传code即可
  • authorize协议是用户访问客户端,在浏览器发起的对服务器端的请求,请求地址示例如下:
http://cas.lezhiyun.com/oauth2.0/authorize?client_id=key&redirect_uri=http://www.lezhiyun.com/main/v5/index.jsp&response_type=code
  • 随后浏览器进入sso界面,如果用户未登录,则需要输入用户名和密码做登录校验。登录成功之后,将进入oauth授权界面。用户点击跳转之后,将传递授权码(code)访问回调地址(redirect_uri)。返回地址示例如下:

http://www.lezhiyun.com/main/v5/index.jsp?code=ST-1-JgETbYqqndcmbsCVtZw5-cas

accessToken协议

  • 请求参数
参数 必选 类型 说明
client_id true string 客户端id,由开发者/第三方向讯飞乐知行申请
client_secret true string 客户端密钥,由开发者/第三方向讯飞乐知行申请
redirect_uri true string 申请客户端信息时填写的回调地址,必须保持一致
grant_type true string 常量,authorization_code即可
code true string 授权码,由authorize协议产生,有效时间为5分钟
  • accessToken协议是客户端根据授权码等信息向服务器端申请acessToken的过程。请求示例如下:
http://cas.lezhiyun.com/oauth2.0/accessToken?client_id=key&client_secret=secret&grant_type=authorization_code&redirect_uri=http://www.lezhiyun.com/main/v5/index.jsp&code=ST-1-JgETbYqqndcmbsCVtZw5-cas
  • 请求成功之后的返回令牌信息如下:
access_token=TGT-1-dbkMkTPJhKLsDtCD1GCaVegcMdlYxFJmm43f3yabn5fYZbtCRf-cas&expires=7192

profile协议

  • 请求参数
参数 必选 类型 说明
access_token true string 由accessToken协议产生的令牌
  • profile协议是客户端根据获取的令牌获取用户信息,请求示例如下:
http://cas.lezhiyun.com/oauth2.0/profile?access_token=TGT-1-dbkMkTPJhKLsDtCD1GCaVegcMdlYxFJmm43f3yabn5fYZbtCRf-cas&expires=7192

返回信息如下:

{
    "id": "exampleUser",
    "attributes": [
        {
            "id": "17194b0d26494756b853a1eaf4b3e1e1"
        },
        {
            "username": "exampleUser"
        },
        {
            "usertype": "2"
        },
        {
            "usertel": "13xxxxxxxxx"
        },
        {
            "useremail": "xx@xx.xx"
        },
        {
            "schoolId": "73B3DAC8C842456A9D0B5E95E95A0E9C"
        },
        {
            "name": "示例用户"
        }
    ]
}

名词解释

  • SSO

Single Sign On, 单点登录

  • TGT

Ticket Granting Ticket, 票据授权票据

  • ST

Service Ticket, 业务系统票据

  • OAUTH

Oauth是一个关于授权的开放标准网络协议,目前的版本是2.0

FAQ

问题1:B/S方式集成统一认证服务,部分用户访问报”空指针“错误

  • 原因:可能是cas中文名乱码的问题

  • 解决方案:在CAS Validation Filter的web.xml配置中增加如下配置项

<init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
</init-param>

问题2:B/S方式集成之后,无法获取到用户attribute里面的值

  • 原因:服务端cas版本太老,需交付/运维同事升级

  • 解决方案:升级至最新cas服务

问题3:B/S方式集成之后,报错 No principal was found in the response from the CAS server

  • 原因:客户端集成的cas版本太老,出现问题的目前是3.1.3版本的

  • 解决方案:需要替换到至少cas-client-core-3.2.1.jar版本

Generated by aglio on 11 Apr 2019