准备前参考: 

 cas server下载地址

 cas client 下载地址

安全cookie setSecure详解

Spring通过构造方法注入的四种方式

 cas 学习博文

自定义登录页和登录认证

  cas server端的login-webflow详细流程

  CAS服务端自定义数据库认证用户

准备工作

  1. cas server下载之后解压,在项目根目录下执行 mvn clean 和 mvn install命令(如果使用给定的命令报错)。

  2. 根目录下会生成target目录,target中会有一个cas.war的文件,将这个文件通过 mvn install 发布到maven的本地仓库中。

  

mvn install:install-file -DgroupId=com.hjz -DartifactId=cas -Dversion=1.0.0 -Dfile=cas.war -Dpackaging=war -DgeneratePom=true

  3.项目中的依赖

<dependency>
<groupId>com.hjz</groupId>
<artifactId>cas</artifactId>
<version>1.0.0</version>
<type>war</type>
</dependency>

客户端cas ticket验证走查

  1.如果请求中携带了参数ticket则将会由TicketValidationFilter来对携带的ticket进行校验。TicketValidationFilter只是对验证ticket的这一类Filter的统称,其并不对应Cas Client中的一个具体类型。Cas Client中有多种验证ticket的Filter,都继承自AbstractTicketValidationFilter,它们的验证逻辑都是一致的,都有AbstractTicketValidationFilter实现,所不同的是使用的TicketValidator不一样。笔者这里将以Cas20TicketValidationFilter为例。

<context-param>
<param-name>serverName</param-name>
<param-value>http://127.0.0.1:8089</param-value>
</context-param> <filter>
<filter-name>casTicketValidationFilter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas10TicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://127.0.0.1:8080/cas</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>casTicketValidationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  一个是用来指定Cas Server登录地址的casServerLoginUrl,另一个是用来指定认证成功后需要跳转地址的serverNameservice。service和serverName只需要指定一个就可以了。当两者都指定了,参数service将具有更高的优先级,即将以service指定的参数值为准。service和serverName的区别在于service指定的是一个确定的URL,认证成功后就会确切的跳转到service指定的URL;而serverName则是用来指定主机名,其格式为{protocol}:{hostName}:{port},如:https://localhost:8443,当指定的是serverName时,AuthenticationFilter将会把它附加上当前请求的URI,以及对应的查询参数来构造一个确定的URL,如指定serverName为“http://localhost”,而当前请求的URI为“/app”,查询参数为“a=b&b=c”,则对应认证成功后的跳转地址将为“http://localhost/app?a=b&b=c”。

必须指定的参数:

   casServerUrlPrefix:用来指定Cas Server对应URL地址的前缀,如上面示例的“https://elim:8443/cas”。

   serverName或service:语义跟前面介绍的一致。

可选参数:

   redirectAfterValidation :表示是否验证通过后重新跳转到该URL,但是不带参数ticket,默认为true。

   useSession :在验证ticket成功后会生成一个Assertion对象,如果useSession为true,则会将该对象存放到Session中。如果为false,则要求每次请求都需要携带ticket进行验证,显然useSession为false跟redirectAfterValidation为true是冲突的。默认为true。

  exceptionOnValidationFailure :表示ticket验证失败后是否需要抛出异常,默认为true。

   renew:当值为true时将发送“renew=true”到Cas Server,默认为false。

  

  2.通过模仿 Cas10TicketValidationFilter 的逻辑,自定义 ticket validation filter。

package com.xxx.web.cas;

