【SSO单点系列】(4):CAS4.0 SERVER登录后用户信息的返回


接着上一篇,在上一篇中我们描述了怎么在CAS SERVER登录页上添加验证码,并进行登录。一旦CAS SERVER验证成功后,我们就会跳转到客户端中去。跳转到客户端去后,大家想一想,客户端总要获取用户信息吧,不然客户端是怎么知道登录的是哪个用户。那么客户端要怎么获取用户信息呢?

其实验证成功,跳转客户端这个过程中,CAS SERVER 会返回登录的相关信息给客户端,客户端只要进行获取,就能知道登录的具体是哪个用户了。不过CAS 默认只返回用户账号给客户端,那么怎么定义CAS SERVER返回的信息呢? 这就是本篇具体讲解的内容了,大家听我慢慢道来。

相关接口

在开始时,我们先了解下有关相关的几个接口

  • Credentials
  • Principal
  • IPersonAttributeDao
  • PrincipalResolver

Credentials

  Credentials (org.jasig.cas.authentication.Credentials接口,我们在上一篇其实有使用过,我们当时有用过一个叫UsernamePasswordCredential的类,就是实现了Credentials接口。这个接口是用来定义我们登录页上输入的认证信息的,比如用户名、密码、验证码等,可以理解为用户认证的相关凭据。

Principal

  Principal (org.jasig.cas.authentication.principal.Principal)接口,这个主要是用来保存用户认证后的用户信息,信息保存在一个Map中。

IPersonAttributeDao

  IPersonAttributeDao (org.jasig.services.persondir.IPersonAttributeDao)接口,这个是用来定义我们需要返回给客户端相关信息的接口,CAS SERVER 默认有提供许多实现,比如

  • LdapPersonAttributeDao :通过查询 LDAP 目录 ,来返回信息
  • SingleRowJdbcPersonAttributeDao : 通过JDBC SQL查询,来返回信息

等等,还有许多,大家可以参考源码中的实现,CAS SERVER 提供了各种功能的实现,有时候我们可以直接使用这个现成的就行了。

PrincipalResolver

  PrincipalResolver(org.jasig.cas.authentication.principal.PrincipalResolver)接口,上面有说到Credentials是从登录页面上进行获取相关用户信息的。那么认证成功后,怎么把Credentials里面的信息转换到Principal中呢,这就是这个接口的作用了。由于认证本身是没有返回用户信息的,只是确定认证是通过还是没有通过。这时还要用到我们上面的IPersonAttributeDao接口,在这接口中我们就可以定义我们需要返回的信息了。

这接口中有两个方法

  • resolve : 解析Credentials中的信息,返回Principal接口
  • supports : 判断Credentials是否支持Principal协议。

ps: 在3.x版本中没有PrincipalResolver接口,对应的是CredentialsToPrincipalResolver,PrincipalResolver这个是在4.0版本中加入的,大家要注意

流程

相关接口讲解后,大家应该对怎么返回信息有个大概的思路了。没错就是实现上面所说的IPersonAttributeDaoPrincipalResolver接口 。下面根据代码讲解下具体的一个流程:

首先打开deployerConfigContext.xml文件,看下面的定义:

 <!--
       | Resolves a principal from a credential using an attribute repository that is configured to resolve
       | against a deployer-specific store (e.g. LDAP).
       -->
    <bean id="primaryPrincipalResolver"
          class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver" >
        <property name="attributeRepository" ref="attributeRepository" />
    </bean>

    <!--
    Bean that defines the attributes that a service may return.  This example uses the Stub/Mock version.  A real implementation
    may go against a database or LDAP server.  The id should remain "attributeRepository" though.
    +-->
    <bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao"
            p:backingMap-ref="attrRepoBackingMap" />
    
    <util:map id="attrRepoBackingMap">
        <entry key="uid" value="uid" />
        <entry key="eduPersonAffiliation" value="eduPersonAffiliation" /> 
        <entry key="groupMembership" value="groupMembership" />
    </util:map>

//PersonDirectoryPrincipalResolver部分源码

public final Principal resolve(final Credential credential) {
        logger.debug("Attempting to resolve a principal...");
      
        String principalId = extractPrincipalId(credential);  //extractPrincipalId 方法从credential中抽取id

       //省略...
        final IPersonAttributes personAttributes = this.attributeRepository.getPerson(principalId);  //根据IPersonAttributeDao 中的getPerson 获取返回的属性
        final Map<String, List<Object>> attributes;

       //最终返回 Principal 
        return new SimplePrincipal(principalId, convertedAttributes);
    }    

具体流程:

1.从上面的deployerConfigContext.xml配置我们可以看到,CAS 默认配置了一个叫做PersonDirectoryPrincipalResolver的类,在 这个类的resolve方法中有调用extractPrincipalId这个方法,这个方法传入一个Credentials类型的参数,默认调用的是CredentialsgetId()方法,CAS默认是返回用户的userName,即登录账号。不过getId()这个方法的实现我们可以在上一章中指定的UsernamePasswordCredential类中自定义,一般是定义成返回用户的userId或者其他唯一键,因为我们如果知道了用户的userId,那么就可以根据这个从数据库中查询中用户的一些具体信息了,进而就可以组成我们需要返回的信息。

2. 继续往下看源码,接着在PersonDirectoryPrincipalResolver中有注入一个attributeRepository属性,这个就是上面的IPersonAttributeDao接口,然后在resolve方法中调用了IPersonAttributeDao 接口的getPerson方法,还传入了一个参数principalId,其实这个传入的参数就是我们上面getId() 返回的值。

所以其实我们只要实现我们需要的IPersonAttributeDao就可以了。 下面给一个简单的IPersonAttributeDao例子:

public class BlogStubPersonAttributeDao extends StubPersonAttributeDao {

    @Override
    public IPersonAttributes getPerson(String uid) {
        
        Map<String, List<Object>> attributes = new HashMap<String, List<Object>>();
        attributes.put("userid", Collections.singletonList((Object)uid));
        attributes.put("cnblogUsername", Collections.singletonList((Object)"http://www.cnblogs.com/vhua"));
        attributes.put("cnblogPassword", Collections.singletonList((Object)"123456"));
        attributes.put("test", Collections.singletonList((Object)"test"));
        return new AttributeNamedPersonImpl(attributes);
    }
    
}

这边传入的uid默认是用户的登录名,我们这边没有做修改,直接用默认的。

这边是只是测试用,所以就直接写死了,实际开发肯定是需要在数据库或者LDAP中进行查询后,然后组装成需要的信息 。

然后在deployerConfigContext.xml中修改

<bean id="primaryPrincipalResolver"
          class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver" >
        <property name="attributeRepository" ref="attributeRepository" />
    </bean>
<!-- 修改前 --> <bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao" p:backingMap-ref="attrRepoBackingMap" /> <util:map id="attrRepoBackingMap"> <entry key="uid" value="uid" /> <entry key="eduPersonAffiliation" value="eduPersonAffiliation" /> <entry key="groupMembership" value="groupMembership" /> </util:map> <!-- 修改前 end-->
<!--修改后--> <bean id="attributeRepository" class="org.jasig.services.persondir.support.BlogStubPersonAttributeDao" /> <!--修改后 end-->

3. 修改完成后,我们还需要在casServiceValidationSuccess.jspcas-server-webapp\src\main\webapp\WEB-INF\view\jsp\protocol\2.0\casServiceValidationSuccess.jsp)

