码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-04-19 10:14:52      阅读:337      评论:0      收藏:0      [点我收藏+]

标签:

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

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#

resultAttributeMappingsql执行完毕后返回的结构属性, 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进行学习。



 


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

标签:

原文地址:http://blog.csdn.net/hejingyuan6/article/details/45126373

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!