import com.xxx.sdk.RedisUtils;
import com.xxx.web.cas.logout.entity.TenantUser;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.HostnameVerifier;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang.StringUtils;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.proxy.AbstractEncryptedProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
import org.jasig.cas.client.proxy.CleanUpTimerTask;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.slf4j.Logger;
import uap.web.esapi.EncryptException;
import uap.web.utils.PropertyUtil;
import uap.web.utils.TokenGenerator; public class TenantProxyReceivingTicketValidationFilter extends AbstractCasFilter {
private static final String[] RESERVED_INIT_PARAMS = { "proxyGrantingTicketStorageClass", "proxyReceptorUrl",
"acceptAnyProxy", "allowedProxyChains", "casServerUrlPrefix", "proxyCallbackUrl", "renew",
"exceptionOnValidationFailure", "redirectAfterValidation", "useSession", "serverName", "service",
"artifactParameterName", "serviceParameterName", "encodeServiceUrl", "millisBetweenCleanUps",
"hostnameVerifier", "encoding", "config", "ticketValidatorClass" };
private static final int DEFAULT_MILLIS_BETWEEN_CLEANUPS = 60000;
private String proxyReceptorUrl;
private Timer timer;
private TimerTask timerTask;
private int millisBetweenCleanUps;
private static final String TENANT_ASSERTION = "tenant_assertion";
private TicketValidator ticketValidator;
private boolean redirectAfterValidation;
private boolean exceptionOnValidationFailure;
private boolean useSession;
private ProxyGrantingTicketStorage proxyGrantingTicketStorage; public TenantProxyReceivingTicketValidationFilter() {
this.redirectAfterValidation = true; this.exceptionOnValidationFailure = false; this.useSession = true; this.proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
} protected void initInternal(FilterConfig filterConfig) throws ServletException {
setProxyReceptorUrl(getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", null)); String proxyGrantingTicketStorageClass = getPropertyFromInitParams(filterConfig,
"proxyGrantingTicketStorageClass", null); if (proxyGrantingTicketStorageClass != null) {
this.proxyGrantingTicketStorage = ((ProxyGrantingTicketStorage) ReflectUtils
.newInstance(proxyGrantingTicketStorageClass, new Object[0])); if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl) this.proxyGrantingTicketStorage;
String cipherAlgorithm = getPropertyFromInitParams(filterConfig, "cipherAlgorithm", "DESede"); String secretKey = getPropertyFromInitParams(filterConfig, "secretKey", null); p.setCipherAlgorithm(cipherAlgorithm);
try {
if (secretKey != null)
p.setSecretKey(secretKey);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} this.logger.trace("Setting proxyReceptorUrl parameter: {}", this.proxyReceptorUrl);
this.millisBetweenCleanUps = Integer
.parseInt(getPropertyFromInitParams(filterConfig, "millisBetweenCleanUps", Integer.toString(60000))); setExceptionOnValidationFailure(
parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "false"))); this.logger.trace("Setting exceptionOnValidationFailure parameter: {}",
Boolean.valueOf(this.exceptionOnValidationFailure));
setRedirectAfterValidation(
parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation", "true"))); this.logger.trace("Setting redirectAfterValidation parameter: {}",
Boolean.valueOf(this.redirectAfterValidation));
setUseSession(parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
this.logger.trace("Setting useSession parameter: {}", Boolean.valueOf(this.useSession)); if ((!(this.useSession)) && (this.redirectAfterValidation)) {
this.logger.warn(
"redirectAfterValidation parameter may not be true when useSession parameter is false. Resetting it to false in order to prevent infinite redirects."); setRedirectAfterValidation(false);
} setTicketValidator(getTicketValidator(filterConfig)); super.initInternal(filterConfig); String servername = PropertyUtil.getPropertyByKey("servername");
if (StringUtils.isNotBlank(servername))
setServerName(servername);
else
setServerName(getPropertyFromInitParams(filterConfig, "serverName", null));
} public void init() {
super.init();
CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
if (this.timer == null) {
this.timer = new Timer(true);
} if (this.timerTask == null) {
this.timerTask = new CleanUpTimerTask(this.proxyGrantingTicketStorage);
}
this.timer.schedule(this.timerTask, this.millisBetweenCleanUps, this.millisBetweenCleanUps);
} private <T> T createNewTicketValidator(String ticketValidatorClass, String casServerUrlPrefix, Class<T> clazz) {
if (CommonUtils.isBlank(ticketValidatorClass)) {
return ReflectUtils.newInstance(clazz, new Object[] { casServerUrlPrefix });
} return ReflectUtils.newInstance(ticketValidatorClass, new Object[] { casServerUrlPrefix });
} protected final TicketValidator getTicketValidator(FilterConfig filterConfig) {
String allowAnyProxy = getPropertyFromInitParams(filterConfig, "acceptAnyProxy", null);
String allowedProxyChains = getPropertyFromInitParams(filterConfig, "allowedProxyChains", null); String casServerUrlPrefix = getCasUrl(filterConfig); String ticketValidatorClass = getPropertyFromInitParams(filterConfig, "ticketValidatorClass", null);
Cas20ServiceTicketValidator validator;
Cas20ServiceTicketValidator validator;
if ((CommonUtils.isNotBlank(allowAnyProxy)) || (CommonUtils.isNotBlank(allowedProxyChains))) {
Cas20ProxyTicketValidator v = (Cas20ProxyTicketValidator) createNewTicketValidator(ticketValidatorClass,
casServerUrlPrefix, Cas20ProxyTicketValidator.class); v.setAcceptAnyProxy(parseBoolean(allowAnyProxy));
v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains));
validator = v;
} else {
validator = (Cas20ServiceTicketValidator) createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
Cas20ServiceTicketValidator.class);
} validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null));
validator.setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage); HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig),
getSSLConfig(filterConfig)); validator.setURLConnectionFactory(factory); validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix,
getPropertyFromInitParams(filterConfig, "encoding", null), factory)); validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null)); Map additionalParameters = new HashMap();
List params = Arrays.asList(RESERVED_INIT_PARAMS); for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {
String s = (String) e.nextElement(); if (!(params.contains(s))) {
additionalParameters.put(s, filterConfig.getInitParameter(s));
}
} validator.setCustomParameters(additionalParameters);
return validator;
} public void destroy() {
super.destroy();
this.timer.cancel();
} protected final boolean preFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String requestUri = request.getRequestURI(); if ((CommonUtils.isEmpty(this.proxyReceptorUrl)) || (!(requestUri.endsWith(this.proxyReceptorUrl)))) {
return true;
}
try {
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
} catch (RuntimeException e) {
this.logger.error(e.getMessage(), e);
throw e;
} return false;
} public final void setProxyReceptorUrl(String proxyReceptorUrl) {
this.proxyReceptorUrl = proxyReceptorUrl;
} public void setProxyGrantingTicketStorage(ProxyGrantingTicketStorage storage) {
this.proxyGrantingTicketStorage = storage;
} public void setTimer(Timer timer) {
this.timer = timer;
} public void setTimerTask(TimerTask timerTask) {
this.timerTask = timerTask;
} public void setMillisBetweenCleanUps(int millisBetweenCleanUps) {
this.millisBetweenCleanUps = millisBetweenCleanUps;
} protected Properties getSSLConfig(FilterConfig filterConfig) {
Properties properties = new Properties();
String fileName = getPropertyFromInitParams(filterConfig, "sslConfigFile", null); if (fileName != null) {
FileInputStream fis = null;
try {
fis = new FileInputStream(fileName);
properties.load(fis);
this.logger.trace("Loaded {} entries from {}", Integer.valueOf(properties.size()), fileName);
} catch (IOException ioe) {
this.logger.error(ioe.getMessage(), ioe);
} finally {
CommonUtils.closeQuietly(fis);
}
}
return properties;
} protected HostnameVerifier getHostnameVerifier(FilterConfig filterConfig) {
String className = getPropertyFromInitParams(filterConfig, "hostnameVerifier", null);
this.logger.trace("Using hostnameVerifier parameter: {}", className);
String config = getPropertyFromInitParams(filterConfig, "hostnameVerifierConfig", null);
this.logger.trace("Using hostnameVerifierConfig parameter: {}", config);
if (className != null) {
if (config != null) {
return ((HostnameVerifier) ReflectUtils.newInstance(className, new Object[] { config }));
}
return ((HostnameVerifier) ReflectUtils.newInstance(className, new Object[0]));
} return null;
} protected void onSuccessfulValidation(HttpServletRequest request, HttpServletResponse response,
Assertion assertion) {
} protected void onFailedValidation(HttpServletRequest request, HttpServletResponse response) {
} public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
if (!(preFilter(servletRequest, servletResponse, filterChain))) {
return;
} HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String ticket = retrieveTicketFromRequest(request); if (CommonUtils.isNotBlank(ticket)) {
this.logger.debug("Attempting to validate ticket: {}", ticket);
try {
Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response)); this.logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName()); request.setAttribute("_const_cas_assertion_", assertion); if (this.useSession) {
request.getSession().setAttribute("_const_cas_assertion_", assertion);
} createToken(ticket, assertion, response);
saveAssertion(ticket, assertion);
onSuccessfulValidation(request, response, assertion); if (this.redirectAfterValidation) {
this.logger.debug("Redirecting after successful ticket validation.");
response.sendRedirect(constructServiceUrl(request, response));
return;
}
} catch (TicketValidationException e) {
this.logger.debug(e.getMessage(), e); onFailedValidation(request, response); if (this.exceptionOnValidationFailure) {
throw new ServletException(e);
} response.sendError(403, e.getMessage()); return;
}
} filterChain.doFilter(request, response);
} public final void setTicketValidator(TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
} public final void setRedirectAfterValidation(boolean redirectAfterValidation) {
this.redirectAfterValidation = redirectAfterValidation;
} public final void setExceptionOnValidationFailure(boolean exceptionOnValidationFailure) {
this.exceptionOnValidationFailure = exceptionOnValidationFailure;
} public final void setUseSession(boolean useSession) {
this.useSession = useSession;
} private void createToken(String ticket, Assertion assertion, HttpServletResponse response) {
AttributePrincipal principal = assertion.getPrincipal();
String userID = "";
if (principal != null) {
Map params = principal.getAttributes();
Object userId = params.get("userId");
if (userId != null) {
userID = userId.toString();
}
} if (StringUtils.isBlank(userID))
return;
String saveUserid = ticket + "__" + userID;
TenantUser existUser = RedisUtils.getUserCache("user.info.login.tenant:" + saveUserid);
HashMap cookiesMap;
Iterator iterator;
if (existUser == null) {
TenantUser user = new TenantUser();
user.setUserId(saveUserid);
long ts = System.currentTimeMillis();
user.setLoginTs(ts);
String cookieValue = "";
try {
cookieValue = TokenGenerator.genToken(saveUserid, ts, RedisUtils.findSeed());
} catch (EncryptException e) {
this.logger.error("Fail to generate cookie!", e);
} HashMap cookiesMap = new HashMap();
cookiesMap.put("tenant_username", saveUserid);
cookiesMap.put("tenant_token", cookieValue); for (Iterator iterator = cookiesMap.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
Cookie cookie = new Cookie(key, URLEncoder.encode((String) cookiesMap.get(key)));
cookie.setPath("/"); cookie.setMaxAge(-1);
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
try {
RedisUtils.cacheUser(saveUserid, user);
} catch (Exception e) {
this.logger.error("登陆信息写入到redis缓存中失败", e);
}
} else {
long ts = existUser.getLoginTs();
String cookieValue = "";
try {
cookieValue = TokenGenerator.genToken(saveUserid, ts, RedisUtils.findSeed());
} catch (EncryptException e) {
this.logger.error("Fail to generate cookie!", e);
} cookiesMap = new HashMap();
cookiesMap.put("tenant_username", saveUserid);
cookiesMap.put("tenant_token", cookieValue); for (iterator = cookiesMap.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
Cookie cookie = new Cookie(key, URLEncoder.encode((String) cookiesMap.get(key)));
cookie.setPath("/"); cookie.setMaxAge(-1);
cookie.setHttpOnly(true);
response.addCookie(cookie);
}
}
} private void saveAssertion(String ticket, Assertion assertion) {
if (assertion == null)
return;
AttributePrincipal principal = assertion.getPrincipal();
String userID = "";
if (principal != null) {
Map params = principal.getAttributes();
Object userId = params.get("userId");
if (userId != null) {
userID = userId.toString();
}
}
if ((userID != null) && (!(userID.equalsIgnoreCase("")))) {
String saveUserid = ticket + "__" + userID;
RedisUtils.putSessionCacheAttribute("tenant_assertion", saveUserid, assertion);
}
} private String getCasUrl(FilterConfig filterConfig) {
String casUrl = PropertyUtil.getPropertyByKey("cas.url");
if (StringUtils.isBlank(casUrl)) {
casUrl = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
}
return casUrl;
}
}

  这样,servername 和 casServerUrlPrefix 获取的逻辑可以自定义了,例如可以放到 .properties 中。

  3.通过casServerUrlPrefix 和 Cas10TicketValidator 或(Cas20ProxyTicketValidator)的 getUrlSuffix方法(返回值分别是“validate”和“serviceValidate”)可以确定出 具体的校验ticket的接口。通过查看 cas server的web.xml,可以在servlet-mapping中看到getUrlSuffix返回值对应的servlet是org.springframework.web.servlet.DispatcherServlet,参数contextConfigLocation 对应的值是'/WEB-INF/cas-servlet.xml, /WEB-INF/cas-servlet-*.xml',里面定义了springmvc初始的handlerMapping和handlerAdapter,如下。

  <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

  <bean