添加一段代码(下面红色部分):

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
    <cas:authenticationSuccess>
        <cas:user>${fn:escapeXml(assertion.primaryAuthentication.principal.id)}</cas:user>
        
    <!-- 这段 -- > <c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes) > 0}"> <cas:attributes> <c:forEach var="attr" items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"> <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}> </c:forEach> </cas:attributes> </c:if> <!-- 这段 end-- >

<c:if test="${not empty pgtIou}"> <cas:proxyGrantingTicket>${pgtIou}</cas:proxyGrantingTicket> </c:if> <c:if test="${fn:length(assertion.chainedAuthentications) > 1}"> <cas:proxies> <c:forEach var="proxy" items="${assertion.chainedAuthentications}" varStatus="loopStatus" begin="0" end="${fn:length(assertion.chainedAuthentications)-2}" step="1"> <cas:proxy>${fn:escapeXml(proxy.principal.id)}</cas:proxy> </c:forEach> </cas:proxies> </c:if> </cas:authenticationSuccess> </cas:serviceResponse>

4. 接下来 在客户端设置信息的接收,我们直接在index.jsp中测试一下:

在java中可以通过下面的方式获取

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();

Map attributes = principal.getAttributes();

String xxx=attributes .get("xxx");

...

<!DOCTYPE html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>返回值测试</title>
</head>
<body>

    <% 
    request.setCharacterEncoding("UTF-8");
    AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
    Map attributes = principal.getAttributes();
    String userid=(String)attributes.get("userid"); 
    String cnblogUsername = (String)attributes.get("cnblogUsername"); 
    String cnblogPassword = (String)attributes.get("cnblogPassword"); 
    String test=(String)attributes.get("test"); 
    
    %>
    <div>飞奔的蜗牛博客:返回值演示</div>
    <ul>
        <li>userid:<%= userid%></li>
        <li>username:<%= cnblogUsername%></li>
        <li>password:<%= cnblogPassword%></li>
        <li>test:<%= test%></li>
    </ul>
</body>
</html>

效果

好了,我们登录运行看看结果。

大家看到没,已经获取成功了,在CAS SERVER那边设置的信息,我们正常获取到了 。大家可以根据业务的需要,返回相关的信息。然后在客户端进行操作。

总结

这一篇本来昨晚就写好了,准备发布,结果因为太困,保存了草稿,上午才发现,大家凑合的看。

优质内容筛选与推荐>>
1、Java_判断文件是否写入完成
2、uva11111 Generalized Matrioshkas
3、Django框架之缓存数据库
4、Mantis配置
5、关于global和$GLOBALS[]的一些实践


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn