- /* Copyright (c) 2000-2004 Yale University. All rights reserved.
- * See full notice at end.
- */
- package edu.yale.its.tp.cas.client.filter;
- import java.io.*;
- import java.net.*;
- import java.util.*;
- import javax.servlet.*;
- import javax.servlet.http.*;
- import edu.yale.its.tp.cas.client.*;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- /*
- *
- * @author Shawn Bayern
- * @author Drew Mazurek
- * @author andrew.petro@yale.edu
- */
- public class CASFilter implements Filter {
- private static Log log = LogFactory.getLog(CASFilter.class);
- // Filter initialization parameters
- //必须参数
- /**
- * loginUrl:指定 CAS 提供登录页面的 URL
- */
- public final static String LOGIN_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.loginUrl";
- /**
- * validateUrl:指定 CAS 提供 service ticket 或 proxy ticket 验证服务的 URL
- */
- public final static String VALIDATE_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.validateUrl";
- /**
- * serviceUrl:本web项目的URL,该参数指定过后将覆盖 serverName 参数,成为登录成功过后重定向的目的地址
- */
- public final static String SERVICE_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.serviceUrl";
- /**
- * serverName:全主机端口号,指定客户端的域名和端口,是指客户端应用所在机器而不是 CAS Server 所在机器,该参数或 serviceUrl 至少有一个必须指定
- */
- public final static String SERVERNAME_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.serverName";
- //可选参数
- /**
- * renew:如果指定为 true,那么受保护的资源每次被访问时均要求用户重新进行验证,而不管之前是否已经通过
- */
- public final static String RENEW_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.renew";
- /**
- * authorizedProxy:用于允许当前应用从代理处获取 proxy tickets,该参数接受以空格分隔开的多个 proxy URLs,但实际使用只需要一个成功即可。当指定该参数过后,需要修改 validateUrl 到 proxyValidate,
- */
- public final static String AUTHORIZED_PROXY_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.authorizedProxy";
- /**
- * proxyCallbackUrl:用于当前应用需要作为其他服务的代理(proxy)时获取 Proxy Granting Ticket 的地址
- */
- public final static String PROXY_CALLBACK_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.proxyCallbackUrl";
- /**
- * wrapRequest:如果指定为 true,那么 CASFilter 将重新包装 HttpRequest,并且使 getRemoteUser() 方法返回当前登录用户的用户名
- */
- public final static String WRAP_REQUESTS_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.wrapRequest";
- /**
- * gateway:这个参数很奇葩,一开始没读懂是干嘛的。。官方解释是一旦发生过CAS重定向,过滤器将不会自动重新设置登录的用户。然后你可以提供一个明确的CAS登录链接(HTTPS:/ / CAS服务器/ CAS /登录?服务= HTTP:/ /应用程序)或建立映射到不同的路径的过滤器的两个实例。一个实例将gateway实现。当你需要登录的用户,直接转到其他过滤器。
- * 是的你没有想错,这一句话着实让人不知道是要说明什么,于是万能的百度上有且仅有一个前辈说出来了这个参数其实是和renew互斥的,renew就是说无论如何都得重新验证此用户,不管你session中有没有上下文信息。而gateway则是只要检测到session中有sso上下文,就不再重新认证
- */
- public final static String GATEWAY_INIT_PARAM = "edu.yale.its.tp.cas.client.filter.gateway";
- public final static String CAS_FILTER_USER = "edu.yale.its.tp.cas.client.filter.user";
- public final static String CAS_FILTER_RECEIPT = "edu.yale.its.tp.cas.client.filter.receipt";
- private static final String CAS_FILTER_GATEWAYED = "edu.yale.its.tp.cas.client.filter.didGateway";
- // *********************************************************************
- // Configuration state
- private String casLogin;
- private String casValidate;
- private String casServiceUrl;
- private String casServerName;
- private String casProxyCallbackUrl;
- private boolean casRenew;
- private boolean wrapRequest;
- private boolean casGateway = false;
- /**
- * 对proxyticketreceptor URL授权代理在过滤器的路径的服务列表
- */
- private List authorizedProxies = new ArrayList();
- // *********************************************************************
- // Initialization
- public void init(FilterConfig config) throws ServletException {
- //拿到参数
- casLogin = config.getInitParameter(LOGIN_INIT_PARAM);
- casValidate = config.getInitParameter(VALIDATE_INIT_PARAM);
- casServiceUrl = config.getInitParameter(SERVICE_INIT_PARAM);
- String casAuthorizedProxy = config.getInitParameter(AUTHORIZED_PROXY_INIT_PARAM);
- casRenew = Boolean.valueOf(config.getInitParameter(RENEW_INIT_PARAM)).booleanValue();
- casServerName = config.getInitParameter(SERVERNAME_INIT_PARAM);
- casProxyCallbackUrl = config.getInitParameter(PROXY_CALLBACK_INIT_PARAM);
- wrapRequest = Boolean.valueOf(config.getInitParameter(WRAP_REQUESTS_INIT_PARAM)).booleanValue();
- casGateway = Boolean.valueOf(config.getInitParameter(GATEWAY_INIT_PARAM)).booleanValue();
- if (casGateway && Boolean.valueOf(casRenew).booleanValue()) {
- //这俩参数不能一起设置为true
- throw new ServletException("gateway and renew cannot both be true in filter configuration");
- }
- if (casServerName != null && casServiceUrl != null) {
- //这俩参数也不能一起设置
- throw new ServletException("serverName and serviceUrl cannot both be set: choose one.");
- }
- if (casServerName == null && casServiceUrl == null) {
- //这俩参数也不能一起为null
- throw new ServletException("one of serverName or serviceUrl must be set.");
- }
- if (casServiceUrl != null) {
- //检测uri前缀
- if (!(casServiceUrl.startsWith("https://") || (casServiceUrl.startsWith("http://")))) {
- throw new ServletException("service URL must start with http:// or https://; its current value is [" + casServiceUrl + "]");
- }
- }
- if (casValidate == null) {
- //cas验证用户的网址不能为空
- throw new ServletException("validateUrl parameter must be set.");
- }
- if (!casValidate.startsWith("https://")) {
- //如果cas认证网址不是以https开头,就报错。。如果你是用http请求,可以屏蔽掉这个判断语句
- throw new ServletException("validateUrl must start with https://, its current value is [" + casValidate + "]");
- }
- //代理是否为空
- if (casAuthorizedProxy != null) {
- // parse and remember authorized proxies
- StringTokenizer casProxies = new StringTokenizer(casAuthorizedProxy);
- while (casProxies.hasMoreTokens()) {
- //授权的标记
- String anAuthorizedProxy = casProxies.nextToken();
- //https前缀检测
- if (!anAuthorizedProxy.startsWith("https://")) {
- throw new ServletException("CASFilter initialization parameter for authorized proxies " + "must be a whitespace delimited list of authorized proxies. " + "Authorized proxies must be secure (https) addresses. This one wasn't: [" + anAuthorizedProxy + "]");
- }
- //将所有授权的代理添加到list中(唉,着实不知道是干什么的,也许几年后回来读读应该能知道答案,2013年4月22日14:56:37)
- this.authorizedProxies.add(anAuthorizedProxy);
- }
- }
- if (log.isDebugEnabled()) {
- log.debug(("CASFilter initialized as: [" + toString() + "]"));
- }
- }
- // *********************************************************************
- // Filter processing
- // 过滤器处理
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc) throws ServletException, IOException {
- //核心思想:首先检查session中有无凭证receipt,如果有,那么就要去下个过滤器链进行处理,如果无,则获取传参ticket,如果有ticket,就经过getAuthenticatedUser()方法去拿到receipt凭证,如果无(这中间会有一些对renew或者gateway的处理),就立即进入cas服务端进行登录
- if (log.isTraceEnabled()) {
- log.trace("entering doFilter()");
- }
- // make sure we've got an HTTP request
- if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
- log.error("doFilter() called on a request or response that was not an HttpServletRequest or response.");
- throw new ServletException("CASFilter protects only HTTP resources");
- }
- // Is this a request for the proxy callback listener? If so, pass
- // it through
- if (casProxyCallbackUrl != null && casProxyCallbackUrl.endsWith(((HttpServletRequest) request).getRequestURI()) && request.getParameter("pgtId") != null && request.getParameter("pgtIou") != null) {
- log.trace("passing through what we hope is CAS's request for proxy ticket receptor.");
- fc.doFilter(request, response);
- return;
- }
- // Wrap the request if desired
- if (wrapRequest) {
- log.trace("Wrapping request with CASFilterRequestWrapper.");
- request = new CASFilterRequestWrapper((HttpServletRequest) request);
- }
- // 1.从当前web应用中拿到session
- HttpSession session = ((HttpServletRequest) request).getSession();
- // if our attribute's already present and valid, pass through the filter chain
- // 1.1.如果存在一个票据(令牌,凭证),就要跳到下一个过滤器链(去验证此票据的真实性,因为此票据的真实性是未知的)
- CASReceipt receipt = (CASReceipt) session.getAttribute(CAS_FILTER_RECEIPT);
- if (receipt != null && isReceiptAcceptable(receipt)) {
- log.trace("CAS_FILTER_RECEIPT attribute was present and acceptable - passing request through filter..");
- fc.doFilter(request, response);
- return;
- }
- // otherwise, we need to authenticate via CAS
- // 1.2.如果receipt(令牌)不存在就先拿到ticket,我们要去cas验证用户进行登录
- String ticket = request.getParameter("ticket");
- // no ticket? abort request processing and redirect
- //如果ticket为空
- if (ticket == null || ticket.equals("")) {
- log.trace("CAS ticket was not present on request.");
- // 4.1判断是否经过网关参数(didGateway这个参数否已经经过网关的一个标记参数,表示不再进行认证)
- // did we go through the gateway already?
- boolean didGateway = Boolean.valueOf((String) session.getAttribute(CAS_FILTER_GATEWAYED)).booleanValue();
- // 4.1.1没有casLogin的配置信息下的异常处理
- if (casLogin == null) {
- // TODO: casLogin should probably be ensured to not be null at filter initialization. -awp9
- log.fatal("casLogin was not set, so filter cannot redirect request for authentication.");
- throw new ServletException("When CASFilter protects pages that do not receive a 'ticket' " + "parameter, it needs a edu.yale.its.tp.cas.client.filter.loginUrl " + "filter parameter");
- }
- // 4.2如果网关标记为false,设置CAS_FILTER_GATEWAYED属性为true,并跳转到cas服务端进行验证
- if (!didGateway) {
- log.trace("Did not previously gateway. Setting session attribute to true.");
- session.setAttribute(CAS_FILTER_GATEWAYED, "true");
- redirectToCAS((HttpServletRequest) request, (HttpServletResponse) response);
- // abort chain
- return;
- } else {
- log.trace("Previously gatewayed.");
- // 4.3 如果有网关参数(之前已经通过了网关),就不再进行验证,从而进入下一个过滤器处理即可。
- // if we should be logged in, make sure validation succeeded
- if (casGateway || session.getAttribute(CAS_FILTER_USER) != null) {
- //已经通过了验证和授权。。
- log.trace("casGateway was true and CAS_FILTER_USER set: passing request along filter chain.");
- // continue processing the request 交给下一个过滤器
- fc.doFilter(request, response);
- return;
- } else {
- // 其他情况下,跳往cas服务端
- // unknown state... redirect to CAS
- //将经过网关的参数didGateway设置为true
- session.setAttribute(CAS_FILTER_GATEWAYED, "true");
- redirectToCAS((HttpServletRequest) request, (HttpServletResponse) response);
- // abort chain
- return;
- }
- }
- }
- try {
- // ticket存在,就经过getAuthenticatedUser()方法去拿到receipt,初步判断此方法是为根据request中的ticket参数组装了一个数据发送给了cas服务端进行判断此ticket是否是正确的合法的(它可能是使用代理类进行的实现)
- receipt = getAuthenticatedUser((HttpServletRequest) request);
- } catch (CASAuthenticationException e) {
- log.error(e);
- throw new ServletException(e);
- }
- if (!isReceiptAcceptable(receipt)) {
- //检测授权不被认可,就是非法的。
- throw new ServletException("Authentication was technically successful but rejected as a matter of policy. [" + receipt + "]");
- }
- //既然拿到了凭证,就去拿到session中是否有相关信息,并写入CASFilter.CAS_FILTER_RECEIPT
- // Store the authenticated user in the session
- if (session != null) { // probably unnecessary
- //将username(用户名)信息放入session中
- session.setAttribute(CAS_FILTER_USER, receipt.getUserName());
- //放入票据
- session.setAttribute(CASFilter.CAS_FILTER_RECEIPT, receipt);
- // don't store extra unnecessary session state
- //不要储存额外的不必要的会话状态
- session.removeAttribute(CAS_FILTER_GATEWAYED);
- }
- if (log.isTraceEnabled()) {
- log.trace("validated ticket to get authenticated receipt [" + receipt + "], now passing request along filter chain.");
- }
- // continue processing the request
- //进入下一个过滤器进行处理
- fc.doFilter(request, response);
- log.trace("returning from doFilter()");
- }
- /**
- * Is this receipt acceptable as evidence of authentication by credentials that would have been acceptable to this path? Current implementation checks whether from renew and whether proxy was authorized.
- *
- * @param receipt 票据
- * @return true if acceptable, false otherwise
- */
- private boolean isReceiptAcceptable(CASReceipt receipt) {
- if (receipt == null)
- throw new IllegalArgumentException("Cannot evaluate a null receipt.");
- if (this.casRenew && !receipt.isPrimaryAuthentication()) {
- return false;
- }
- if (receipt.isProxied()) {
- if (!this.authorizedProxies.contains(receipt.getProxyingService())) {
- return false;
- }
- }
- return true;
- }
- // *********************************************************************
- // Utility methods
- /**
- * Converts a ticket parameter to a CASReceipt, taking into account an optionally configured trusted proxy in the tier immediately in front of us.
- *
- * @throws ServletException -
- * when unable to get service for request
- * @throws CASAuthenticationException -
- * on authentication failure
- */
- private CASReceipt getAuthenticatedUser(HttpServletRequest request) throws ServletException, CASAuthenticationException {
- log.trace("entering getAuthenticatedUser()");
- ProxyTicketValidator pv = null;
- pv = new ProxyTicketValidator();
- pv.setCasValidateUrl(casValidate);
- pv.setServiceTicket(request.getParameter("ticket"));
- pv.setService(getService(request));
- pv.setRenew(Boolean.valueOf(casRenew).booleanValue());
- if (casProxyCallbackUrl != null) {
- pv.setProxyCallbackUrl(casProxyCallbackUrl);
- }
- if (log.isDebugEnabled()) {
- log.debug("about to validate ProxyTicketValidator: [" + pv + "]");
- }
- return CASReceipt.getReceipt(pv);
- }
- /**
- * Returns either the configured service or figures it out for the current request. The returned service is URL-encoded.
- */
- private String getService(HttpServletRequest request) throws ServletException {
- log.trace("entering getService()");
- String serviceString;
- // ensure we have a server name or service name
- if (casServerName == null && casServiceUrl == null)
- throw new ServletException("need one of the following configuration " + "parameters: edu.yale.its.tp.cas.client.filter.serviceUrl or " + "edu.yale.its.tp.cas.client.filter.serverName");
- // use the given string if it's provided
- if (casServiceUrl != null)
- serviceString = URLEncoder.encode(casServiceUrl);
- else
- // otherwise, return our best guess at the service
- serviceString = Util.getService(request, casServerName);
- if (log.isTraceEnabled()) {
- log.trace("returning from getService() with service [" + serviceString + "]");
- }
- return serviceString;
- }
- /**
- * Redirects the user to CAS, determining the service from the request.
- */
- private void redirectToCAS(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
- if (log.isTraceEnabled()) {
- log.trace("entering redirectToCAS()");
- }
- String casLoginString = casLogin + "?service=" + getService((HttpServletRequest) request) + ((casRenew) ? "&renew=true" : "") + (casGateway ? "&gateway=true" : "");
- if (log.isDebugEnabled()) {
- log.debug("Redirecting browser to [" + casLoginString + ")");
- }
- ((HttpServletResponse) response).sendRedirect(casLoginString);
- if (log.isTraceEnabled()) {
- log.trace("returning from redirectToCAS()");
- }
- }
- public String toString() {
- StringBuffer sb = new StringBuffer();
- sb.append("[CASFilter:");
- sb.append(" casGateway=");
- sb.append(this.casGateway);
- sb.append(" wrapRequest=");
- sb.append(this.wrapRequest);
- sb.append(" casAuthorizedProxies=[");
- sb.append(this.authorizedProxies);
- sb.append("]");
- if (this.casLogin != null) {
- sb.append(" casLogin=[");
- sb.append(this.casLogin);
- sb.append("]");
- } else {
- sb.append(" casLogin=NULL!!!!!");
- }
- if (this.casProxyCallbackUrl != null) {
- sb.append(" casProxyCallbackUrl=[");
- sb.append(casProxyCallbackUrl);
- sb.append("]");
- }
- if (this.casRenew) {
- sb.append(" casRenew=true");
- }
- if (this.casServerName != null) {
- sb.append(" casServerName=[");
- sb.append(casServerName);
- sb.append("]");
- }
- if (this.casServiceUrl != null) {
- sb.append(" casServiceUrl=[");
- sb.append(casServiceUrl);
- sb.append("]");
- }
- if (this.casValidate != null) {
- sb.append(" casValidate=[");
- sb.append(casValidate);
- sb.append("]");
- } else {
- sb.append(" casValidate=NULL!!!");
- }
- return sb.toString();
- }
- /* (non-Javadoc)
- * @see javax.servlet.Filter#destroy()
- */
- public void destroy() {
- // TODO Auto-generated method stub
- }
- }
注释都已经写在代码块里了。顺便放上含有英文注释和中文注释对比的帖子一份:http://hi.baidu.com/ae6623/item/3b62cc03c0841415acdc7068
相关推荐
[置顶] SSO单点登录系列2:cas客户端和cas服务端交互原理动画图解,cas协议终极分析 http://blog.csdn.net/ae6623/article/details/8848107 目 录 1 引言 4 1.1 摘要 4 1.2 范围 4 1.3 读者对象 4 1.4 关键词 4 2 ...
CAS(Central Authentication Service)是基于Java的开源身份验证框架,主要设计用于Web应用程序,提供单一登录(Single Sign-On,SSO)服务。"cas-client-java-2.1.1"是一个特定版本的CAS客户端库,它允许Java应用...
单点登录 sso cas带超详细文档,包含(cas-server-webapp-4.0.0.war、c3p0-0.9.1.2.jar、cas-client-core-3.3.3.jar、cas-server-support-jdbc-4.0.0.jar、cas-server-webapp-support-4.0.0.jar、commons-logging-...
在这个场景中,"cas客户端jar包"是指用于与CAS服务器通信的Java库,方便开发者集成到他们的Java应用程序中,实现单点登录功能。 首先,我们需要了解CAS客户端的主要组件和工作原理。当用户尝试访问一个受CAS保护的...
CAS(Central Authentication Service)是Java开发的一个开源的单点登录(Single Sign-On,简称SSO)框架,主要用于解决网络应用中的身份验证问题。本压缩包提供了CAS服务端自定义认证的实现,以及CAS客户端的配置...
Spring Boot 整合 CAS Client 是一种流行的解决方案,用于实现单点登录(Single Sign-On,简称 SSO)。在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。CAS Client 负责处理对客户端受保护...
在"SSO之CAS单点登录客户端服务端jar包"中,包含的是实现CAS单点登录功能所需的客户端和服务端组件。这些jar包包含了以下关键知识点: 1. **CAS服务器**:这是整个SSO架构的核心,负责处理用户的登录请求,验证凭证...
CAS(Central Authentication Service)是一种基于Web的单点登录(Single Sign-On, SSO)协议,主要用于简化用户在多个应用系统间的登录流程。`cas-client-core-3.3.3` 是一个CAS客户端的核心库,它使得应用程序能够...
CAS(Central Authentication Service)是耶鲁大学开发的一种开放源代码的单点登录(Single Sign-On,简称SSO)协议,用于网络应用中的身份验证。它允许用户在一个认证点登录后,无需再次输入凭证就能访问其他受保护...
CAS客户端JAR包版本3.3.3下载之后拷贝到lib文件夹下 配置web.xml <!--退出--> <context-param> <param-name>casServerLogoutUrl</param-name> <param-value>...
SSO(Single Sign-On)是单点登录的缩写,是一种网络用户身份验证的机制,允许用户在一次登录后访问多个应用系统而无需再次验证。CAS(Central Authentication Service)是SSO的一种实现,由耶鲁大学开发并开源,它...
CAS(Central Authentication Service)是 Yale 大学开发的一个开源项目,主要用于实现单点登录(Single Sign-On, SSO)。它是一个基于Web的认证协议,旨在简化用户对多个应用系统的访问管理,通过一次登录即可访问...
在这个"cas-client3.5单点登录官方客户端程序demo"中,我们可以深入理解如何在Java环境中集成CAS客户端来实现单点登录功能。 首先,CAS客户端3.5是为Java应用程序设计的,它允许这些应用与CAS服务器进行通信,验证...
CAS(Central Authentication Service)是一种广泛使用的开放源代码的单点登录(Single Sign-On,简称SSO)系统。它允许用户通过一个认证入口访问多个应用系统,而无需在每个系统上分别进行登录。在这个场景中,"cas...
CAS(Central Authentication Service)是一个开放源码的单点登录(Single Sign-On,SSO)框架,主要用于实现Web应用的安全认证。`cas-client-3.1.6-release.zip` 是一个包含CAS客户端库的版本3.1.6的压缩包,用于与...
Java单点登录(Single Sign-On, SSO)系统是一种网络身份验证机制,它允许用户在一次登录后,访问多个相互信任的应用系统而无需再次输入凭证。JA-SIG CAS(Central Authentication Service)是Java社区开发的一个...
sword-cas-client-java-1.0-beta-1.jar
CAS(Central Authentication Service)是一种广泛使用的开放源代码的单点登录(Single Sign-On,简称SSO)框架。SSO允许用户在访问多个应用系统时只需要进行一次身份验证,之后访问其他系统无需再次输入凭证,提高...
CAS Client 3.5.1客户端,用于单点登录客户端拦截,编译环境JDK1.8,可用于JDK1.8及以上Java版本。
CAS(Central Authentication Service)是基于Java的开源身份验证框架,用于在网络上实现单点登录(Single Sign-On, SSO)。在本场景中,"cas_client.zip" 是一个包含CAS客户端组件的压缩包,它使得应用程序能够与...