id="handlerMappingC"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"
p:alwaysUseFullPath="true">
<property name="mappings">
<util:properties>
<prop key="/serviceValidate">serviceValidateController</prop>
<prop key="/proxyValidate">proxyValidateController</prop> <prop key="/p3/serviceValidate">v3ServiceValidateController</prop>
<prop key="/p3/proxyValidate">v3ProxyValidateController</prop>
<prop key="/validate">legacyValidateController</prop>
<prop key="/proxy">proxyController</prop>
<prop key="/authorizationFailure.html">passThroughController</prop>
</util:properties>
</property>
</bean>

  找到 '/serviceValidate' 对应的bean -> serviceValidateController

   <bean id="abstractValidateController" class="org.jasig.cas.web.ServiceValidateController" abstract="true"
p:centralAuthenticationService-ref="centralAuthenticationService"
p:proxyHandler-ref="proxy20Handler"
p:argumentExtractor-ref="casArgumentExtractor"
p:servicesManager-ref="servicesManager" /> <bean id="serviceValidateController" parent="abstractValidateController"
p:validationSpecificationClass="org.jasig.cas.validation.Cas20WithoutProxyingValidationSpecification"/>

  于是可以确认 最终调用 org.jasig.cas.web.ServiceValidateController 进行ticket验证。到底是调用哪个方法呢? 这里先看一下 springmvc中 DispatchServlet的doDispatch方法,如下。

  protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
