实现原理:客户端提供一个登录界面,其中action为服务端登录地址,通过改写服务端的登录代码,生成ticket票据,写cookie,重定向到客户端的service地址(如果验证失败同样,并有错误码返回),过滤器判断是否带有ticket,验证通过获取远程用户名。
1、使用源码构建自己工程
版本:
cas-server 3.5.2 (https://github.com/apereo/cas/releases/tag/v3.5.2)
cas-client 3.4.1 (https://github.com/apereo/java-cas-client/releases)
cas-demo 为测试工程
其它的工程都是插件,可以不用(groupId,artifactId,version也可以自己定义)
改造登录页面分为服务器端和客户端,下面分别说明
2、服务器端改造
1)重载AuthenticationViaFormAction 的submit和validatorCode方法
submit 方法去掉了credentials 参数,该参数自己创建。用户名和密码从request 中获得。并且自定义errorCode 作为返回客户端的提示code。
this.centralAuthenticationService.createTicketGrantingTicket(credentials) 方法中实现了验证用户名和密码的方法。
public final String submit(final RequestContext context, final MessageContext messageContext) throws Exception {
final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);
final Service service = WebUtils.getService(context);
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
//用户名和密码从rquest 中获得
String username = request.getParameter("username");
if(!StringUtils.hasText(username)){
context.getFlowScope().put("errorCode", "required.username");//设置errorCode ,在跳转页面返回给demo 页面提示错误信息
return "error";
}
String password = request.getParameter("password");
if(!StringUtils.hasText(password)){
context.getFlowScope().put("errorCode", "required.password");
return "error";
}
//用户名和密码从rquest 中获得,自己构建对象
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials();
credentials.setUsername(username);
credentials.setPassword(password);
if (StringUtils.hasText(context.getRequestParameters().get("renew")) && ticketGrantingTicketId != null && service != null) {
try {
final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service, credentials);
WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
putWarnCookieIfRequestParameterPresent(context);
return "warn";
} catch (final TicketException e) {
if (e.getCause() != null && AuthenticationException.class.isAssignableFrom(e.getCause().getClass())) {
//populateErrorsInstance(e, messageContext);
context.getFlowScope().put("errorCode", e.getCode());
return "error";
}
this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);
if (logger.isDebugEnabled()) {
logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e);
}
}
}
try {
//createTicketGrantingTicket 方法验证用户名和密码(实现类SimpleTestUsernamePasswordAuthenticationHandler)
WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials));
putWarnCookieIfRequestParameterPresent(context);
return "success";
} catch (final TicketException e) {
//populateErrorsInstance(e, messageContext);
context.getFlowScope().put("errorCode", e.getCode());
return "error";
}
}
validatorCode 方法,参考:http://blog.csdn.net/convict_eva/article/details/52848475。只是参数不同
public final String validatorCode(final RequestContext context, final MessageContext messageContext) throws Exception {
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
HttpSession session = request.getSession();
String authcode = (String)session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
session.removeAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
String submitAuthcode =request.getParameter("authcode");
if(!StringUtils.hasText(submitAuthcode)){
context.getFlowScope().put("errorCode", NullAuthcodeAuthenticationException.CODE);
return "error";
}
if(submitAuthcode.equals(authcode)){
context.getFlowScope().remove("errorCode");
return "success";
}
context.getFlowScope().put("errorCode", BadAuthcodeAuthenticationException.CODE);
return "error";
}
package org.jasig.cas.web.flow;
import org.hibernate.validator.constraints.NotEmpty;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.web.support.ArgumentExtractor;
import org.jasig.cas.web.support.CookieRetrievingCookieGenerator;
import org.jasig.cas.web.support.WebUtils;
import org.springframework.util.StringUtils;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import javax.servlet.http.HttpServletRequest;
import javax.validation.constraints.NotNull;
import java.util.List;
public class RemoteLoginAction extends AbstractAction {
/** CookieGenerator for the Warnings. */
@NotNull
private CookieRetrievingCookieGenerator warnCookieGenerator;
/** CookieGenerator for the TicketGrantingTickets. */
@NotNull
private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator;
/** Extractors for finding the service. */
@NotEmpty
private List<ArgumentExtractor> argumentExtractors;
/** Boolean to note whether we've set the values on the generators or not. */
private boolean pathPopulated = false;
protected Event doExecute(final RequestContext context) throws Exception {
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
if (!this.pathPopulated) {
final String contextPath = context.getExternalContext().getContextPath();
final String cookiePath = StringUtils.hasText(contextPath) ? contextPath : "/";
logger.info("Setting path for cookies to: "
+ cookiePath);
this.warnCookieGenerator.setCookiePath(cookiePath);
this.ticketGrantingTicketCookieGenerator.setCookiePath(cookiePath);
this.pathPopulated = true;
}
context.getFlowScope().put("ticketGrantingTicketId", this.ticketGrantingTicketCookieGenerator.retrieveCookieValue(request));
context.getFlowScope().put("warnCookieValue",Boolean.valueOf(this.warnCookieGenerator.retrieveCookieValue(request)));
final Service service = WebUtils.getService(this.argumentExtractors,context);
if (service != null && logger.isDebugEnabled()) {
logger.debug("Placing service in FlowScope: " + service.getId());
}
context.getFlowScope().put("contextPath", request.getContextPath());
context.getFlowScope().put("service", service);
// 客户端必须传递loginUrl参数过来,否则无法确定登陆目标页面。作用:登录失败后重定向页面
if (StringUtils.hasText(request.getParameter("loginUrl"))) {
context.getFlowScope().put("remoteLoginUrl", request.getParameter("loginUrl"));
} else {
request.setAttribute("remoteLoginMessage", "loginUrl parameter must be supported.");
return error();
}
// 若参数包含submit则进行提交,否则进行验证票据
if (StringUtils.hasText(request.getParameter("submit"))) {
return result("submit");
} else {
return result("checkTicketGrantingTicket");
}
}
public void setTicketGrantingTicketCookieGenerator(
final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator) {
this.ticketGrantingTicketCookieGenerator = ticketGrantingTicketCookieGenerator;
}
public void setWarnCookieGenerator(final CookieRetrievingCookieGenerator warnCookieGenerator) {
this.warnCookieGenerator = warnCookieGenerator;
}
public void setArgumentExtractors(
final List<ArgumentExtractor> argumentExtractors) {
this.argumentExtractors = argumentExtractors;
}
}
package org.jasig.cas.web.flow;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import org.hibernate.