zoukankan      html  css  js  c++  java
  • Cas_Java客户端登录相关过滤器的处理流程

    • 首先了解一下CAS登录原理:

      1、CAS结构中一般包含CAS服务器(Cas验证服务器)、应用服务器(程序所在服务器)、客户端(web浏览器)三个部分

      2、客户端向应用服务器发出请求,由于未登录,会被跳转到CAS服务器登录。

      3、登录成功后跳转回应用服务器的登录前的URL,但是CAS服务器会给URL加上一个ticket参数。

      4、应用服务器(这里应该是指业务服务器)拿着ticket去CAS服务器验证,验证成功后即加入一个session表示已登录,以后就不用再次登录了。

    •  

      在web.xml配置中,AuthenticationFilter和TicketValidationFilter两个过滤器是负责处理登录流程的。

      web.xml配置:(serverName表示上述的应用服务器)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      <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://localhost:8080/cas/login</param-value>
          </init-param>
          <init-param>
               /* 中心服务器地址 */

              <param-name>serverName</param-name>
              <param-value>http://localhost:9999</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>CAS Authentication Filter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>
      <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://localhost:8080/cas</param-value>
          </init-param>
          <init-param>
              /* 中心服务器地址 */


              <param-name>serverName</param-name>
              <param-value>http://localhost:9999</param-value>
          </init-param>
      </filter>
      <filter-mapping>
          <filter-name>CAS Validation Filter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>

              

      1、AuthenticationFilter

       

      AuthenticationFilter中doFilter方法源码:

      a)验证session是否有值(用户数据放在session中)
      b)验证是否有ticket(不验证其正确性)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
              final HttpServletRequest request = (HttpServletRequest) servletRequest;
              final HttpServletResponse response = (HttpServletResponse) servletResponse;
              final HttpSession session = request.getSession(false);
              final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
       
              // 如果session中的CONST_CAS_ASSERTION(即"_const_cas_assertion_")不为空,则跳过此过滤器
              //用户账号在服务端统一管理,session放在服务端,所以是公用的
              if (assertion != null) {
                  filterChain.doFilter(request, response);
                  return;
              }
       
              final String serviceUrl = constructServiceUrl(request, response);
              final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
              final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
       
              // 如果ticket参数不为空,则跳过此过滤器
              // ticket有值则直接执行下一个filter,先不验证其正确性
              if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
                  filterChain.doFilter(request, response);
                  return;
              }
       
              final String modifiedServiceUrl;
       
              log.debug("no ticket and no assertion found");
              if (this.gateway) {
                  log.debug("setting gateway attribute in session");
                  modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
              } else {
                  modifiedServiceUrl = serviceUrl;
              }
       
              if (log.isDebugEnabled()) {
                  log.debug("Constructed service url: " + modifiedServiceUrl);
              }
       
              final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
               //modifiedServiceUrl 登录前的参数
       
              if (log.isDebugEnabled()) {
                  log.debug("redirecting to "" + urlToRedirectTo + """);
              }
       
              // 跳转到CAS Server登录页面,让用户登录(第一道验证未过)
              response.sendRedirect(urlToRedirectTo);
          }

        

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

       

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

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

      3、如果上面两个条件都不满足,也就是既没有"_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方法源码:

       

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
       
          if (!preFilter(servletRequest, servletResponse, filterChain)) {
              return;
          }
       
          final HttpServletRequest request = (HttpServletRequest) servletRequest;
          final HttpServletResponse response = (HttpServletResponse) servletResponse;
          final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
       
          // 如果ticket不为空,则校验此ticket,否则直接跳过此过滤器
          //经过AuthenticationFilter的过滤,ticket应该不为空,只是不能保证他的正确性
          if (CommonUtils.isNotBlank(ticket)) {
              if (log.isDebugEnabled()) {
                  log.debug("Attempting to validate ticket: " + ticket);
              }
       
              try {
                  // 校验ticket,校验失败抛出异常(检验ticket应该是做对比)
                  final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response));
       
                  if (log.isDebugEnabled()) {
                      log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());
                  }
       
                  //AuthenticationFilter中是从session取这个值
                  request.setAttribute(CONST_CAS_ASSERTION, assertion);
       
                  if (this.useSession) {
                      // 设置session中的CONST_CAS_ASSERTION(即"_const_cas_assertion_")属性
                      request.getSession().setAttribute(CONST_CAS_ASSERTION, assertion);
                  }
                  onSuccessfulValidation(request, response, assertion);
       
                  if (this.redirectAfterValidation) {
                      log. debug("Redirecting after successful ticket validation.");
       
                      // URL去掉ticket参数并跳转
                      response.sendRedirect(constructServiceUrl(request, response));
                      return;
                  }
              } catch (final TicketValidationException e) {
                  response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                  log.warn(e, e);
       
                  onFailedValidation(request, response);
       
                  if (this.exceptionOnValidationFailure) {
                      throw new ServletException(e);
                  }
       
                  return;
              }
          }
       
          filterChain.doFilter(request, response);
       
      }

      AuthenticationFilter

       

      通过源码可以看出这个过滤器的处理流程:【很重要,ticket只是用一次】

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

      2、如果合法则在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,但是session有值,AuthenticationFilter一样会跳转到下一个Filter)由于已经没有ticket参数,不会再次去校验ticket,而同一个ticket只能使用一次,再次去CAS服务器校验会出现TicketValidationException异常。)

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

      整理一下整个登录流程:

      1、第一次请求应用服务器:

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

      2、第二次请求应用服务器:

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

      3、第三次请求应用服务器:

      此时由于session中已经有了"_const_cas_assertion_",会通过AuthenticationFilter,由于没有ticket参数,也通过了TicketValidationFilter,也就是可以正常显示出这个页面了。以后再请求应用服务器就和这次一样了,由于session包含"_const_cas_assertion_"即可正常访问。(以后每次都是根据session进行验证,直到单点登出清空session)

  • 相关阅读:
    Java 抽象类 初学者笔记
    JAVA super关键字 初学者笔记
    Java 标准输入流 初学者笔记
    JAVA 将对象引用作为参数修改实例对象参数 初学者笔记
    JAVA 根据类构建数组(用类处理数组信息) 初学者笔记
    JAVA 打印日历 初学者笔记
    Python 测试代码 初学者笔记
    Python 文件&异常 初学者笔记
    Python 类 初学者笔记
    ubuntu网络连接失败
  • 原文地址:https://www.cnblogs.com/gossip/p/5651503.html
Copyright © 2011-2022 走看看