ModelAndView mv = null;
Exception dispatchException = null; try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//这个是重点,第一步由HandlerMapping找到对应的handler
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
} // Determine handler adapter for the current request.
//这是第二步,找到合适的HandlerAdapter,然后由它来调度执行handler的方法
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
} if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
} try {
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
} applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}

  接下来看一下  org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter 的handle方法,如下。

package org.springframework.web.servlet.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView; public class SimpleControllerHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return handler instanceof Controller;
} public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
} public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}

  可见,handlerAdapter的handle方法调用了某个具体handler的handleRequest方法,查看如上handler配置中 org.jasig.cas.web.ServiceValidateController 的handleRequest方法,如下。

package org.springframework.web.servlet.mvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.WebContentGenerator;
import org.springframework.web.util.WebUtils; public abstract class AbstractController extends WebContentGenerator implements Controller {
private boolean synchronizeOnSession; public AbstractController() {
this.synchronizeOnSession = false;
} public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
this.synchronizeOnSession = synchronizeOnSession;
} public final boolean isSynchronizeOnSession() {
return this.synchronizeOnSession;
} public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
checkAndPrepare(request, response, this instanceof LastModified); if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
} return handleRequestInternal(request, response);
} protected abstract ModelAndView handleRequestInternal(HttpServletRequest paramHttpServletRequest,
HttpServletResponse paramHttpServletResponse) throws Exception;
}

  父类handleRequest方法调用了handleRequestInternal方法,子类ServiceValidateController 自身重写了 handleRequestInternal方法。

