CAS 实现单点登录(SSO)返回更多用户信息(五)

配置转换器返回更多用户信息

从cas server登录成功后,默认只能从cas server得到用户名。但程序中也可能遇到需要得到更多如姓名,手机号,email等更多用户信息的情况。

cas client拿到用户名后再到数据库中查询,的确可以得到关于该用户的更多信息。

但是如果用户登录成功后,直接从cas server返回给cas client用户的详细信息,这也是一个不错的做法。这个好处,尤其是在分布式中得以彰显,cas
server可以把用户信息传递给各个应用系统,如果是上面那种做法,那么各个系统得到用户名后,都得去数据库中查询一遍,无疑是一件重复性工作。

一、首先需要配置属性attributeRepository

首先,你需要到WEB-INF目录找到 deployerConfigContext.xml文件,同时配置
attributeRepository 如下:

<bean  class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao" id="attributeRepository">
        <constructor-arg index="0" ref="dataSource"/>
        <constructor-arg index="1" value="select * from t_user where {0}"/>
        <property name="queryAttributeMapping">
            <map>
				<!--这里的key需写username和登录页面一致,value对应数据库用户名字段-->
                <entry key="username" value="loginname"/> 

            </map>
        </property>
        <property name="resultAttributeMapping">
            <map>
				<!--key为对应的数据库字段名称,value为提供给客户端获取的属性名字,系统会自动填充值-->
                <entry key="Id" value="Id"/>
                <entry key="password" value="password"/>
                <entry key="age" value="age"/>
            </map>
        </property>
       <!--  <property name="queryType">
			<value>OR</value>
      </property> -->

    </bean>

其中:

切记:查询出来的字段名中间不能使用 _ (下划线),否则获取不到数据,如 cell_phone 需要 设置别名为 cellPhone.

queryAttributeMapping是组装sql用的查询条件属性,上述配置后,结合封装成查询sql就是 select*
from userinfo where loginname=#username#

resultAttributeMapping是sql执行完毕后返回的结构属性, key对应数据库字段,value对应客户端获取参数。

如果要组装多个查询条件,需要加上下面这个,默认为AND

<property name="queryType">

<value>OR</value>

</property>

二、配置用户认证凭据转化的解析器

也是在 deployerConfigContext.xml中,为 UsernamePasswordCredentialsToPrincipalResolver注入
attributeRepository,那么 attributeRepository就会被触发并通过此类进行解析,红色为新添部分。

<property name="credentialsToPrincipalResolvers">
			<list>
				<bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver" >
					<property name="attributeRepository" ref="attributeRepository" />
				</bean>
				<bean
					class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
			</list>
		</property>
 

三、修改 deployerConfigContext.xml

中的 org.jasig.cas.services.InMemoryServiceRegistryDaoImpl的属性 registeredServices

修改 registeredServices  列表中的每个协议中的 allowedAttributes属性的值。列出的每个值,在客户端就可以访问了

<bean
		id="serviceRegistryDao"
        class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl">
            <property name="registeredServices">
                <list>
                    <bean class="org.jasig.cas.services.RegexRegisteredService">
                        <property name="id" value="0" />
                        <property name="name" value="HTTP and IMAP" />
                        <property name="description" value="Allows HTTP(S) and IMAP(S) protocols" />
                        <property name="serviceId" value="^(https?|imaps?)://.*" />
                        <property name="evaluationOrder" value="10000001" />						

						   <property name="allowedAttributes">
                                <list>
                                        <value>Id</value>
                                        <value>password</value>
                                         <value>age</value>
                                </list>
                        </property>

                    </bean>

此步骤灰常重要,可以看看 org.jasig.cas.services.RegexRegisteredService的源码,其中的 allowedAttributes是关键

【提示】网上说此bean中的ignoreAttributes属性默认是不添加用户信息,查看了
CAS 3.5.2版本的 AbstractRegisteredService 源码后,发现其默认值就是 false,即:添加属性后,客户端就可见了

四、修改casServiceValidationSuccess.jsp

WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp

在server验证成功后,这个页面负责生成与客户端交互的xml信息,在默认的casServiceValidationSuccess.jsp中,只包括用户名,并不提供其他的属性信息,因此需要对页面进行扩展,如下,红色为新添加部分

<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
  <cas:authenticationSuccess>
