zoukankan      html  css  js  c++  java
  • CAS认证(3):验证用户信息

    这篇文章主要是对用户提交的用户名及密码等进行认证。该过程相对简单一些,但我们更多的是学习他的设计思路。

    上一篇文章中,我们在authenticationViaFormAction 中的submit方法中TGT的生成是通过

    String tempTGT= this.centralAuthenticationService.createTicketGrantingTicket(credentials);

    方法来生成的。

    在该方法中,参数credentials就是对用户提交的认证信息的封装,该对象主要就是简单的包含了用户名和密码等。

    那我们进详细的看一看该方法中逻辑行为吧。

    CentralAuthenticationService 接口是CAS中的核心接口,他定了CAS中核心的一些行为。

    createTicketGrantingTicket 该方法主要是验证认证信息,生成TGT。
    grantServiceTicket 该方法主要是生成ST
    validateServiceTicket 该方法主要是验证ST有效性。
    destroyTicketGrantingTicket   该方法主要是销毁TGT
    delegateTicketGrantingTicket  该方法主要是代理票据的生成

    可以看到,这些都是认证流程中的核心方法。

    该接口的默认实现是 CentralAuthenticationServiceImpl 。我们先来看一下createTicketGrantingTicket方法

    final Authenticationauthentication = this.authenticationManager.authenticate(credentials);
    finalTicketGrantingTicket ticketGrantingTicket= new TicketGrantingTicketImpl(
        this.ticketGrantingTicketUniqueTicketIdGenerator.getNewTicketId(TicketGrantingTicket.PREFIX),
        authentication, this.ticketGrantingTicketExpirationPolicy);
    this.ticketRegistry.addTicket(ticketGrantingTicket);
    return ticketGrantingTicket.getId();

          这里可以看到, 代码中先是通过调用this.authenticationManager.authenticate方法对credentials进行验证。并返回验证结果 authentication。在这个过程中,如果验证出现问题,是会抛出异常的。

       然后通过构建一个TicketGrantingTicketImpl 对象来形成TGT对应信息。之前说过,TGT是以cookie的形式存在的。那是在客户端浏览器中存在形式。在server端测试存在一个ticketGrantTicket对象的。这一点同session与jessionid的关系非常像。生成的tgt存储在ticketRegistry中。然后返回TGT。这里先不对ticket的生成和管理进行讲解,后面会详细的说明,这里继续讲解认证的过程。

     this.authenticationManager.authenticate(credentials);

    我们要看一下该方法中的认证是怎么样的,首先要看一下这个类的结构是什么样的。CAS中对象的管理主要是通过spring的IOC实现的。关于认证的bean的配置主要是在deployConfigContext.xml中定义的。在该配置文件中

    复制代码
    <bean id="authenticationManager" class="org.jasig.cas.authentication.AuthenticationManagerImpl">
    <!--
        credentialsToPrincipalResolvers主要是干两件事儿,
        一是确认用户要授权,在默认配置中用的DefaultCredentialsToPrincipalResolver来填充角色
        二是用这些分解器确认一个服务请求代理票据验证。这个需要明确的是代理登录流程        
    +-->
      <property name="credentialsToPrincipalResolvers">
        <list>
           <!--
            这个UsernamePasswordCredentials是支持用户名密码登录的解析器。如果采用其他的认证方式,那么需要替换解析器。这种解析器需要支持这种认证方式
           +-->
           <bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver">
              <property name="attributeRepository">
                 <ref local="attributeRepository"/>
               </property>
           </bean>        
    
           <!--这个类是表示支持http或是https协议的。他可以从URL获取凭证,并且可以根据回调地址获取服务地址。如果采用其他的通讯协议,可以进行相应的替换+-->
           <bean class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver"/>
        </list>
      </property>
    
      <!-- 这里的list主要是维护了一个认证器链,用来对用户提交的认证信息进行认证。  +-->
      <property name="authenticationHandlers">
        <list>
           <bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
              p:httpClient-ref="httpClient"/>
           <bean  class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
                <property name="dataSource"ref="dataSource" />
                <property name="sql"value="select USER_PASSWORD fromt_sso_userinfo where USER_NAME=? " />
           </bean>
        </list>
      </property>
    </bean>
    复制代码

    我们可以看到,在manager中,主要有两个list的属性:credentialsToPrincipalResolvers和authenticationHandlers

    其中authenticationHandlers list主要是用来做认证用的。该list中所有的bean都需要实现AuthenticationHandler接口中的authenticate方法。用户提交认证请求之后,要满足该list中任意认证的条件才算是认证成功。每一个bean都可以配置自己的验证方式。所以,对于有多个认证方式的应用的时候,在这里自行组装认证条件就可以。

    credentialsToPrincipalResolverslist属性主要是在验证成功之后,将用户属性提取出来。并传递到接入系统中。如果用户数据存储在存在多个数据源中,则在这里可以写多个属性提取器,分别将用户的属性提取出来,然后处理后传递给客户端。

    下面我们就源码进行分析。查看AuthenticationManagerImpl.authenticateAndObtainPrincipal方法:

    复制代码
    //通过验证器链对用户提供的认证信息进行认证
    for (final AuthenticationHandlerauthenticationHandler : this.authenticationHandlers) {
        if(authenticationHandler.supports(credentials)) {
            foundSupported = true;
            if(!authenticationHandler.authenticate(credentials)) {
                。。。。。
            } else {
               。。。。。
                authenticatedClass =authenticationHandler;
                authenticated = true;
                break;
            }
        }
    }
    复制代码

    这里可以看到,就是对authenticationHandlers 进行遍历,如果通过认证,则break。

    复制代码
    foundSupported = false;
    //认证成功之后,通过认证信息,提取出用户的完整信息
    for (finalCredentialsToPrincipalResolver credentialsToPrincipalResolver : this.credentialsToPrincipalResolvers) {
        if(credentialsToPrincipalResolver.supports(credentials)) {
            final Principal principal =credentialsToPrincipalResolver
               .resolvePrincipal(credentials);
            foundSupported = true;
            if (principal != null) {
                return new Pair<AuthenticationHandler,Principal>(authenticatedClass,principal);
            }
        }
    }
    复制代码

    这里对credentialsToPrincipalResolvers进行解析,一旦获取到用户的属性,则构建了一个新的Pair对象,该对象中有认证的类和用户属性。

    下面,我们就深入org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler看一下该类是如何对用户进行认证的。

    这里,我对这个类简单看一下,credentials是对认证请求的简单封装。当然可以获取到用户名和密码。然后将密码进行加密之后,同数据库查询的结果进行比对。

    复制代码
    final Stringusername = getPrincipalNameTransformer().transform(credentials.getUsername());
    final String password =credentials.getPassword();
    final String encryptedPassword = this.getPasswordEncoder().encode(
        password);
    try {
        final String dbPassword =getJdbcTemplate().queryForObject(this.sql, String.class,username);
        returndbPassword.equals(encryptedPassword);
    } catch (finalIncorrectResultSizeDataAccessException e) {
        // this means the username was notfound.
        return false;
    }
    复制代码

    CAS中,对于数据库的操作主要都是使用spring的JdbcTemplate 来操作的。这里也不例外。 

    这里的业务逻辑比较简单。在回头看看这里的实现。他将能分离出来的组件全都独立出来。比如说密码加密算法。数据源,查询语句等。任何组件的更改不会对其他的组件产生影响。这就是面向对象的好处,这就是使用spring ioc的好处。

  • 相关阅读:
    AcWing 3772. 更新线路(BFS)
    AcWing 3760. 最大剩余油量(树的最长路径)
    AcWing 3771. 选取石子
    洛谷P2014—选课(树形DP)
    吴恩达机器学习ex1
    洛谷P1122—最大子树和(树形DP)
    论文中关于要使用600线的python代码
    mysql 笛卡尔积
    mysql 外键
    写参考文献有感
  • 原文地址:https://www.cnblogs.com/zzhuyongxin/p/4600307.html
Copyright © 2011-2022 走看看