/*
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.web; import org.jasig.cas.CasProtocolConstants;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.AuthenticationException;
import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.HttpBasedServiceCredential;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.authentication.principal.WebApplicationService;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.services.UnauthorizedProxyingException;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.jasig.cas.ticket.TicketException;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.TicketValidationException;
import org.jasig.cas.ticket.proxy.ProxyHandler;
import org.jasig.cas.validation.Assertion;
import org.jasig.cas.validation.Cas20ProtocolValidationSpecification;
import org.jasig.cas.validation.ValidationSpecification;
import org.jasig.cas.web.support.ArgumentExtractor;
import org.jasig.cas.web.view.CasViewConstants;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.net.URL;
import java.util.Collections;
import java.util.Map; /**
* Process the /validate , /serviceValidate , and /proxyValidate URL requests.
* <p>
* Obtain the Service Ticket and Service information and present them to the CAS
* validation services. Receive back an Assertion containing the user Principal
* and (possibly) a chain of Proxy Principals. Store the Assertion in the Model
* and chain to a View to generate the appropriate response (CAS 1, CAS 2 XML,
* SAML, ...).
*
* @author Scott Battaglia
* @author Misagh Moayyed
* @since 3.0.0
*/
public class ServiceValidateController extends DelegateController {
/** View if Service Ticket Validation Fails. */
public static final String DEFAULT_SERVICE_FAILURE_VIEW_NAME = "cas2ServiceFailureView"; /** View if Service Ticket Validation Succeeds. */
public static final String DEFAULT_SERVICE_SUCCESS_VIEW_NAME = "cas2ServiceSuccessView"; /** Implementation of Service Manager. */
@NotNull
private ServicesManager servicesManager; /** The CORE which we will delegate all requests to. */
@NotNull
private CentralAuthenticationService centralAuthenticationService; /** The validation protocol we want to use. */
@NotNull
private Class<?> validationSpecificationClass = Cas20ProtocolValidationSpecification.class; /** The proxy handler we want to use with the controller. */
@NotNull
private ProxyHandler proxyHandler; /** The view to redirect to on a successful validation. */
@NotNull
private String successView = DEFAULT_SERVICE_SUCCESS_VIEW_NAME; /** The view to redirect to on a validation failure. */
@NotNull
private String failureView = DEFAULT_SERVICE_FAILURE_VIEW_NAME; /** Extracts parameters from Request object. */
@NotNull
private ArgumentExtractor argumentExtractor; /**
* Overrideable method to determine which credentials to use to grant a
* proxy granting ticket. Default is to use the pgtUrl.
*
* @param service the webapp service requesting proxy
* @param request the HttpServletRequest object.
* @return the credentials or null if there was an error or no credentials
* provided.
*/
protected Credential getServiceCredentialsFromRequest(final WebApplicationService service, final HttpServletRequest request) {
final String pgtUrl = request.getParameter(CasProtocolConstants.PARAMETER_PROXY_CALLBACK_URL);
if (StringUtils.hasText(pgtUrl)) {
try {
final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
verifyRegisteredServiceProperties(registeredService, service);
return new HttpBasedServiceCredential(new URL(pgtUrl), registeredService);
} catch (final Exception e) {
logger.error("Error constructing pgtUrl", e);
}
} return null;
} /**
* Inits the binder with the required fields. <code>renew</code> is required.
*
* @param request the request
* @param binder the binder
*/
protected void initBinder(final HttpServletRequest request, final ServletRequestDataBinder binder) {
binder.setRequiredFields("renew");
} @Override
protected final ModelAndView handleRequestInternal(final HttpServletRequest request, final HttpServletResponse response)
throws Exception {
final WebApplicationService service = this.argumentExtractor.extractService(request);
final String serviceTicketId = service != null ? service.getArtifactId() : null; if (service == null || serviceTicketId == null) {
logger.debug("Could not identify service and/or service ticket for service: [{}]", service);
return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_REQUEST,
CasProtocolConstants.ERROR_CODE_INVALID_REQUEST, null);
} try {
final Credential serviceCredential = getServiceCredentialsFromRequest(service, request);
TicketGrantingTicket proxyGrantingTicketId = null; if (serviceCredential != null) {
try {
proxyGrantingTicketId = this.centralAuthenticationService.delegateTicketGrantingTicket(serviceTicketId,
serviceCredential);
logger.debug("Generated PGT [{}] off of service ticket [{}] and credential [{}]",
proxyGrantingTicketId.getId(), serviceTicketId, serviceCredential);
} catch (final AuthenticationException e) {
logger.info("Failed to authenticate service credential {}", serviceCredential);
} catch (final TicketException e) {
logger.error("Failed to create proxy granting ticket for {}", serviceCredential, e);
} if (proxyGrantingTicketId == null) {
return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
new Object[] {serviceCredential.getId()});
}
} final Assertion assertion = this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service); final ValidationSpecification validationSpecification = this.getCommandClass();
final ServletRequestDataBinder binder = new ServletRequestDataBinder(validationSpecification, "validationSpecification");
initBinder(request, binder);
binder.bind(request); if (!validationSpecification.isSatisfiedBy(assertion)) {
logger.debug("Service ticket [{}] does not satisfy validation specification.", serviceTicketId);
return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_TICKET,
CasProtocolConstants.ERROR_CODE_INVALID_TICKET, null);
} String proxyIou = null;
if (serviceCredential != null && this.proxyHandler.canHandle(serviceCredential)) {
proxyIou = this.proxyHandler.handle(serviceCredential, proxyGrantingTicketId);
if (StringUtils.isEmpty(proxyIou)) {
return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK,
new Object[] {serviceCredential.getId()});
}
} onSuccessfulValidation(serviceTicketId, assertion);
logger.debug("Successfully validated service ticket {} for service [{}]", serviceTicketId, service.getId());
return generateSuccessView(assertion, proxyIou, service, proxyGrantingTicketId);
} catch (final TicketValidationException e) {
final String code = e.getCode();
return generateErrorView(code, code,
new Object[] {serviceTicketId, e.getOriginalService().getId(), service.getId()});
} catch (final TicketException te) {
return generateErrorView(te.getCode(), te.getCode(),
new Object[] {serviceTicketId});
} catch (final UnauthorizedProxyingException e) {
return generateErrorView(e.getMessage(), e.getMessage(), new Object[] {service.getId()});
} catch (final UnauthorizedServiceException e) {
return generateErrorView(e.getMessage(), e.getMessage(), null);
}
} /**
* Triggered on successful validation events. Extensions are to
* use this as hook to plug in behvior.
*
* @param serviceTicketId the service ticket id
* @param assertion the assertion
*/
protected void onSuccessfulValidation(final String serviceTicketId, final Assertion assertion) {
// template method with nothing to do.
} /**
* Generate error view, set to {@link #setFailureView(String)}.
*
* @param code the code
* @param description the description
* @param args the args
* @return the model and view
*/
private ModelAndView generateErrorView(final String code, final String description, final Object[] args) {
final ModelAndView modelAndView = new ModelAndView(this.failureView);
final String convertedDescription = getMessageSourceAccessor().getMessage(description, args, description);
modelAndView.addObject("code", code);
modelAndView.addObject("description", convertedDescription); return modelAndView;
} /**
* Generate the success view. The result will contain the assertion and the proxy iou.
*
* @param assertion the assertion
* @param proxyIou the proxy iou
* @param service the validated service
* @param proxyGrantingTicket the proxy granting ticket
* @return the model and view, pointed to the view name set by
*/
private ModelAndView generateSuccessView(final Assertion assertion, final String proxyIou,
final WebApplicationService service,
final TicketGrantingTicket proxyGrantingTicket) { final ModelAndView success = new ModelAndView(this.successView);
success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_ASSERTION, assertion);
success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_SERVICE, service);
success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET_IOU, proxyIou);
if (proxyGrantingTicket != null) {
success.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET, proxyGrantingTicket.getId());
}
final Map<String, ?> augmentedModelObjects = augmentSuccessViewModelObjects(assertion);
if (augmentedModelObjects != null) {
success.addAllObjects(augmentedModelObjects);
}
return success;
} /**
* Augment success view model objects. Provides
* a way for extension of this controller to dynamically
* populate the model object with attributes
* that describe a custom nature of the validation protocol.
*
* @param assertion the assertion
* @return map of objects each keyed to a name
*/
protected Map<String, ?> augmentSuccessViewModelObjects(final Assertion assertion) {
return Collections.emptyMap();
} /**
* Gets the command class based on {@link #setValidationSpecificationClass(Class)}.
*
* @return the command class
*/
private ValidationSpecification getCommandClass() {
try {
return (ValidationSpecification) this.validationSpecificationClass.newInstance();
} catch (final Exception e) {
throw new RuntimeException(e);
}
} /**
* {@inheritDoc}
*/
@Override
public boolean canHandle(final HttpServletRequest request, final HttpServletResponse response) {
return true;
} /**
* @param centralAuthenticationService The centralAuthenticationService to
* set.
*/
public final void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) {
this.centralAuthenticationService = centralAuthenticationService;
} public final void setArgumentExtractor(final ArgumentExtractor argumentExtractor) {
this.argumentExtractor = argumentExtractor;
} /**
* @param validationSpecificationClass The authenticationSpecificationClass
* to set.
*/
public final void setValidationSpecificationClass(final Class<?> validationSpecificationClass) {
this.validationSpecificationClass = validationSpecificationClass;
} /**
* @param failureView The failureView to set.
*/
public final void setFailureView(final String failureView) {
this.failureView = failureView;
} /**
* @param successView The successView to set.
*/
public final void setSuccessView(final String successView) {
this.successView = successView;
} /**
* @param proxyHandler The proxyHandler to set.
*/
public final void setProxyHandler(final ProxyHandler proxyHandler) {
this.proxyHandler = proxyHandler;
} /**
* Sets the services manager.
*
* @param servicesManager the new services manager
*/
public final void setServicesManager(final ServicesManager servicesManager) {
this.servicesManager = servicesManager;
} /**
* Ensure that the service is found and enabled in the service registry.
* @param registeredService the located entry in the registry
* @param service authenticating service
* @throws UnauthorizedServiceException
*/
private void verifyRegisteredServiceProperties(final RegisteredService registeredService, final Service service) {
if (registeredService == null) {
final String msg = String.format("ServiceManagement: Unauthorized Service Access. "
+ "Service [%s] is not found in service registry.", service.getId());
logger.warn(msg);
throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
}
if (!registeredService.getAccessStrategy().isServiceAccessAllowed()) {
final String msg = String.format("ServiceManagement: Unauthorized Service Access. "
+ "Service [%s] is not enabled in service registry.", service.getId()); logger.warn(msg);
throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg);
}
}
}

  handleRequestInternal方法中调用了一个argumentExtractor,在上面的配置中可以看到,可确定这是个 spring bean。这个bean在 WEB-INF/spring-configuration/argumentExtractorsConfiguration.xml中,内容如下。