<cas:user>${fn:escapeXml(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].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>

<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>

通过完成上面四个步骤的配置后,CAS Server端的工作就完成了,那么如何在客户端获取这些信息呢?下面进行说明:

cas client获取用户信息:

AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
Map attributes = principal.getAttributes();

String email=attributes .get("age");

补充:

cas_client项目结构:

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>cas_client</display-name>
  <welcome-file-list>
    <welcome-file>userInfoView.jsp</welcome-file>
  </welcome-file-list>

 <!-- ======================== 单点登录开始 ======================== -->
                 <!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置-->
                <listener>
                        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
                </listener>
                <filter>
                        <filter-name>CAS Single Sign Out Filter</filter-name>
                        <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
                </filter>
                <filter-mapping>
                        <filter-name>CAS Single Sign Out Filter</filter-name>
                        <url-pattern>/*</url-pattern>
                </filter-mapping>

				<filter>
                        <filter-name>CAS 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</param-value>
                        </init-param>
                        <init-param>
                                <param-name>serverName</param-name>
                                <param-value>http://localhost:18080</param-value>
                        </init-param>
                </filter>
                <filter-mapping>
                        <filter-name>CAS Filter</filter-name>
                        <url-pattern>/*</url-pattern>
                </filter-mapping>

                  <!-- 该过滤器负责对Ticket的校验工作,必须启用它 -->
                <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:18080</param-value>
                        </init-param>
                </filter>
                <filter-mapping>
                        <filter-name>CAS Validation Filter</filter-name>
                        <url-pattern>/*</url-pattern>
                </filter-mapping>

               <!--   该过滤器负责实现HttpServletRequest请求的包裹, -->
               <!--  比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 -->

                <filter>
                        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
                        <filter-class>
                                org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
                </filter>
                <filter-mapping>
                        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
                        <url-pattern>/*</url-pattern>
                </filter-mapping>

             <!--  该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 -->
             <!--  比如AssertionHolder.getAssertion().getPrincipal().getName()。根据客户端获取的方式可以选择使用这两种 -->

               <!--  <filter>
                        <filter-name>CAS Assertion Thread Local Filter</filter-name>
                        <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
                </filter>
                <filter-mapping>
                        <filter-name>CAS Assertion Thread Local Filter</filter-name>
                        <url-pattern>/*</url-pattern>
                </filter-mapping> -->

<!-- ======================== 单点登录结束 ======================== -->

   <servlet>
       <servlet-name>HelloWorldExample</servlet-name>
       <servlet-class>com.tgb.cas.client.HelloWorldExample</servlet-class>
   </servlet>

   <servlet-mapping>
      <servlet-name>HelloWorldExample</servlet-name>
      <url-pattern>/servlet/HelloWorldExample</url-pattern>
  </servlet-mapping>
</web-app>

HelloWorldExample:

public class HelloWorldExample extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();

		out.println("<html>");
		out.println("<head>");

		String title = "Hello";

		out.println("<title>" + title + "</title>");
		out.println("</head>");
		out.println("<body bgcolor=\"white\">");

		out.println("<a href=\"../helloworld.html\">");
		out.println("<img src=\"../images/code.gif\" height=24 "
				+ "width=24 align=right border=0 alt=\"view code\"></a>");
		out.println("<a href=\"../index.html\">");
		out.println("<img src=\"../images/return.gif\" height=24 "
				+ "width=24 align=right border=0 alt=\"return\"></a>");
		out.println("<h1>" + title + "</h1>");
		//以下是两种获取用户信息的两种方式,分别与Web.XML中的配置相对应,大家结合理解

		// 通过 CAS HttpServletRequest Wrapper Filter 获取用户信息
		String userNameString = request.getRemoteUser();

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

		if (null != principal) {
			Map<String, Object> attMap = principal.getAttributes();
			out.println(" Log | getAttributes Map size = " + attMap.size() + "<br>");
			for (Entry<String, Object> entry : attMap.entrySet()) {
				out.println("     | " + entry.getKey() + "=:" + entry.getValue() + "<br>");
			}
			String username = null;
			out.print(" Log | UserName:");
			if (null != principal) {
				username = principal.getName();
				out.println("<span style='color:red;'>" + username + "</span><br>");
			}
		}

		// 通过CAS Assertion Thread Local Filter  获取用户信息,共两种方式
//		Assertion assertion = (Assertion) request.getSession().getAttribute(
//				AbstractCasFilter.CONST_CAS_ASSERTION);
//		if (null != assertion) {
//
//			Map<String, Object> attMap = assertion.getPrincipal().getAttributes();
//			out.println(" Log | getAttributes Map size = " + attMap.size() + "<br>");
//			for (Entry<String, Object> entry : attMap.entrySet()) {
//				out.println("     | " + entry.getKey() + "=:" + entry.getValue() + "<br>");
//			}
//
//			AttributePrincipal principal = assertion.getPrincipal();
//			// AttributePrincipal principal = (AttributePrincipal) request
//			// .getUserPrincipal();
//			String username = null;
//			out.print(" Log | UserName:");
//			if (null != principal) {
//				username = principal.getName();
//				out.println("<span style='color:red;'>" + username + "</span><br>");
//			}
//		}

		out.println("</body>");
		out.println("</html>");
	}
}

userInfoView.jsp(未使用):

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<a href="servlet/HelloWorldExample">获取Server端用户信息</a>
	<br>
	<a href="http://localhost:8080/cas/logout?service=http://localhost:18080/cas_client/servlet/HelloWorldExample">单点退出</a>
</body>
</html>

效果:

访问:http://localhost:18080/cas_client/servlet/HelloWorldExample

附:

其中cas服务端对应的代码是官网中提供的cas-server-webapp-3.5.2.war,而cas_client客户端代码是我们自己编写的,参照tomcat中的example项目。

总结:

CAS返回更多用户信息这方面的文章有很多,在这里我再次进行总结学习也是为了梳理自己的知识,让自己加深理解。以上只是一个简单的知识点,大家理解就好,接下来会结合Spring进行学习。

时间: 2024-08-02 20:46:01

CAS 实现单点登录(SSO)返回更多用户信息(五)的相关文章

Atitit. 单点登录sso 的解决方案 总结

Atitit.  单点登录sso 的解决方案 总结 1. 系统应用场景and SSO模式选型 2 2. 系统应用的原则与要求 2 2.1. 开发快速简单::绝大部分系统来说,开发快速简单为主 2 2.2. 支持token交换,这样有利于集成先有的系统模块无需大改动,仅仅需要改动登陆模块.. 2 2.3. 支持用户名映射.当多个子系统username不同时候儿 2 3. 脱机验证sso (分散认证,类似护照的识别方式) 2 3.1. 适合场景:: 高性能场景,支持单方面改造 3 3.2. 缺点::

CAS单点登录(SSO)完整教程

转:http://blog.csdn.net/frinder/article/details/7969925 CAS单点登录(SSO)完整教程(2012-02-01更新) 一.教程说明 前言 教程目的:从头到尾细细道来单点登录服务器及客户端应用的每个步骤 单点登录(SSO):请看百科解释猛击这里打开 本教程使用的SSO服务器是Yelu大学研发的CAS(Central Authentication Server), 官网:http://www.jasig.org/cas 本教程环境: Tomcat

JAVA CAS单点登录(SSO) 教程

一.教程前言 教程目的:从头到尾细细道来单点登录服务器及客户端应用的每个步骤 单点登录(SSO):请看百科解释猛击这里打开 本教程使用的SSO服务器是Yelu大学研发的CAS(Central Authentication Server),官网:http://www.jasig.org/cas 本教程环境: Tomcat6.0.29 JDK6 CAS Server版本:cas-server-3.4.3.1 CAS Client版本:cas-client-3.1.12 教程撰写日期:2010-11-

CAS 实现单点登录(SSO)数据库查询认证机制-xml方式(三)

继前面介绍过基于CAS实现单点登录(SSO)的实例演示,演示过程中服务端认证机制采用的是默认配置即CAS Servier默认用户名和密码一致即可登录成功,那么本文将侧重于应用方面,真正通过查询用户名密码来进程验证用户是否可以登录. CAS Server添加相关的jar包 需要在web项目的lib下添加两个包:cas-server-support-jdbc-x.x.x.jar和 mysql-connector-java-x.x.x-bin.jar(具体版本号根据情况而定) 修改CAS Server

(四)SSO之CAS框架单点登录,修改验证数据库的方式

应需求的变化,在登录cas的时候,默认根据用户名和密码进行验证,如果加上用户名,密码和一个系统标识进行验证呢?该如何做呢? 我们知道cas默认的登录界面中,输入的用户名和密码,再配置一下deployerConfigContext.xml 这个文件中的bean  org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler 的这个标签,写上对应的sql,以及在<bean id="dataSource" class=&q

cas 单点登录(SSO)实验之二: cas-client

cas 单点登录(SSO)实验之二: cas-client 參考文章: http://my.oschina.net/indestiny/blog/200768#comments http://wenku.baidu.com/view/0bcc0d01e87101f69e319595.html 接上一篇文章: cas 单点登录(SSO)实验之中的一个: jasig cas-server 安装 本文说明怎样写一个web服务(cas-study).使用cas-server提供的验证服务.当用户訪问这个

cas 单点登录(SSO)之一: jasig cas-server 安装

cas 单点登录(SSO)实验之一: jasig cas-server 安装 参考文章: http://my.oschina.net/indestiny/blog/200768#comments http://wenku.baidu.com/view/0bcc0d01e87101f69e319595.html SSO原理不多重复,需要理解的就一点,一个复杂系统需要一个唯一的验证服务, 这就是CAS(Central Authentication Service) Server.系统内的各种服务(W

十六、【适合中小企业的.Net轻量级开源框架】EnterpriseFrameWork框架核心类库之单点登录SSO

回<[开源]EnterpriseFrameWork框架系列文章索引> EFW框架源代码下载:http://pan.baidu.com/s/1qWJjo3U 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 如上图,当用户第一次访问应用系统1的时候,因为还没有登录,会被引导到认证系统中进行登录:根据用户提供的登录信息,认证系统进行身份校验,如果通过校验,应该返

单点登录SSO的实现原理 (转)

单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任.单点登录在大型网站里使用得非常频繁,例如像阿里巴巴这样的网站,在网站的背后是成百上千的子系统,用户一次操作或交易可能涉及到几十个子系统的协作,如果每个子系统都需要用户认证,不仅用户会疯掉,各子系统也会为这种重复认证授权的逻辑搞疯掉.实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性,因