CAS客户端过滤器源码备忘

AuthenticationFilter中doFilter方法源码:

 

01. public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throwsIOException, ServletException {
02. final HttpServletRequest request = (HttpServletRequest) servletRequest;
03. final HttpServletResponse response = (HttpServletResponse) servletResponse;
04. final HttpSession session = request.getSession(false);
05. final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
06.  
07. // 如果session中的CONST_CAS_ASSERTION(即"_const_cas_assertion_")不为空,则跳过此过滤器
08. if (assertion != null) {
09. filterChain.doFilter(request, response);
10. return;
11. }
12.  
13. final String serviceUrl = constructServiceUrl(request, response);
14. final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
15. final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
16.  
17. // 如果ticket参数不为空,则跳过此过滤器
18. if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
19. filterChain.doFilter(request, response);
20. return;
21. }
22.  
23. final String modifiedServiceUrl;
24.  
25. log.debug("no ticket and no assertion found");
26. if (this.gateway) {
27. log.debug("setting gateway attribute in session");
28. modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
29. else {
30. modifiedServiceUrl = serviceUrl;
31. }
32.  
33. if (log.isDebugEnabled()) {
34. log.debug("Constructed service url: " + modifiedServiceUrl);
35. }
36.  
37. final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
38.  
39. if (log.isDebugEnabled()) {
40. log.debug("redirecting to "" + urlToRedirectTo + """);
41. }
42.  
43. // 否则跳转到CAS Server登录页面
44. response.sendRedirect(urlToRedirectTo);
45. }

通过源码可以看出这个过滤器的处理流程:

 

如果session中包含name为"_const_cas_assertion_"的属性,也就是用户已经登录过了,则跳过此过滤器。

如果ticket参数不为空,即可能是登录后跳转回来的URL,则跳过此过滤器。(注意,AuthenticationFilter只判断ticket是否为空,并不做ticket合法校验,也就是随便输入一个ticket参数在URL中都可以通过此过滤器。而负责校验ticket的是第二个过滤器:TicketValidationFilter 。)

如果上面两个条件都不满足,也就是既没有"_const_cas_assertion_"的session又没有ticket参数,则跳转到XML配置的casServerLoginUrl,让用户到CAS Server上登录,并在URL加上一个参数service(即XML配置的serverName加上相对路径,用于登录成功后返回登录前的页面)。

下面来测试一下,web.xml先只配置一个过滤器,去掉其他过滤器:

浏览器中输入地址http://localhost:9999/a/b/c并打开,会跳到登录页面,也就是XML中配置的casServerLoginUrl,并加上一个用于返回登录前页面的参数,这个参数由XML配置的serverName加上路径/a/b/c生成,即http://localhost:8080/cas/login?service=http://localhost:9999/a/b/c。再次页面输入用户名、密码登录,如果登录成功,则跳转到service参数指定的页面,并加上一个参数ticket,即http://localhost:9999/a/b/c?ticket=ST-12-1XGQRUtFnwtqQxdNLOdv-cas01.example.org。

这里可以再测试一下AuthenticationFilter是否校验ticket合法性。例如在浏览器中打开http://localhost:9999/a/b/c?ticket=123,其中ticket参数是随便写的,肯定不是合法的,但是访问可以直接进入页面,不再需要登录。也就是AuthenticationFilter只判断ticket是否为空,并不校验是否合法。

2、TicketValidationFilter

由于TicketValidationFilter、Cas20ProxyReceivingTicketValidationFilter都继承自AbstractTicketValidationFilter,下面看AbstractTicketValidationFilter中的doFilter方法源码:

 

01. public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throwsIOException, ServletException {
02.  
03. if (!preFilter(servletRequest, servletResponse, filterChain)) {
04. return;
05. }
06.  
07. final HttpServletRequest request = (HttpServletRequest) servletRequest;
08. final HttpServletResponse response = (HttpServletResponse) servletResponse;
09. final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
10.  
11. // 如果ticket不为空,则校验此ticket,否则直接跳过此过滤器
12. if (CommonUtils.isNotBlank(ticket)) {
13. if (log.isDebugEnabled()) {
14. log.debug("Attempting to validate ticket: " + ticket);
15. }
16.  
17. try {
18. // 校验ticket,校验失败抛出异常
19. final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));
20.  
21. if (log.isDebugEnabled()) {
22. log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());
23. }
24.  
25. request.setAttribute(CONST_CAS_ASSERTION, assertion);
26.  
27. if (this.useSession) {
28. // 设置session中的CONST_CAS_ASSERTION(即"_const_cas_assertion_")属性
29. request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion);
30. }
31. onSuccessfulValidation(request, response, assertion);
32.  
33. if (this.redirectAfterValidation) {
34. log. debug("Redirecting after successful ticket validation.");
35.  
36. // URL去掉ticket参数并跳转
37. response.sendRedirect(constructServiceUrl(request, response));
38. return;
39. }
40. catch (final TicketValidationException e) {
41. response.setStatus(HttpServletResponse.SC_FORBIDDEN);
42. log.warn(e, e);
43.  
44. onFailedValidation(request, response);
45.  
46. if (this.exceptionOnValidationFailure) {
47. throw new ServletException(e);
48. }
49.  
50. return;
51. }
52. }
53.  
54. filterChain.doFilter(request, response);
55.  
56. }

 

通过源码可以看出这个过滤器的处理流程:

如果有ticket参数,校验ticket是否合法(不合法则异常:org.jasig.cas.client.validation.TicketValidationException: CAS Server could not validate ticket)。

如果合法则在session加入"_const_cas_assertion_",并再次跳转,这次跳转主要就是去掉ticket参数,即从http://localhost:9999/a/b/c?ticket=ST-12-1XGQRUtFnwtqQxdNLOdv-cas01.example.org跳转到http://localhost:9999/a/b/c。(这样做有个好处就是如果用户F5刷新页面,由于已经没有ticket参数,不会再次去校验ticket,而同一个ticket只能使用一次,再次去CAS服务器校验会出现TicketValidationException异常。)

如果没有ticket参数,则直接跳过此过滤器(没有ticket参数的话就一定是session中包含"_const_cas_assertion_"的属性,否则连第一个过滤器AuthenticationFilter都无法通过)。

整理一下整个登录流程:

第一次请求应用服务器:

当用户第一次访问应用服务器的URL,由于session中没有"_const_cas_assertion_"且参数中没有ticket,会被AuthenticationFilter跳转到CAS服务器的登录页面。

第二次请求应用服务器:

在CAS服务器的登录页面成功登录以后,会跳转到应用服务器登录前的页面,但是加上了一个参数ticket。此次请求由于有ticket参数,通过了AuthenticationFilter,但是TicketValidationFilter会对ticket进行校验,校验成功后,会在session中加入"_const_cas_assertion_",再去掉ticket参数进行一次跳转。

第三次请求应用服务器:

此时由于session中已经有了"_const_cas_assertion_",会通过AuthenticationFilter,由于没有ticket参数,也通过了TicketValidationFilter,也就是可以正常显示出这个页面了。以后再请求应用服务器就和这次一样了,由于session包含"_const_cas_assertion_"即可正常访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值