<?xml version="1.0" encoding="UTF-8"?>
<!-- Licensed to Apereo under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Apereo licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<description>
Argument Extractors are what are used to translate HTTP requests into requests of the appropriate protocol (i.e.
CAS, SAML, SAML2,
OpenId, etc.). By default, only CAS is enabled.
</description> <!--
<bean id="samlArgumentExtractor" class="org.jasig.cas.support.saml.web.support.SamlArgumentExtractor" />
--> <bean
id="casArgumentExtractor"
class="org.jasig.cas.web.support.CasArgumentExtractor"/> <util:list id="argumentExtractors">
<!-- <ref bean="samlArgumentExtractor" /> -->
<ref bean="casArgumentExtractor"/>
</util:list>
</beans>

  查看org.jasig.cas.web.support.CasArgumentExtractor内容如下。

/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/
package org.jasig.cas.web.support; import javax.servlet.http.HttpServletRequest;
import org.jasig.cas.authentication.principal.SimpleWebApplicationServiceImpl;
import org.jasig.cas.authentication.principal.WebApplicationService; public final class CasArgumentExtractor extends AbstractArgumentExtractor {
public WebApplicationService extractServiceInternal(HttpServletRequest request) {
return SimpleWebApplicationServiceImpl.createServiceFrom(request);
}
}

  最后查看org.jasig.cas.authentication.principal.SimpleWebApplicationServiceImpl内容如下。

