`
gogo1217
  • 浏览: 152311 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

CAS 系列 之 AJAX 请求

阅读更多

版权所有,转载请注明来源http://gogo1217.iteye.com,违者必究!

 

最早接触 CAS 大概是 2011 年,现在发展到 5.x 了。相比较那时候的版本。比较有意思的特性有:
服务端:
1. 使用 spring-boot 开发
2. 推荐使用 overlay 的方式对服务端进行修改,这个大大的赞。虽然我接触 CAS 的第二年就是这么干的 ...

客户端 3.5.0 版本:
1、默认强制获取 session。
2、增加了 authenticationRedirectStrategyClass ,使得能自定义没有登录时选择重定向还是返回地址交给浏览器去决定。

CAS 单点登录的原理不再赘述,这里重点阐述下 AJAX 请求 CAS Client 资源发生重定向的解决方案。


问题描述
假定,我们存在以下服务:
1、CAS 服务端 Server ;
2、CAS 客户端 Client-A、Client-B,集成了 CAS 客户端;

假定 Client-A 有个页面 Page-A,使用 AJAX 调用了 Client-B 的接口 API-B。当用户访问  Page-A 时,分为以下 3 种情况:

一):没有登录过单点服务
1、API-B 发现没有登录,通知浏览器重定向到 Server
3、由于 AJAX 不能重定向,因此请求无法继续

二):Server端已登陆,但 Client-B 还未登录
1、API-B 发现没有登录,通知浏览器重定向到 Server
3、由于 AJAX 不能重定向,因此请求无法继续

三):Client-B 登录过单点服务
 1、API-B 发现已经登录,返回正确的内容


解决思路
想要解决这个问题,必须拦截所有的 AJAX 请求,根据返回判断,手动触发 CAS 的重定向。


1、首先对 Client-B (CAS 的客户端)进行改造。


如果是 AJAX 请求,则返回一个重定向的目标地址给客户端,由客户端完成URL的重定向,这里直接使用 client 3.5.0 版本,已经具备了这种接口。

 

<init-param>
    <param-name>authenticationRedirectStrategyClass</param-name>
    <param-value>org.jasig.cas.client.authentication.FacesCompatibleAuthenticationRedirectStrategy</param-value>
</init-param>

 

 

 这是它默认的实现,使用的是特殊参数,我们一般改为通过HTTP请求头判定比较合适。

 

 

public final class FacesCompatibleAuthenticationRedirectStrategy implements AuthenticationRedirectStrategy {
    private static final String FACES_PARTIAL_AJAX_PARAMETER = "javax.faces.partial.ajax";

    public FacesCompatibleAuthenticationRedirectStrategy() {
    }

    public void redirect(HttpServletRequest request, HttpServletResponse response, String potentialRedirectUrl) throws IOException {

        String xRequestedWith = request.getHeader("X-Requested-With");

        if ((CommonUtils.isNotBlank(xRequestedWith) && "XMLHttpRequest".equals(xRequestedWith.trim()))
                || CommonUtils.isNotBlank(request.getParameter(FACES_PARTIAL_AJAX_PARAMETER))) {
            response.setContentType("application/json");
            response.setStatus(200);
            PrintWriter writer = response.getWriter();
            writer.write("{\"casError\":\"403\",\"redirect\":\"" + potentialRedirectUrl + "\"}");
        } else {
            response.sendRedirect(potentialRedirectUrl);
        }
    }
}

 

 

 

2、对 Server (CAS 的服务端)进行改造。


a. 支持 AJAX 跨域调用:


修改 application.yml ,增加以下配置,使得 CAS 服务端支持跨域调用。


注意,如果要对CAS默认的属性进行配置,建议修改 application.yml 。

 

cas:
  httpWebRequest:
    cors:  # cors 跨域
      enabled: true
      allowCredentials: true
      allowOrigins: ["*"]
      allowMethods: ["*"]
      allowHeaders: ["*"]
      maxAge: 3600

 

 

b. 当 CAS 服务端已登录,而客户端没有登录,返回 ST 内容,而不是重定向。


参考:https://apereo.github.io/cas/5.0.x/protocol/CAS-Protocol-Specification.html

 

 

Use POST responses instead of redirects:

https://cas.example.org/cas/login?method=POST&service=http%3A%2F%2Fwww.example.org%2Fservice


在请求中增加参数 method=POST ,CAS 服务端将以 Form 表单的方式返回,而不是直接服务器重定向。

 

 

实现步骤

 

有了上面的准备,我们重新梳理下 AJAX请求,需要做什么处理。

 

一):没有登录过单点服务
1、API-B 发现没有登录,返回一个重定向到 Server 的地址( AJAX 特殊处理,客户端改造)。
2、AJAX 获得这个地址后在 URL 后追加参数 method=POST,重新发起 AJAX 请求,请求 Server(服务端跨请求处理)。
3、由于 Server 没有登录,服务端返回登录页面的内容。
4、AJAX 获得返回内容,发现是登录页面,提醒用户登录。
5、用户登录后,重新发起AJAX请求。

二):Server 端已登陆,但 Client-B 还未登录
1、API-B 发现没有登录,返回一个重定向到 Server 的地址( AJAX 特殊处理,客户端改造)。
2、AJAX 获得这个地址后在 URL 后追加参数 method=POST,重新发起 AJAX 请求,请求 Server(服务端跨域请求处理)。
3、由于 Server 已经登录,服务端以表单的方式返回 ST 凭证(参数 method=POST,服务端处理)。
4、AJAX 获得ST后,在最初的请求上附加 ST 凭证,重新请求 API-B。
5、Client-B 根据 ST 去 Server 获取用户信息,完成登录,并返回正确内容。

 

在上述环节中,如果 Server 端已经登录过,我们是可以做到完全静默请求,用户无感知的。

 

通过对 AJAX 的统一拦截处理,使得开发人员在开发过程中,无感知 cas。以下是 axios 的默认实现。

(function () {
    if (!axios) {
        return;
    }
    axios.interceptors.request.use((config) => {
        if (!config.headers['X-Requested-With']) {
            config.headers['X-Requested-With'] = 'XMLHttpRequest';
        }
        return config;
    });

    var oldRequest = axios.Axios.prototype.request;

    axios.Axios.prototype.request = function request(config) {
        var self = this;
        return new Promise(function (resolve, reject) {
            oldRequest.call(self, config).then(
                function (response) {
                    if (response.data && response.data.casError == '403' && response.data.redirect) {
                        console.log('原始请求- 发现 CAS 客户端未登录');
                        let url = response.data.redirect + "&method=POST";
                        axios.get(url, {
                            withCredentials: true,
                            responseType: 'document',
                        }).then(function (ssoResponse) {
                            var form = ssoResponse.data.getElementsByTagName('form');
                            if (form) {
                                form = form[0];
                                if (form.getAttribute('id') === 'fm1') {
                                    console.log('获取ticket 失败 ,CAS 服务端未登录');
                                    ssoResponse.message = '获取ticket 失败 ,CAS 服务端未登录';
                                    reject(ssoResponse);
                                    alert('请在弹出窗口完成登录后,再进行操作');
                                    window.open(response.data.redirect.substring(0, response.data.redirect.indexOf('?')));
                                }
                                else if (form.getAttribute('name') === 'acsForm' && form.getElementsByTagName('input')) {
                                    var join = config.url.indexOf('?') > 0 ? '&' : '?';
                                    config.url = config.url + join + 'ticket=' + form.getElementsByTagName('input')[0].value;
                                    axios.request(config).then(function (withLoginResponse) {
                                        console.log('获取ticket 成功,再次请求数据');
                                        resolve(withLoginResponse);
                                    }).catch(function (err) {
                                        reject(err);
                                    });
                                }
                            }
                        })
                    } else {
                        resolve(response);
                    }
                },
                function (error) {
                    reject(error);
                }
            );
        });
    };
})();

 

 完整代码:

 https://github.com/gogo1217/sso-parent

 

0
0
分享到:
评论
1 楼 masuweng 2018-06-20  
好好好    

相关推荐

    基于springboot,cas5.3,shiro,pac4j,rest接口获取ticket不再跳转cas server登录页

    在这个过程中,我们使用Pac4J的CasClient来处理请求,获取并验证ticket,然后将用户信息返回给客户端,完成认证过程。 5. 用户认证流程优化:通过上述配置,用户在访问受保护的REST接口时,不会被重定向到CAS服务器...

    AJAX获取服务器当前时间及时间格式输出处理

    其次,客户端使用AJAX技术向Web服务发送请求并接收响应。文中提到了使用jQuery库简化AJAX调用的过程。在客户端HTML页面中,通过引入jQuery库文件(js/Jquery1.7.js),可以使用jQuery提供的$.ajax方法发起异步请求。...

    前后端分离集成cas

    在这个前后端分离的项目中,Vue.js用于构建前端页面,与后端通过Ajax进行通信,实现动态交互和数据展示。 集成CAS(Central Authentication Service)是为了实现单点登录(Single Sign-On, SSO)。CAS是一个开源的...

    decision(修正ajax error)

    在这个场景中,"decision(修正ajax error)" 指的可能是解决在实现SSO过程中遇到的与Ajax请求相关的错误。Ajax错误通常涉及到异步数据交互的问题,可能由于网络、服务器响应或JavaScript代码错误导致。 "ecology8"和...

    axios 处理 302 状态码的解决方法

    在现代Web应用程序中,尤其是单页面应用(SPA)如Vue.js,前端与后端的交互主要依赖于Ajax请求。Axios 是一个广泛使用的JavaScript库,用于处理这些异步数据请求。然而,在某些情况下,比如用户的认证令牌过期,后端...

    使用CAS整合CXF实现单点登录部署步骤

    3. **前端展示**:在登录页面上添加验证码图片和输入框,通过JavaScript或AJAX获取和验证验证码。 4. **后端验证**:在登录请求处理时,验证用户输入的验证码是否与服务器生成的验证码一致。 以上就是使用CAS整合...

    cas server assets文件

    5. **第三方库**:可能包含一些用于处理 AJAX 请求、表单验证或提供额外功能的 JavaScript 库。 **二、CAS Server Assets 在单点登录中的作用** 1. **用户界面**:Assets 文件提供了用户首次接触 CAS 时看到的登录...

    cas的assets文件

    2. **JavaScript(JS)**:JS文件负责增加页面的动态功能,如表单验证、按钮交互、AJAX请求等。在CAS中,JavaScript可能用于处理认证过程中的客户端逻辑,例如处理重定向、显示错误消息等。 3. **图片**:CAS的assets...

    技术架构规范标准.doc

    - 页面框架和技术:jQuery用于事件处理、Ajax请求和页面操作,Jqzoom处理图片放大,Jquery-validator执行输入验证。 - 权限安全控制:利用Apache Shiro框架实现,Shiro提供角色和资源的权限验证,并可与CAS单点...

    session过期问题

    2. **JavaScript心跳检测**:客户端可以通过定期发送Ajax请求(心跳检测)来保持Session活动。每当服务器接收到心跳请求时,就会刷新Session的过期时间。这种方法可以避免用户因为长时间无操作而导致Session过期,但...

    CAS:plugin.dj的自定义头像脚本

    在头像系统中,用户选择或上传头像时,AJAX可以用来向服务器发送请求并接收反馈,保持用户体验的流畅。 2. **前端表单处理**:用户上传头像通常需要填写表单,JavaScript可以用来验证输入(如文件类型、大小限制)...

    sso单点登录

    在SSO场景下,AjaxAnyWhere可能被用来无刷新地处理登录状态,比如在用户登录后自动更新页面内容,或者在用户访问受保护资源时,通过Ajax请求验证用户身份。 6. **SSODemo**:这个文件可能是一个示例项目,展示了...

    CAS:Class Attendance System&#40;课堂考勤系统&#41;

    4. **数据传输**:使用AJAX(异步JavaScript和XML)技术,JavaScript可以在后台与服务器交换数据,完成签到请求,而不会打断用户的界面体验。 5. **通知与提示**:JavaScript可以用于显示消息提示,如签到成功或...

    java 登陆系统

    在登录系统中,Struts或Struts2可以处理HTTP请求,管理视图与控制器之间的交互,以及执行业务逻辑。 AJAX(Asynchronous JavaScript and XML)则用于提升用户界面的交互性。在登录过程中,AJAX可以让用户在不刷新...

    JAVA技术架构及开发规范文档.pdf

    页面展示和控制方式多样化,包括动态同步请求、Velocity模板生成页面、Ajax异步请求。前端使用jQuery库、jqzoom插件、jQuery-validator等技术进行交互处理。权限安全控制则由Apache Shiro框架负责,它支持基于角色和...

    dealwithcors:CORS机制

    文档 ##简单请求Lorsque je fais une requête AJAX simple (Simple requests) depuis mon browser http:localhost:8888 unservice JSON 远程http:localhost:3000 。 Rappel:一个简单的跨站点请求是: 仅使用 GET...

    JAVA技术架构及开发规范文档.docx

    Shiro与CAS单点登录整合,便于扩展多个应用模块。此外,自定义了UsernamePasswordToken和Realm,实现基于验证码和数据库用户密码的登录验证。 Spring的注解控制器用于控制器层,支持返回Velocity视图、Ajax JSON和...

    jQueryAPI chm

    例如,使用jQuery的Ajax方法`$.ajax()`或`$.post()`,可以向服务器发送异步请求,更新部分页面内容,而无需刷新整个页面。 【单点登录(Single Sign-On, SSO)】是一种身份验证机制,允许用户在一个应用系统中登录...

    单点登录+在线列表+防浏览器关闭

    3. **AJAX轮询**:通过定时发送AJAX请求,服务器可以检查客户端是否在线。如果在一段时间内没有收到客户端的响应,可以认为用户已离线,然后清理相关资源。 4. **事件监听**:利用JavaScript监听浏览器的`...

Global site tag (gtag.js) - Google Analytics