- 浏览: 154880 次
- 性别:
- 来自: 益阳
文章分类
- 全部博客 (98)
- 数据库 (11)
- 乱码 (1)
- java (38)
- struts2 (3)
- html (7)
- JQuery (10)
- javascript (4)
- 正则表达式 (1)
- jar异常 (1)
- IBATIS (1)
- dom4j (1)
- 那点小事 (2)
- my97 (1)
- jsp (2)
- 单元测试 (1)
- Eclipse (0)
- 错误类 (1)
- DB2 (1)
- 我们一家 (0)
- software (0)
- servicemix启动出错 (1)
- mongodb (1)
- mysql (1)
- shell (1)
- webservice (1)
- 架构 (3)
- 软件 (1)
- maven (1)
- spring (1)
- 工具 (1)
- 环境搭建 (1)
- js (1)
- robot-framework (1)
最新评论
-
qzg196:
楼主啊 我用的4.0的服务器版。为什么配置好后 地址已经跳转 ...
CAS-3.2.1自定义客户端登录界面----完整篇 -
月色无夜:
楼主你附带在项目中的证书导入密码是什么?
CAS-3.2.1自定义客户端登录界面----完整篇 -
zws_miss:
AuthenticationViaFormAction 这 ...
CAS-3.2.1自定义客户端登录界面----完整篇 -
hotapple:
...
tomcat下摘要认证(数据库配置用户角色)+java代码模拟请求 -
cherishit:
https的安装配置在哪里呵? 求拔刀相助
linux下:apache2.2.27 + tomcat7.0 + openssl-1.0.1g(集群+session共享|摘要认证|HTTPS)
前言:在已有的CAS SSO架构代码上,经过4天的研究终于完成了客户端登录界面需求,其中多亏网上的资料【让CAS支持客户端自定义登陆页面——服务器新篇与客户端新篇】本文也是基于此完成的,不过我修改了一些代码,CAS SSO的内部流程我只了解了60%,如果你想看CAS SSO原理,请忽略此文章;本文致力提供完整,详细的搭建流程,争取让你一次成功!附件中有本文工程,分客户端与服务端,采用maven搭建,想要运行请配置好maven环境,数据库采用mysql。
原文链接地址:http://lsz1023-126-com.iteye.com/blog/2098973
实现原理:
一、 逻辑
客户端修改CAS Authentication Filter过滤器,该过滤器会判断用户是否登录,如果没有登录则跳转到自身配置的登录界面;
用户输入正确的信息,登录时,会被提交到服务端的登录流程中;
服务端通过新增一个remoteLogin处理类,专门处理客户端自定义登录业务;
该remoteLogin处理类与原始的login处理极为类似,只是修改了获取用户名与密码的方式;
如果用户名与密码不匹配,校验失败,会通过remoteCallbackView.jsp界面将错误提示与service一并响应给客户端的登录界面,接收之后将错误提示显示到界面上;
如果用户名与密码匹配,校验成功,会直接重定向到客户端的service页面,这时CAS Authentication Filter过滤器与CAS Validation Filter过滤器分别校验用户是否登录与ticket票据是否正确,完成最后的校验,否则要求用户重新登录。
二、 修改点
客户端
1. 修改web.xml,修改原先的CAS Authentication Filter过滤器
2. 新增RemoteAuthenticationFilter过滤器
3. 新增login.jsp
服务端
1. 修改web.xml,给cas过滤器添加remoteLogin servlet-mapping映射
2. 新增RemoteLoginAction登录处理类与AuthenticationViaFormAction表单处理类
3. 新增remoteLogin-webflow.xml自定义登录webflow流程文件
4. 修改cas-servlet.xml配置文件,新增一些bean配置
5. 新增remoteCallbackView.jsp响应界面,校验错误时用来通知客户端登录界面。
客户端篇:
1.替换原来过滤器org.jasig.cas.client.authentication.AuthenticationFilter,改成自己的过滤器RemoteAuthenticationFilter.java,这个过滤器可以自己随便放到哪个包中,保证web.xml能够正确引用到就行:
2.web.xml中配置:
旧配置
修改成这样,注:其它路径mapping不用改。
3.加上你自己定义的登录界面,注:我修改了一些网上介绍的代码:
至此客户端完结!
服务端篇:注我没有加上登出代码,因为登出代码可以使用原有的
1.添加客户端登录Action,org.jasig.cas.web.flow.RemoteLoginAction:
2.重写LoginForm代码,org.jasig.cas.web.flow.AuthenticationViaFormAction重写,此类基本上采用原有代码,只是添加了获取用户名与密码的代码:
3.web.xml配置,原有基础上新增这两句:
4.在cas-servlet.xml中最后面增加以下信息:
5.新建一个文件与login-webflow.xml同级,remoteLogin-webflow.xml:
6.加上一个回调视图配置,在default_views.properties中新增以下两句:
### 配置远程回调页面
remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlView
remoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp
其它不变
7.加上回调页面jsp:
完结...
请关注下一篇,shiro + cas sso实现客户端自定义登录界面完整实现
原文链接地址:http://lsz1023-126-com.iteye.com/blog/2098973
实现原理:
一、 逻辑
客户端修改CAS Authentication Filter过滤器,该过滤器会判断用户是否登录,如果没有登录则跳转到自身配置的登录界面;
用户输入正确的信息,登录时,会被提交到服务端的登录流程中;
服务端通过新增一个remoteLogin处理类,专门处理客户端自定义登录业务;
该remoteLogin处理类与原始的login处理极为类似,只是修改了获取用户名与密码的方式;
如果用户名与密码不匹配,校验失败,会通过remoteCallbackView.jsp界面将错误提示与service一并响应给客户端的登录界面,接收之后将错误提示显示到界面上;
如果用户名与密码匹配,校验成功,会直接重定向到客户端的service页面,这时CAS Authentication Filter过滤器与CAS Validation Filter过滤器分别校验用户是否登录与ticket票据是否正确,完成最后的校验,否则要求用户重新登录。
二、 修改点
客户端
1. 修改web.xml,修改原先的CAS Authentication Filter过滤器
2. 新增RemoteAuthenticationFilter过滤器
3. 新增login.jsp
服务端
1. 修改web.xml,给cas过滤器添加remoteLogin servlet-mapping映射
2. 新增RemoteLoginAction登录处理类与AuthenticationViaFormAction表单处理类
3. 新增remoteLogin-webflow.xml自定义登录webflow流程文件
4. 修改cas-servlet.xml配置文件,新增一些bean配置
5. 新增remoteCallbackView.jsp响应界面,校验错误时用来通知客户端登录界面。
客户端篇:
1.替换原来过滤器org.jasig.cas.client.authentication.AuthenticationFilter,改成自己的过滤器RemoteAuthenticationFilter.java,这个过滤器可以自己随便放到哪个包中,保证web.xml能够正确引用到就行:
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 { 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 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")); 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); if (log.isDebugEnabled()) { log.debug("Constructed service url: " + serviceUrl); } String urlToRedirectTo = CommonUtils.constructRedirectUrl( this.casServerLoginUrl, getServiceParameterName(), serviceUrl, this.renew, this.gateway); // 加入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; } }
2.web.xml中配置:
旧配置
<filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://localhost:8443/cas/login</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:8080</param-value> </init-param> <init-param> <param-name>renew</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>gateway</param-name> <param-value>false</param-value> </init-param> </filter>
修改成这样,注:其它路径mapping不用改。
<filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.demo.user.common.RemoteAuthenticationFilter</filter-class> <init-param> <param-name>localLoginUrl</param-name> <param-value>http://localhost:8080/app/mylogin.jsp</param-value> </init-param> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://localhost:8443/cas/remoteLogin</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://localhost:8080</param-value> </init-param> </filter>
3.加上你自己定义的登录界面,注:我修改了一些网上介绍的代码:
<%@ 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>APP1客户端登录</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.substr(1, queryString.length - 1).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); } // 注入service var service = getParam("service"); if (service) document.getElementById("service").value = decodeURIComponent(service); else document.getElementById("service").value = location.href; } </script> </head> <body> <h1>APP1客户端登录</h1> <div id="errorMessage" style="color: red;"></div> <form id="myLoginForm" action="https://localhost:8443/cas/remoteLogin?service=http://localhost:8080/app/pages/home.jsp" method="post"> <input type="hidden" name="loginUrl" value="http://localhost:8080/app/mylogin.jsp"> <input type="hidden" name="submit" value="true" /> <input type="hidden" name="lt" id="loginTicket" value="" /> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密 码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td colspan="2"><input type="submit" value="登陆" /></td> </tr> </table> </form> <script type="text/javascript"> init() </script> </body> </html>
至此客户端完结!
服务端篇:注我没有加上登出代码,因为登出代码可以使用原有的
1.添加客户端登录Action,org.jasig.cas.web.flow.RemoteLoginAction:
/** * 远程登陆票据提供Action. 根据InitialFlowSetupAction修改. * 由于InitialFlowSetupAction为final类,因此只能将代码复制过来再进行修改. */ 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. */ @NotNull @Size(min = 1) 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))); // 存放service url // context.getFlowScope().put("serviceUrl", request.getParameter("service")); final Service service = WebUtils.getService(this.argumentExtractors, context); if (service != null && logger.isDebugEnabled()) { logger.debug("Placing service in FlowScope: " + service.getId()); } 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; } }
2.重写LoginForm代码,org.jasig.cas.web.flow.AuthenticationViaFormAction重写,此类基本上采用原有代码,只是添加了获取用户名与密码的代码:
public class AuthenticationViaFormAction { /** * Binder that allows additional binding of form object beyond Spring * defaults. */ private CredentialsBinder credentialsBinder; /** Core we delegate to for handling all ticket related tasks. */ @NotNull private CentralAuthenticationService centralAuthenticationService; @NotNull private CookieGenerator warnCookieGenerator; protected Logger logger = LoggerFactory.getLogger(getClass()); public final void doBind(final RequestContext context, final Credentials credentials) throws Exception { final HttpServletRequest request = WebUtils .getHttpServletRequest(context); if (this.credentialsBinder != null && this.credentialsBinder.supports(credentials.getClass())) { this.credentialsBinder.bind(request, credentials); } } public final String submit(final RequestContext context, final MessageContext messageContext) throws Exception { // Validate login ticket final String authoritativeLoginTicket = WebUtils .getLoginTicketFromFlowScope(context); final String providedLoginTicket = WebUtils .getLoginTicketFromRequest(context); if (!authoritativeLoginTicket.equals(providedLoginTicket)) { this.logger.warn("Invalid login ticket " + providedLoginTicket); final String code = "INVALID_TICKET"; messageContext.addMessage(new MessageBuilder().error().code(code) .arg(providedLoginTicket).defaultText(code).build()); return "error"; } final String ticketGrantingTicketId = WebUtils .getTicketGrantingTicketId(context); final Service service = WebUtils.getService(context); final HttpServletRequest request = WebUtils .getHttpServletRequest(context); org.jasig.cas.authentication.principal.UsernamePasswordCredentials credentials = new org.jasig.cas.authentication.principal.UsernamePasswordCredentials(); credentials.setPassword(request.getParameter("password")); credentials.setUsername(request.getParameter("username")); 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(context, e, messageContext); return "error"; } this.centralAuthenticationService .destroyTicketGrantingTicket(ticketGrantingTicketId); if (logger.isDebugEnabled()) { logger.debug( "Attempted to generate a ServiceTicket using renew=true with different credentials", e); } } } try { WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService .createTicketGrantingTicket(credentials)); putWarnCookieIfRequestParameterPresent(context); return "success"; } catch (final TicketException e) { populateErrorsInstance(context, e, messageContext); return "error"; } } public final String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception { // Validate login ticket final String authoritativeLoginTicket = WebUtils .getLoginTicketFromFlowScope(context); final String providedLoginTicket = WebUtils .getLoginTicketFromRequest(context); if (!authoritativeLoginTicket.equals(providedLoginTicket)) { this.logger.warn("Invalid login ticket " + providedLoginTicket); final String code = "INVALID_TICKET"; messageContext.addMessage(new MessageBuilder().error().code(code) .arg(providedLoginTicket).defaultText(code).build()); return "error"; } final String ticketGrantingTicketId = WebUtils .getTicketGrantingTicketId(context); final Service service = WebUtils.getService(context); 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 (isCauseAuthenticationException(e)) { populateErrorsInstance(e, messageContext); return getAuthenticationExceptionEventId(e); } this.centralAuthenticationService .destroyTicketGrantingTicket(ticketGrantingTicketId); if (logger.isDebugEnabled()) { logger.debug( "Attempted to generate a ServiceTicket using renew=true with different credentials", e); } } } try { WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService .createTicketGrantingTicket(credentials)); putWarnCookieIfRequestParameterPresent(context); return "success"; } catch (final TicketException e) { populateErrorsInstance(e, messageContext); if (isCauseAuthenticationException(e)) return getAuthenticationExceptionEventId(e); return "error"; } } private void populateErrorsInstance(final TicketException e, final MessageContext messageContext) { try { messageContext.addMessage(new MessageBuilder().error() .code(e.getCode()).defaultText(e.getCode()).build()); } catch (final Exception fe) { logger.error(fe.getMessage(), fe); } } private void populateErrorsInstance(final RequestContext context, final TicketException e, final MessageContext messageContext) { try { messageContext.addMessage(new MessageBuilder().error() .code(e.getCode()).defaultText(e.getCode()).build()); Message[] messages = messageContext.getAllMessages(); context.getFlowScope().put("remoteLoginMessage", messages[messages.length - 1].getText()); } catch (final Exception fe) { logger.error(fe.getMessage(), fe); } } private void putWarnCookieIfRequestParameterPresent( final RequestContext context) { final HttpServletResponse response = WebUtils .getHttpServletResponse(context); if (StringUtils.hasText(context.getExternalContext() .getRequestParameterMap().get("warn"))) { this.warnCookieGenerator.addCookie(response, "true"); } else { this.warnCookieGenerator.removeCookie(response); } } private AuthenticationException getAuthenticationExceptionAsCause( final TicketException e) { return (AuthenticationException) e.getCause(); } private String getAuthenticationExceptionEventId(final TicketException e) { final AuthenticationException authEx = getAuthenticationExceptionAsCause(e); if (this.logger.isDebugEnabled()) this.logger .debug("An authentication error has occurred. Returning the event id " + authEx.getType()); return authEx.getType(); } private boolean isCauseAuthenticationException(final TicketException e) { return e.getCause() != null && AuthenticationException.class.isAssignableFrom(e.getCause() .getClass()); } public final void setCentralAuthenticationService( final CentralAuthenticationService centralAuthenticationService) { this.centralAuthenticationService = centralAuthenticationService; } /** * Set a CredentialsBinder for additional binding of the HttpServletRequest * to the Credentials instance, beyond our default binding of the * Credentials as a Form Object in Spring WebMVC parlance. By the time we * invoke this CredentialsBinder, we have already engaged in default binding * such that for each HttpServletRequest parameter, if there was a JavaBean * property of the Credentials implementation of the same name, we have set * that property to be the value of the corresponding request parameter. * This CredentialsBinder plugin point exists to allow consideration of * things other than HttpServletRequest parameters in populating the * Credentials (or more sophisticated consideration of the * HttpServletRequest parameters). * * @param credentialsBinder * the credentials binder to set. */ public final void setCredentialsBinder( final CredentialsBinder credentialsBinder) { this.credentialsBinder = credentialsBinder; } public final void setWarnCookieGenerator( final CookieGenerator warnCookieGenerator) { this.warnCookieGenerator = warnCookieGenerator; } }
3.web.xml配置,原有基础上新增这两句:
<servlet-mapping> <servlet-name>cas</servlet-name> <url-pattern>/remoteLogin</url-pattern> </servlet-mapping>
4.在cas-servlet.xml中最后面增加以下信息:
<bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/remoteLogin">remoteLoginController</prop> </props> </property> <property name="interceptors"> <list> <ref bean="localeChangeInterceptor" /> </list> </property> </bean> <bean id="remoteLoginController" class="org.springframework.webflow.mvc.servlet.FlowController"> <property name="flowExecutor" ref="remoteLoginFlowExecutor" /> <property name="flowUrlHandler" ref="flowUrlHandler" /> </bean> <webflow:flow-executor id="remoteLoginFlowExecutor" flow-registry="remoteLoginFlowRegistry"> <webflow:flow-execution-attributes> <webflow:always-redirect-on-pause value="false" /> </webflow:flow-execution-attributes> </webflow:flow-executor> <webflow:flow-registry id="remoteLoginFlowRegistry" flow-builder-services="builder"> <webflow:flow-location path="/WEB-INF/remoteLogin-webflow.xml" id="remoteLogin" /> </webflow:flow-registry> <webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="viewFactoryCreator" /> <bean id="remoteLoginAction" class="org.jasig.cas.web.flow.RemoteLoginAction" p:argumentExtractors-ref="argumentExtractors" p:warnCookieGenerator-ref="warnCookieGenerator" p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator" /> <bean id="remoteLogoutController" class="org.springframework.webflow.mvc.servlet.FlowController"> <property name="flowExecutor" ref="remoteLogoutFlowExecutor" /> <property name="flowUrlHandler" ref="flowUrlHandler" /> </bean>
5.新建一个文件与login-webflow.xml同级,remoteLogin-webflow.xml:
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd" start-state="remoteLogin"> <!-- <on-start> <evaluate expression="remoteLoginAction.doBind(flowRequestContext, flowScope.credentials)" /> </on-start> --> <var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" /> <!-- 远程登陆主要Action --> <action-state id="remoteLogin"> <evaluate expression="remoteLoginAction" /> <transition on="error" to="remoteCallbackView" /> <transition on="submit" to="bindAndValidate" /> <transition on="checkTicketGrantingTicket" to="ticketGrantingTicketExistsCheck" /> </action-state> <!-- 远程回调页面,主要以JavaScript的方式回传一些参数用 --> <end-state id="remoteCallbackView" view="remoteCallbackView" /> <action-state id="bindAndValidate"> <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" /> <transition on="success" to="submit" /> <transition on="error" to="remoteCallbackView" /> </action-state> <decision-state id="ticketGrantingTicketExistsCheck"> <if test="flowScope.ticketGrantingTicketId != null" then="hasServiceCheck" else="gatewayRequestCheck" /> </decision-state> <decision-state id="hasServiceCheck"> <if test="flowScope.service != null" then="generateServiceTicket" else="remoteCallbackView" /> </decision-state> <decision-state id="gatewayRequestCheck"> <if test="externalContext.requestParameterMap['gateway'] neq '' && externalContext.requestParameterMap['gateway'] neq null && flowScope.service neq null" then="redirect" else="remoteCallbackView" /> </decision-state> <action-state id="generateServiceTicket"> <evaluate expression="generateServiceTicketAction" /> <transition on="success" to="warn" /> <transition on="error" to="remoteCallbackView" /> <transition on="gateway" to="redirect" /> </action-state> <decision-state id="warn"> <if test="flowScope.warnCookieValue" then="showWarningView" else="redirect" /> </decision-state> <action-state id="submit"> <evaluate expression="authenticationViaFormAction.submit(flowRequestContext, messageContext)" /> <transition on="warn" to="warn" /> <transition on="success" to="sendTicketGrantingTicket" /> <transition on="error" to="remoteCallbackView" /> </action-state> <action-state id="sendTicketGrantingTicket"> <evaluate expression="sendTicketGrantingTicketAction" /> <transition to="serviceCheck" /> </action-state> <decision-state id="serviceCheck"> <if test="flowScope.service neq null" then="generateServiceTicket" else="remoteCallbackView" /> </decision-state> <end-state id="showWarningView" view="casLoginConfirmView" /> <!-- <end-state id="redirect" view="bean:dynamicRedirectViewSelector" /> --> <action-state id="redirect"> <evaluate expression="flowScope.service.getResponse(requestScope.serviceTicketId)" result-type="org.jasig.cas.authentication.principal.Response" result="requestScope.response" /> <transition to="postRedirectDecision" /> </action-state> <decision-state id="postRedirectDecision"> <if test="requestScope.response.responseType.name() eq 'POST'" then="postView" else="redirectView" /> </decision-state> <!-- <decision-state id="hashServiceUrl"> <if test="flowScope.serviceUrl neq null" then="redirectServiceView" else="redirectView"/> </decision-state> <end-state id="redirectServiceView" view="externalRedirect:${flowScope.serviceUrl}" /> --> <end-state id="postView" view="postResponseView"> <on-entry> <set name="requestScope.parameters" value="requestScope.response.attributes" /> <set name="requestScope.originalUrl" value="flowScope.service.id" /> </on-entry> </end-state> <end-state id="redirectView" view="externalRedirect:${requestScope.response.url}" /> <end-state id="viewServiceErrorView" view="viewServiceErrorView" /> <end-state id="viewServiceSsoErrorView" view="viewServiceSsoErrorView" /> <global-transitions> <transition to="viewServiceErrorView" on-exception="org.springframework.webflow.execution.repository.NoSuchFlowExecutionException" /> <transition to="viewServiceSsoErrorView" on-exception="org.jasig.cas.services.UnauthorizedSsoServiceException" /> <transition to="viewServiceErrorView" on-exception="org.jasig.cas.services.UnauthorizedServiceException" /> </global-transitions> </flow>
6.加上一个回调视图配置,在default_views.properties中新增以下两句:
### 配置远程回调页面
remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlView
remoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp
其它不变
7.加上回调页面jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%-- <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> --%> <script type="text/javascript"> var remoteUrl = "${remoteLoginUrl}?validated=true"; // 构造错误消息,从webflow scope中取出 var errorMessage = '${remoteLoginMessage}'; /* <spring:hasBindErrors name="credentials"> errorMessage = "&errorMessage=" + encodeURIComponent('<c:forEach var="error" items="${errors.allErrors}"><spring:message code="${error.code}" text="${error.defaultMessage}" /></c:forEach>'); </spring:hasBindErrors> */ // 如果存在错误消息则追加到 url中 if(null != errorMessage && errorMessage.length > 0) { errorMessage = "&errorMessage=" + encodeURIComponent(errorMessage); } // 构造service var service = ""; <c:if test="${service != null && service != ''}"> service = "&service=" + encodeURIComponent("${service}"); </c:if> // 跳转回去(客户端) window.location.href = remoteUrl + errorMessage + service; </script>
完结...
请关注下一篇,shiro + cas sso实现客户端自定义登录界面完整实现
- trunk.rar (531.8 KB)
- 下载次数: 130
评论
5 楼
qzg196
2017-11-08
楼主啊 我用的4.0的服务器版。为什么配置好后 地址已经跳转了 但就是没进去 报500错误。服务器端处理我的remotelogin 时卡住了,检查了好几遍控制流程没有配错啊。
4 楼
月色无夜
2017-06-23
楼主你附带在项目中的证书导入密码是什么?
3 楼
zws_miss
2017-05-16
AuthenticationViaFormAction 这个类好多东西报错, 不知道怎么来的, 云里雾里!
2 楼
javabean96
2015-04-23
下一篇cas+shiro自定义界面有博文没
1 楼
游其是你
2014-12-18
楼主的这个例子挺不错的,请问能不能给一个MyEclipse的代码呢?上面的代码Server端在MyEclipse里导入不进来,谢谢!
发表评论
-
shiro + cas sso实现客户端自定义登录界面完整实现
2014-08-19 17:38 0前言:研究CAS SSO已经一个月了,说熟悉不是,说不熟悉也不 ... -
CAS-3.2.1客户端登陆页面存储到服务端----解决方案
2014-08-02 17:38 0上一篇介绍:客户端登录页面自定义([url] http://l ... -
linux下:apache2.2.27 + tomcat7.0 + openssl-1.0.1g(集群+session共享|摘要认证|HTTPS)
2014-06-04 15:24 4116前序:网上有很多资料都介绍了apache httpd + to ... -
Spring+CXF+IBatis详细介绍及下载
2014-05-21 17:53 4661今天开始使用JAVA WEB工 ...
相关推荐
赠送原API文档:cas-client-core-3.2.1-javadoc.jar; 赠送源代码:cas-client-core-3.2.1-sources.jar; 赠送Maven依赖信息文件:cas-client-core-3.2.1.pom; 包含翻译后的API文档:cas-client-core-3.2.1-javadoc...
cas-client-core-3.2.1
在这个压缩包中,包含的是CAS服务器端3.5.1版本和客户端3.2.1版本的代码和相关依赖的jar文件。 首先,我们来看`cas-server-3.5.1`部分。这是CAS服务器端的核心组件,负责处理用户的认证请求和响应。3.5.1版本可能...
"cas-client-3.2.1"是CAS客户端的一个特定版本,它允许用户通过一个统一的身份验证入口点登录到多个服务,而无需为每个服务单独输入凭证。 在CAS客户端3.2.1版本中,与2.1.1版本相比,配置过程确实可能更加复杂。这...
在这个压缩包文件中,"cas-client-3.2.1" 和 "cas-server-3.5.2" 分别代表CAS客户端和服务器的特定版本。 **CAS Server 3.5.2** CAS Server是整个SSO系统的核心,它负责验证用户的凭证并提供登录服务。版本3.5.2是...
标题提到的"cas-server-3.4.10-release"和"cas-client-3.2.1-release"是CAS协议的两个关键组件:服务器端和客户端。 **CAS服务器端(cas-server-3.4.10-release)** CAS服务器是整个系统的核心,它负责处理用户的...
在"cas-client-3.2.1-release.zip"中,可能包含了以下内容: - 客户端库文件:如JAR文件,供开发者集成到Java Web应用中。 - 示例配置:展示如何配置CAS客户端,包括web.xml的设置和CAS服务器地址的指定。 - 文档...
接下来是`cas-client-3.2.1-release`,它是CAS客户端的发布包,用于集成到各个应用系统中,实现对CAS服务器的调用。这个版本包含客户端库、配置示例和其他相关资源。客户端的主要功能有: 1. **票证验证**:客户端...
Gradle-3.2.1是Gradle的一个版本,该版本包含了所有必要的组件,可以在解压后直接使用,无需额外安装或配置,这对于那些网络环境不佳或者需要离线开发的用户来说非常方便。 Gradle 的核心特性之一是它的基于Groovy...
1. **CAS客户端**(cas-client-3.2.1-release.zip): CAS客户端是安装在各个应用服务器上的组件,它负责与CAS服务器通信,实现用户的身份验证。在这个版本中,CAS客户端3.2.1可能包含了以下特性: - 支持多种协议...
在"cas-server-3.4.11-release.zip"和"cas-client-3.2.1-release.zip"这两个压缩包中,你可以找到源码、文档、示例以及必要的配置文件,用于部署和定制CAS服务器和客户端。通过深入研究这些资源,你可以了解到如何...
赠送原API文档:commons-collections-3.2.1-javadoc.jar; 赠送源代码:commons-collections-3.2.1-sources.jar; 包含翻译后的API文档:commons-collections-3.2.1-javadoc-API文档-中文(简体)版.zip 对应Maven...
这两个资源包,`javase-3.2.1.jar` 和 `zxing-core-3.2.1.jar`,是开发Java应用程序时用于生成和解析一维码与二维码的关键组件。 `javase-3.2.1.jar` 是一个针对Java Standard Edition(Java SE)平台的特定版本库...
jquery-3.2.1
apache-maven-3.2.1apache-maven-3.2.1apache-maven-3.2.1apache-maven-3.2.1apache-maven-3.2.1apache-maven-3.2.1apache-maven-3.2.1
com.alibaba:hessian-lite:jar:3.2.1-fixed-2 hessian-lite hessian-lite-3.2.1-fixed-2.jar
赠送原API文档:commons-collections-3.2.1-javadoc.jar 赠送源代码:commons-collections-3.2.1-sources.jar 包含翻译后的API文档:commons-collections-3.2.1-javadoc-API文档-中文(简体)-英语-对照版.zip 对应...