/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/
package org.jasig.cas.authentication.principal; import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils; public final class SimpleWebApplicationServiceImpl extends AbstractWebApplicationService {
private static final long serialVersionUID = 8334068957483758042L;
private static final String CONST_PARAM_SERVICE = "service";
private static final String CONST_PARAM_TARGET_SERVICE = "targetService";
private static final String CONST_PARAM_TICKET = "ticket";
private static final String CONST_PARAM_METHOD = "method";
private final Response.ResponseType responseType; public SimpleWebApplicationServiceImpl(String id) {
this(id, id, null, null);
} private SimpleWebApplicationServiceImpl(String id, String originalUrl, String artifactId,
Response.ResponseType responseType) {
super(id, originalUrl, artifactId);
this.responseType = responseType;
} public static SimpleWebApplicationServiceImpl createServiceFrom(HttpServletRequest request) {
String targetService = request.getParameter("targetService");
String service = request.getParameter("service");
String serviceAttribute = (String) request.getAttribute("service");
String method = request.getParameter("method");
String serviceToUse;
String serviceToUse;
if (StringUtils.hasText(targetService)) {
serviceToUse = targetService;
} else {
String serviceToUse;
if (StringUtils.hasText(service))
serviceToUse = service;
else {
serviceToUse = serviceAttribute;
}
}
if (!(StringUtils.hasText(serviceToUse))) {
return null;
} String id = cleanupUrl(serviceToUse);
String artifactId = request.getParameter("ticket"); return new SimpleWebApplicationServiceImpl(id, serviceToUse, artifactId,
("POST".equals(method)) ? Response.ResponseType.POST : Response.ResponseType.REDIRECT);
} public Response getResponse(String ticketId) {
Map parameters = new HashMap(); if (StringUtils.hasText(ticketId)) {
parameters.put("ticket", ticketId);
} if (Response.ResponseType.POST == this.responseType) {
return DefaultResponse.getPostResponse(getOriginalUrl(), parameters);
}
return DefaultResponse.getRedirectResponse(getOriginalUrl(), parameters);
}
}

  在SimpleWebApplicationServiceImpl createServiceFrom方法中可以看到,ticket从request中取到。也就是对应 org.jasig.cas.web.ServiceValidateController 的handleRequestInternal 如下获取方式。

final WebApplicationService service = this.argumentExtractor.extractService(request);
final String serviceTicketId = service != null ? service.getArtifactId() : null;

通过配置deployerConfigContext.xml实现自定义认证(本例中提供了四中方式)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:sec="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- 数据源配置, 使用druid连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${druid.initialSize}"/>
<property name="minIdle" value="${druid.minIdle}"/>
<property name="maxActive" value="${druid.maxActive}"/> <!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${druid.maxWait}"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" /> <property name="validationQuery" value="${druid.validationQuery}" />
<property name="testWhileIdle" value="${druid.testWhileIdle}" />
<property name="testOnBorrow" value="${druid.testOnBorrow}" />
<property name="testOnReturn" value="${druid.testOnReturn}" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。-->
<property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="${druid.maxPoolPreparedStatementPerConnectionSize}" />
</bean> <bean id="usernamePasswordAuthenticationHandler" class="com.hjzgg.auth.UsernamePasswordAuthenticationHandler"/> <util:map id="authenticationHandlersResolvers">
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
</util:map> <util:list id="authenticationMetadataPopulators">
<ref bean="successfulHandlerMetaDataPopulator" />
<ref bean="rememberMeAuthenticationMetaDataPopulator" />
</util:list> <bean id="attributeRepository" class="org.jasig.services.persondir.support.NamedStubPersonAttributeDao"
p:backingMap-ref="attrRepoBackingMap" /> <!--first 默认方式-->
<!--<alias name="acceptUsersAuthenticationHandler" alias="primaryAuthenticationHandler" />--> <!--second 使用 MD5对密码进行加密-->
<!--<bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" autowire="byName">
<constructor-arg value="MD5"/>
</bean >
<bean id="queryDatabaseAuthenticationHandler" name="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="passwordEncoder" ref="MD5PasswordEncoder"/>
</bean>--> <!--third 不使用加密算法-->
<!--<alias name="queryDatabaseAuthenticationHandler" alias="primaryAuthenticationHandler" />--> <!--forth 通过继承AbstractUsernamePasswordAuthenticationHandler,并实现authenticateUsernamePasswordInternal-->
<alias name="usernamePasswordAuthenticationHandler" alias="primaryAuthenticationHandler" />
<alias name="personDirectoryPrincipalResolver" alias="primaryPrincipalResolver" /> <alias name="dataSource" alias="queryDatabaseDataSource" /> <util:map id="attrRepoBackingMap">
<entry key="uid" value="uid" />
<entry key="eduPersonAffiliation" value="eduPersonAffiliation" />
<entry key="groupMembership" value="groupMembership" />
<entry>
<key><value>memberOf</value></key>
<list>
<value>faculty</value>
<value>staff</value>
<value>org</value>
</list>
</entry>
</util:map> <alias name="serviceThemeResolver" alias="themeResolver" /> <alias name="jsonServiceRegistryDao" alias="serviceRegistryDao" /> <alias name="defaultTicketRegistry" alias="ticketRegistry" /> <alias name="ticketGrantingTicketExpirationPolicy" alias="grantingTicketExpirationPolicy" />
<alias name="multiTimeUseOrTimeoutExpirationPolicy" alias="serviceTicketExpirationPolicy" /> <alias name="anyAuthenticationPolicy" alias="authenticationPolicy" />
<alias name="acceptAnyAuthenticationPolicyFactory" alias="authenticationPolicyFactory" /> <bean id="auditTrailManager"
class="org.jasig.inspektr.audit.support.Slf4jLoggingAuditTrailManager"
p:entrySeparator="${cas.audit.singleline.separator:|}"
p:useSingleLine="${cas.audit.singleline:false}"/> <alias name="neverThrottle" alias="authenticationThrottle" /> <util:list id="monitorsList">
<ref bean="memoryMonitor" />
<ref bean="sessionMonitor" />
</util:list> <alias name="defaultPrincipalFactory" alias="principalFactory" />
<alias name="defaultAuthenticationTransactionManager" alias="authenticationTransactionManager" />
<alias name="defaultPrincipalElectionStrategy" alias="principalElectionStrategy" />
<alias name="tgcCipherExecutor" alias="defaultCookieCipherExecutor" />
</beans>

相关参考

CAS Properties

Database Authentication

CAS4.2.x

报异常:Application Not Authorized to Use CAS

cas4.2.7 取消https

Cas https 证书生成

keytool -genkey -keyalg RSA -alias cas.server.com -dname "cn=cas.server.com" -keystore /usr/data/tomcat.keystore

