单点登录(八)cas支持客户端登录——客户端
来源:互联网 发布:mysql 存储过程 循环 编辑:程序博客网 时间:2024/05/21 17:06
客户端即指使用CAS中央认证服务器的应用程序,而不是指用户浏览器
客户端实现主要需要满足5个case:
· 1. 用户未在中央认证服务器登陆,访问客户端受保护资源时,客户端重定向到中央认证服务器请求TGT认证,认证失败,转回客户端登陆页面,保证受保护资源URL信息不丢失
· 2. 用户未在中央认证服务器登陆,访问客户端登陆页面时,客户端重定向到中央认证服务器请求TGT认证,认证失败,转回客户端登陆页面,此次登录页面不再受保护,允许访问
· 3. 用户已在中央认证服务器登陆,访问客户端受保护资源时,客户端重定向到中央认证服务器请求TGT认证,认证成功,直接转回受保护资源
· 4. 用户在客户端登陆页面提交用户名密码,客户端将用户名密码信息提交给服务器端,认证失败,转回客户端登陆页面,携带失败信息并保证转到登陆页面前受保护资源URL信息不丢失
· 5. 用户在客户端登陆页面提交用户名密码,客户端将用户名密码信息提交给服务器端,认证成功,转回转到登陆页面前受保护资源
对于case 1和case 3,普通的CAS客户端即可满足需求,但对于case 4和case 5,则需要我们定制自己的登陆页面。对于case 2,主要是需要满足部分登陆页面希望在用户未登陆状态显示登陆框,在已登陆状态显示用户欢迎信息的需求,实现这个需求我们是通过让CAS客户端认证器满足一个排除约定,即当用户请求路径为登陆页面且带有validated=true的参数时,即不进行重定向TGT认证请求
客户端修改方案
根据服务器流程修改方案,我们可以知道,所有的远程请求都必须携带有loginUrl参数信息以使得服务器端知道在认证失败后转向客户端登陆页面。而在CAS客户端上,上一节的case 4和case 5,我们主要通过提交表单的方式传递loginUrl,而case 1, case 3则是依靠org.jasig.cas.client.authentication.AuthenticationFilter类进行的转向,但使用AuthenticationFilter转向时,是没有loginUrl信息的,因此我们首先需要重新实现一个自己的认证过滤器,以下是我们自己的认证过滤器的代码:
/** * @author xiaoxiao * 日期:2013-8-7 上午11:23:02 * The default character set is UTF-8.*/ package td.sso.filter;import java.io.IOException;import java.net.URL;import java.net.URLEncoder;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;import org.jasig.cas.client.authentication.GatewayResolver;import org.jasig.cas.client.util.AbstractCasFilter;import org.jasig.cas.client.util.CommonUtils;import org.jasig.cas.client.validation.Assertion;/** * 远程认证过滤器. * 由于AuthenticationFilter的doFilter方法被声明为final, * 只好重新实现一个认证过滤器,支持localLoginUrl设置. */public class RemoteAuthenticationFilter extends AbstractCasFilter { public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_"; /** * 本地登陆页面URL. */ private String localLoginUrl; /** * The URL to the CAS Server login. */ private String casServerLoginUrl; /** * Whether to send the renew request or not. */ private boolean renew = false; /** * Whether to send the gateway request or not. */ private boolean gateway = false; protected void initInternal(final FilterConfig filterConfig) throws ServletException { super.initInternal(filterConfig); setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null)); log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl); setLocalLoginUrl(getPropertyFromInitParams(filterConfig, "localLoginUrl", null)); log.trace("Loaded LocalLoginUrl parameter: " + this.localLoginUrl); setRenew(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false"))); log.trace("Loaded renew parameter: " + this.renew); setGateway(Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false"))); log.trace("Loaded gateway parameter: " + this.gateway); } public void init() { super.init(); CommonUtils.assertNotNull(this.localLoginUrl, "localLoginUrl cannot be null."); CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null."); } public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { System.out.println("调用RemoteAuthenticationFilter--->"); final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; final HttpSession session = request.getSession(false); final String ticket = request.getParameter(getArtifactParameterName()); final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;// final String serviceUrl = constructServiceUrl(request, response); final boolean wasGatewayed = session != null && session.getAttribute(CONST_CAS_GATEWAY) != null; // 如果访问路径为localLoginUrl且带有validated参数则跳过 URL url = new URL(localLoginUrl); final boolean isValidatedLocalLoginUrl = request.getRequestURI().endsWith(url.getPath()) && CommonUtils.isNotBlank(request.getParameter("validated")); System.out.println(!isValidatedLocalLoginUrl && CommonUtils.isBlank(ticket) && assertion == null && !wasGatewayed); if (!isValidatedLocalLoginUrl && CommonUtils.isBlank(ticket) && assertion == null && !wasGatewayed) { log.debug("no ticket and no assertion found"); if (this.gateway) { log.debug("setting gateway attribute in session"); request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes"); } final String serviceUrl = constructServiceUrl(request, response); System.out.println(serviceUrl); if (log.isDebugEnabled()) { log.debug("Constructed service url: " + serviceUrl); } String urlToRedirectTo = CommonUtils.constructRedirectUrl( this.casServerLoginUrl, getServiceParameterName(), serviceUrl, this.renew, this.gateway); System.out.println("RemoteAuthenticationFilter urlToRedirectTo\t"+urlToRedirectTo+",loginUrl\t"+localLoginUrl); // 加入localLoginUrl urlToRedirectTo += (urlToRedirectTo.contains("?") ? "&" : "?") + "loginUrl=" + URLEncoder.encode(localLoginUrl, "utf-8"); if (log.isDebugEnabled()) { log.debug("redirecting to \"" + urlToRedirectTo + "\""); } response.sendRedirect(urlToRedirectTo); return; } if (session != null) { log.debug("removing gateway attribute from session"); session.setAttribute(CONST_CAS_GATEWAY, null); } filterChain.doFilter(request, response); } public final void setRenew(final boolean renew) { this.renew = renew; } public final void setGateway(final boolean gateway) { this.gateway = gateway; } public final void setCasServerLoginUrl(final String casServerLoginUrl) { this.casServerLoginUrl = casServerLoginUrl; } public final void setLocalLoginUrl(String localLoginUrl) { this.localLoginUrl = localLoginUrl; } }以上黄色背景代码为修改部分,其余代码均拷贝自org.jasig.cas.client.authentication.AuthenticationFilter,可以看到我们为原有的认证过滤器增加了一个参数localLoginUrl。在
applicationContext-cas.xml,将authenticationFilter指向的类换成我们的RemoteAuthenticationFilter,并添加localLoginUrl 参数
applicationContext-cas.xml:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "><context:property-placeholder location="classpath:cas-client.properties" /><!-- 负责用户的认证 --><bean name="authenticationFilter"class="com.talkingdatawebsite.sso.filter.RemoteAuthenticationFilter"><property name="localLoginUrl" value="${local.loginUrl}"></property><property name="casServerLoginUrl" value="${cas.server.loginUrl}" /><property name="renew" value="${cas.server.renew}" /><property name="gateway" value="${cas.server.gateway}" /><property name="service" value="${cas.client.serverName}" /></bean><!-- 对认证ticket进行校验 --><bean name="ticketValidationFilter"class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter"><property name="service" value="${cas.client.serverName}" /><property name="ticketValidator"><bean class="org.jasig.cas.client.validation.Cas10TicketValidator"><constructor-arg index="0" value="${cas.server.url}" /></bean></property></bean></beans>
cas-client.properties:
cas.server.url=https://www.talkingdata.net:8446/cas.server.loginUrl=https://www.talkingdata.net:8446/remoteLoginlocal.loginUrl=http://localhost:8080/index.jspcas.client.serverName=http://localhost:8080/index.jspcas.server.renew=false cas.server.gateway=false
最后来看看我们的登录页面login.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"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>远程CAS客户端登陆页面</title><link rel="stylesheet" type="text/css"href="<%=request.getContextPath()%>/styles/main.css" /><script type="text/javascript">function getParam(name) {var queryString = window.location.search;var param = queryString.substring(1, queryString.length).split("&");for ( var i = 0; i < param.length; i++) {var keyValue = param[i].split("=");if (keyValue[0] == name){return keyValue[1];}}return null;}function init() {// 显示异常信息var error = getParam("errorMessage");if (error) {document.getElementById("errorMessage").innerHTML = decodeURIComponent(error);}// 注入servicevar service = getParam("service");if (service)document.getElementById("service").value = decodeURIComponent(service);// document.getElementById("service").value = service;elsedocument.getElementById("service").value = location.href;}</script></head><body ><h1>远程CAS客户端client1.1登陆页面</h1><%if (request.getRemoteUser() == null) {%><div id="errorMessage"></div><form id="myLoginForm" action="https://www.talkingdata.net:8446/remoteLogin"method="post"><input type="hidden" id="service" name="service" value=""><input type="hidden" name="loginUrl" value="http://localhost:8082/login.jsp"><input type="hidden" name="loginsubmit" value="true" /><table><tr><td>用户名:</td><td><input type="text" id="username" name="username"></td></tr><tr><td>密 码:</td><td><input type="password" id="password" name="password"></td></tr><tr><td colspan="2"><input type="submit" value="登陆" /></td></tr></table></form><script type="text/javascript">init()</script><%} else {%><div class="welcome">您好:<%=request.getRemoteUser()%></div><div id="logout"><a href="https://www.talkingdata.net:8446/logout?service=http://localhost:8082/">退出</a> <a href="http://localhost:8081">我要去client1</a></div><%}%></body></html>
以上黄色背景字中,我们首先将表单action指向服务器端remoteLogin,然后在里面设置了两个重要的hidden域以传递 loginUrl和submit参数,前者用于告诉服务器失败后转向何处,后者告诉服务器端webflow现在要进行提交而不是TGT认证请求。
这样我们的自定义客户端远程登陆页面就完成了,现在赶快测试一下吧~。
- 单点登录(八)cas支持客户端登录——客户端
- 单点登录(七)cas支持客户端登录——服务器端
- Cas单点登录客户端配置
- 单点登录学习(3)CAS客户端配置
- 单点登录(四)cas客户端配置
- cas 单点登录服务端客户端配置
- CAS单点登录源码解析之【客户端】
- CAS单点登录python客户端分析
- CAS单点登录客户端--C++版
- CAS单点登录服务端与客户端配置
- CAS 制作单点登录登录后跳转客户端页面出错
- CAS单点登录三-客户端获取登录信息
- CAS单点登录-客户端集成(cas-client)(九)
- 单点登录CAS系列8-客户端配置单点登出
- CAS的php客户端实践—单点登录整合php程序
- CAS 客户端登录验证
- 基于CAS的单点登录SSO[4]: 加入两个CAS客户端测试单点登录
- 单点登录之,客户端
- 彻底理解各种排序算法
- session存储优化之基于mysql的存取
- Linux 下常用的 19 条命令
- fatal error LNK1104: cannot open file“vld.lib”
- CComCoClass
- 单点登录(八)cas支持客户端登录——客户端
- C++拷贝构造函数(深拷贝,浅拷贝)
- KDTable单元格如何格式化显示。
- Oracle 解释执行计划
- _CreatorClass
- 怎样使一个Android应用不被杀死?
- Magento 社区版 1.8.0
- JAVA操作properties文件
- ramdisk/ramfs/tmpfs/initrd/initramfs/rootfs/cpio