sudo keytool -export -alias cas.server.com -keystore /usr/data/tomcat.keystore  -file /usr/data/tomcat.cer

cd  /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/security

sudo keytool -import -keystore cacerts -file /usr/data/tomcat.cer -alias cas.server.com -storepass changeit

sudo keytool -delete -trustcacerts -alias cas.server.com -keystore "/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/security/cacerts" -storepass changeit

keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit 

完整项目地址:https://github.com/hjzgg/cas4.2.7-authentication

cas4.2.7 template项目地址:https://github.com/hjzgg/cas-overlay-template-4.2.7

cas4.2.7实现单点登录的更多相关文章

  1. 单点登录(十八)----cas4.2.x客户端增加权限控制shiro

    我们在上面章节已经完成了cas4.2.x登录启用mongodb的验证方式. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 也完成了获取管理员身份属性 ...

  2. 单点登录(十七)----cas4.2.x登录mongodb验证方式成功后返回更多信息更多属性到客户端

    我们在之前已经完成了cas4.2.x登录使用mongodb验证方式登录成功了.也解决了登录名中使用中文乱码的问题. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方 ...

  3. 单点登录(十六)-----遇到问题-----cas4.2.x登录成功后报错No principal was found---cas中文乱码问题完美解决

    情况 我们之前已经完成了cas4.2.x登录使用mongodb验证方式并且自定义了加密. 单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密 但是悲剧的是 ...

  4. 单点登录(十五)-----实战-----cas4.2.x登录mongodb验证方式实现自定义加密

    我们在前一篇文章中实现了cas4.2.x登录使用mongodb验证方式. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程 也学习参考了cas5.0.x版 ...

  5. 单点登录(十三)-----实战-----cas4.2.X登录启用mongodb验证方式完整流程

    我们在之前的文章中中已经讲到了正确部署运行cas server 和 在cas client中配置. 在此基础上 我们去掉了https的验证,启用了http访问的模式. 单点登录(七)-----实战-- ...

  6. 2、cas4.0 单点登录 之 cas-client

    cas4.0 单点登录 之 cas-client cas4.0 单点登录 之 https证书已经做好了证书的准备工作.如今结合cas-server来配置单点登录: 一.安装cas服务端(cas-ser ...

  7. 【试水CAS-4.0.3】第07节_CASclient配置单点登录

    完整版见https://jadyer.github.io/2015/07/26/sso-cas-client-login/ 本文源代码下载:http://download.csdn.net/detai ...

  8. jasig CAS 实现单点登录 - java、php客户端登录实现

    jasig CAS项目本身就是一个完整的CAS单点登录服务 1.服务端需要把  认证处理类.用户属性返回值处理类 调整成我们自己处理类即可实现单点登录 2.java客户端需要引入cas-client- ...

  9. CSA单点登录环境配置

    本篇先写一些基础 今天看到一个cas单点登录的源码,搞环境就废了大半时间 <SSO CAS单点系列>http://www.imooc.com/article/3576 参考了这篇博客里的配 ...

随机推荐

  1. JavascriptS中的各结构的嵌套和函数

    各位朋友大家好,上周更新给大家分享了JavaScript的入门知识及各种常用结构的用法,那么,本次更新博主就跟大家更深入的聊一聊JS各结构的嵌套用法,及JS中及其常用的一种结构--函数.以下为函数和循 ...

  2. 基于Flink的windows--简介

    新的一年,新的开始,新的习惯,现在开始. 1.简介 Flink是德国一家公司名为dataArtisans的产品,2016年正式被apache提升为顶级项目(地位同spark.storm等开源架构).并 ...

  3. JQuery分页插件封装(源码来自百度,自己封装)

    最近由于项目的需要,做了一个基于JQuery的表格分页插件封装,部分源码来源百度,经由自己封装完成. 下面是具体代码和说明,仅供参考.第一步可以先将我的HTML,CSS,JS这三部分的代码创建好后先运 ...

  4. java面试题(二)

    21.描述一下JVM加载class文件的原理机制? 答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时 ...

  5. JS实现banner图轮换

    方法一: <!--灰色背景代码替换图片--> <!DOCTYPE html><html> <head> <meta charset="U ...

  6. 基于Hadoop分布式集群YARN模式下的TensorFlowOnSpark平台搭建

    1. 介绍 在过去几年中,神经网络已经有了很壮观的进展,现在他们几乎已经是图像识别和自动翻译领域中最强者[1].为了从海量数据中获得洞察力,需要部署分布式深度学习.现有的DL框架通常需要为深度学习设置 ...

  7. HNOI2017 滚粗记

    这次HNOI,感觉自己收获了很多啊,高一的蒟蒻,也就是去历练一番,长长见识吧.. $day0$ 上午做了一道斜率优化的题,下午好像在颓??晚上也不想复习了,看了会电视,$12$点才睡.. $day1$ ...

  8. [ext4]磁盘布局 - inode bitmap & table

    在[磁盘布局 group部分]已经介绍过ext4的整体布局,其中存在两个与inode有关的名称:inode bitmap和inode table. Inode bitmap,用于表示inode tab ...

  9. String属于“假引用类型”,代码为证(一个String引发的血案...)

    一直以为String是引用类型,今天写了个浅拷贝的测试,发现String有基本类型的特征. class A{ public int a = 555; } class User implements C ...

  10. C++接口的定义与实现的详细过程

    1.接口的定义与实现 所谓的接口,即将内部实现细节封装起来,外部用户用过预留的接口可以使用接口的功能而不需要知晓内部具体细节.C++中,通过类实现面向对象的编程,而在基类中只给出纯虚函数的声明,然后在 ...