- 浏览: 550058 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
jsdsh:
自己写的就是不一样.
Spring3 MVC 注解(二)---@RequestMapping -
jsdsh:
吼,非常吼.学习了
Spring3 MVC 注解(一)---注解基本配置及@controller和 @RequestMapping 常用解释 -
爱情是一种错觉:
我爱你 i love 你[color=red][/color] ...
Spring3 MVC 注解(一)---注解基本配置及@controller和 @RequestMapping 常用解释 -
fs08ab:
大哥,目前这个问题有什么可靠的解决方案吗
@ResponseBody注解 -
lhs295988029:
说的很清楚,明白了~
Spring3 MVC 注解(一)---注解基本配置及@controller和 @RequestMapping 常用解释
此方案主要是通过页面重定向来实现客户端独立登陆页面
基本原理
首先,在我们修改之间,先了解以下CAS运行基本原理。CAS服务器,客户端(应用),浏览器的序列图如下:
其中:
- ST:Service Ticket,用于客户端应用持有,每个ST对应一个用户在一个客户端上
- TGT:Ticket Granting Ticket,存储在CAS服务器端和用户cookie两个地方
CAS服务器持有ST与TGT+客户端的映射关系,客户端持有ST与用户Session的映射关系,在renew的情况下,每次客户端根据用户 Session将ST发送给CAS服务器端,服务器端检验ST是否存在即可知道此用户是否已登陆。在普通情况下,用户第一次登陆应用时,客户端将用户页面 重定向到CAS服务器,服务器取出用户cookie中的TGT,检验是否在服务器中存在,若存在则生成ST返回给客户端 (若不存在则要求登陆,登陆成功后同样返回ST给客户端),客户端拿到ST后再发送给CAS服务器认证是否为真实ST,认证成功即表示登陆成功
我们可以看到,其实我们需要做的就是第2步中返回的登陆页面由服务器改放到客户端,然后让第3步中由用户在客户端上输入用户名密码但提交到CAS服务器端,登陆成功与失败都将转向客户端。
服务器详细登陆流程
对于上一节讲述的整体登陆流程,CAS 3.3.1服务器端上是依赖于Spring Webflow 1.0.3实现的,其主要流程在/WEB-INF/login-webflow.xml中配置,配置的页面流活动图如下(有删节):
图中命名均按照webflow配置文件中的命名,图解如下:
- Action State图标表示webflow配置文件中的action-state或view-state节点
- Decision图标表示webflow配置文件中的decision-state节点
- Initial State图标表示webflow配置文件中的start-state节点
- Final State图标表示webflow配置文件中的end-state节点
登陆的流程依照图上说明,在此不再累述,下面简单说明下CAS服务器端Spring Webflow的运作
首先CAS在/WEB-INF/web.xml中配置命名为cas的servlet以拦截输入请求,若不在cas servlet mapping范围内的资源路径请求均转向到/login上:
<servlet> <servlet-name>cas</servlet-name> <servlet-class>org.jasig.cas.web.init.SafeDispatcherServlet</servlet-class> <init-param> <param-name>publishContext</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>cas</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> ... <!-- 其他cas servlet mapping -->
所有映射到cas servlet上的请求都将经过/WEB-INF/cas-servlet.xml检查确定进入哪个Action,cas-servlet.xml中最重要的两个bean就是handlerMappingB和handlerMappingC
handlerMappingB配置了登陆流程进入的路径映射,而handlerMappingC则配置了其他的流程的路径映射。/WEB-INF/login-webflow.xml流程配置文件即是在handlerMappingB中通过/login映射进入的。
Webflow依据一个生成的flowExecutionKey来确定一个流程实例走到了哪一步,每次页面流程运转总是需要提交这个 flowExecutionKey来告诉webflow它是从流程的哪个位置出发的有了以上理论作为依据,我们在下一节就可以根据自己的需要修改流程,使 之支持远程登录了
服务器登陆流程修改目标
修改后的登陆流程活动图如下:
图中橙色为我们修改的流程节点,这里我们增加了一个开始节点remoteLogin和一个结束节点
remoteCallbackView,删除了原有的loginFormView节点、 viewGenericLoginSuccess以及
renew节点(renew节点由于系统无此需求而删除),然后将所有这些节点的转向全部都转向
到remoteCallbackView节点,因为登陆和显示登陆成功信息都应该是客户端完成的
=============================服务器================================
修改需要基于几个基本原则:
- 不影响原有统一登陆界面功能
- 客户端应尽量保持简单
- 尽量保证原有功能的完整性和安全性
对于第三点,必须事先说明:将登陆页面放到客户端本身就是降低了CAS安全性,这意味着作为服务向外发布的CAS服务器中的用户密码有可能由于客户端的不安全性而导致泄露,整个CAS系统成为了一个“水桶形态”,整个CAS体系的安全性将取决于所有客户端中安全性最低的一个 。这也是CAS官方一直不推荐的方式。
接下来我们讲解服务器端修改的详细过程:
首先,修改/WEB-INF/web.xml,为cas增加一个/remoteLogin的映射:
<servlet-mapping>
<servlet-name>cas</servlet-name>
<url-pattern>/remoteLogin
</url-pattern>
</servlet-mapping>
然后修改cas-servlet.xml文件,增加我们对/remoteLogin映射的处理,需要增加一个新流程:
<bean id="handlerMappingB" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/login">loginController</prop>
<prop key="/remoteLogin">remoteLoginController</prop>
</props>
</property>
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
然后在cas-servlet.xml文件中添加我们上面所配置的remoteController的bean:
<!-- 增加远程控制者,允许以/remote请求启动remote控制流程 -->
<bean id="remoteLoginController"
class="org.springframework.webflow.executor.mvc.FlowController"
p:flowExecutor-ref="remoteLoginFlowExecutor"
p:defaultFlowId="remoteLogin-webflow">
<property name="argumentHandler">
<bean
class="org.springframework.webflow.executor.support.RequestParameterFlowExecutorArgumentHandler"
p:flowExecutionKeyArgumentName="lt"
p:defaultFlowId="remoteLogin-webflow" />
</property>
</bean>
<flow:executor id="remoteLoginFlowExecutor" registry-ref="remoteLoginFlowRegistry">
<flow:execution-attributes>
<flow:alwaysRedirectOnPause value="false"/>
</flow:execution-attributes>
</flow:executor>
<flow:registry id="remoteLoginFlowRegistry">
<flow:location path="/WEB-INF/remoteLogin-webflow.xml
"/>
</flow:registry>
可以看到上面将请求指向了webflow配置文件/WEB-INF/remoteLogin-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-1.0.xsd"> <start-state idref="remoteLogin"/> <!-- 远程登陆主要Action --> <action-state id="remoteLogin"> <action bean="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" /> <decision-state id="ticketGrantingTicketExistsCheck"> <if test="${flowScope.ticketGrantingTicketId != null}" then="hasServiceCheck" else="gatewayRequestCheck" /> </decision-state> <decision-state id="gatewayRequestCheck"> <if test="${externalContext.requestParameterMap['gateway'] != ''&& externalContext.requestParameterMap['gateway'] != null && flowScope.service != null}" then="redirect" else="remoteCallbackView " /> </decision-state> <decision-state id="hasServiceCheck"> <if test="${flowScope.service != null}" then="generateServiceTicket" else="remoteCallbackView " /> </decision-state> <!-- The "warn" action makes the determination of whether to redirect directly to the requested service or display the "confirmation" page to go back to the server. --> <decision-state id="warn"> <if test="${flowScope.warnCookieValue}" then="showWarningView" else="redirect" /> </decision-state> <action-state id="bindAndValidate"> <action bean="authenticationViaFormAction" /> <transition on="success" to="submit" /> <transition on="error" to="remoteCallbackView " /> </action-state> <action-state id="submit"> <action bean="authenticationViaFormAction" method="submit" /> <transition on="warn" to="warn" /> <transition on="success" to="sendTicketGrantingTicket" /> <transition on="error" to="remoteCallbackView " /> </action-state> <action-state id="sendTicketGrantingTicket"> <action bean="sendTicketGrantingTicketAction" /> <transition on="success" to="serviceCheck" /> </action-state> <decision-state id="serviceCheck"> <if test="${flowScope.service != null}" then="generateServiceTicket" else="remoteCallbackView " /> </decision-state> <action-state id="generateServiceTicket"> <action bean="generateServiceTicketAction" /> <transition on="success" to ="warn" /> <transition on="error" to="remoteCallbackView" /> <transition on="gateway" to="redirect" /> </action-state> <!-- The "showWarningView" end state is the end state for when the user has requested privacy settings (to be "warned") to be turned on. It delegates to a view defines in default_views.properties that display the "Please click here to go to the service." message. --> <end-state id="showWarningView" view="casLoginConfirmView" /> <!-- The "redirect" end state allows CAS to properly end the workflow while still redirecting the user back to the service required. --> <end-state id="redirect" view="bean:dynamicRedirectViewSelector" /> <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>
以上文件根据原login-webflow.xml文件修改,粗体为修改部分。可以看到,我们在流程中增加了remoteLogin Action节点和remoteCallback View节点,下面我们配置remoteLogin节点:
在/WEB-INF/cas-servlet.xml文件中增加remoteLoginAction配置:
<bean id="remoteLoginAction"
class="com.baidu.cas.web.flow.RemoteLoginAction
"
p:argumentExtractors-ref="argumentExtractors"
p:warnCookieGenerator-ref="warnCookieGenerator"
p:ticketGrantingTicketCookieGenerator-ref="ticketGrantingTicketCookieGenerator" />
同时创建com.baidu.cas.web.flow.RemoteLoginAction类:
/**
* 远程登陆票据提供Action.
* 根据InitialFlowSetupAction修改.
* 由于InitialFlowSetupAction为final类,因此只能将代码复制过来再进行修改.
*
* @author GuoLin
*/
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("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;
}
}
以上粗体为修改部分,要求客户端必须传入loginUrl参数,且当客户端传入submit参数时,直接为其提交用户名密码
然后再配置remoteCallbackView显示节点,修改src/default_views.properties文件,增加remoteCallbackView配置:
### 配置远程回调页面 remoteCallbackView.(class)=org.springframework.web.servlet.view.JstlView remoteCallbackView.url=/WEB-INF/view/jsp/default/ui/remoteCallbackView.jsp
创建/WEB-INF/view/jsp/default/ui/remoteCallbackView.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"%> <html> <head> <script type="text/javascript"> var remoteUrl = "${remoteLoginUrl}?validated=true"; // 构造错误消息 var errorMessage = ""; <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> // 构造service var service = ""; <c:if test="${service != null && service != ''}"> service = "&service=" + encodeURIComponent("${service}"); </c:if> // 跳转回去 window.location.href = remoteUrl + errorMessage + service; </script> </head> <body> ${remoteLoginMessage} </body> </html>
以上文件注意粗体部分validated=true,这里我们与客户端约定,当客户端登陆页面后带有参数validated=true时,不进行票据认证请求。这是因为,客户端登陆页面为http://clienthost/login.jsp ,那么当用户访问URL http://clienthost/login.jsp 时, 客户端会重定向到CAS中央服务器请求TGT认证,但认证失败后CAS中央认证服务器会重定向到客户端登陆页面并显示登陆框,此时客户端必须以某种规则避 免重新请求中央认证服务器认证, 在这里我们与客户端约定,当回发的请求为登陆页面且带有参数validated=true时即不转发TGT认证请求,即 http://clienthost/login.jsp?validated=true 请求客户端不会重新发送TGT认证请求给中央认证服务器
==================================================客户端===============================================
客户端实现目标
客户端实现主要需要满足5个case:
- 1. 用户未在中央认证服务器登陆,访问客户端受保护资源时,客户端重定向到中央认证服务器请求TGT认证,认证失败,转回客户端登陆页面,保证受保护资源URL信息不丢失
- 2. 用户未在中央认证服务器登陆,访问客户端登陆页面时,客户端重定向到中央认证服务器请求TGT认证,认证失败,转回客户端登陆页面,此次登录页面不再受保护,允许访问
- 3. 用户已在中央认证服务器登陆,访问客户端受保护资源时,客户端重定向到中央认证服务器请求TGT认证,认证成功,直接转回受保护资源
- 4. 用户在客户端登陆页面提交用户名密码,客户端将用户名密码信息提交给服务器端,认证失败,转回客户端登陆页面,携带失败信息并保证转到登陆页面前受保护资源URL信息不丢失
- 5. 用户在客户端登陆页面提交用户名密码,客户端将用户名密码信息提交给服务器端,认证成功,转回转到登陆页面前受保护资源
对于case 1和case 3,普通的CAS客户端即可满足需求,但对于case 4和case 5,则需要我们定制自己的登陆页面。对于case 2,主要是需要满足部分登陆页面希望在用户未登陆状态显示登陆框,在已登陆状态显示用户欢迎信息的需求,实现这个需求我们是通过让CAS客户端认证器满足 一个排除约定,即当用户请求路径为登陆页面且带有validated=true的参数时,即不进行重定向TGT认证请求
客户端修改方案
远程客户端修改,对于任何一种客户端方案都可以实现,这里为了简单起见,我们给出的修改方案基于CAS官方提供的Java客户端3.1.3。首先我们使用CAS Client 3.1.3搭建一个CAS客户端,具体搭建方法可以参考CAS官网:CAS Client for Java 3.1
根据服务器流程修改方案,我们可以知道,所有的远程请求都必须携带有loginUrl参数信息以使得服务器端知道在认证失败后转向客户端登陆页面。 而在CAS客户端上,上一节的case 4和case 5,我们主要通过提交表单的方式传递loginUrl,而case 1, case 3则是依靠org.jasig.cas.client.authentication.AuthenticationFilter类进行的转向,但使用 AuthenticationFilter转向时,是没有loginUrl信息的,因此我们首先需要重新实现一个自己的认证过滤器,以下是我们自己的认证 过滤器的代码:
/** * 远程认证过滤器. * 由于AuthenticationFilter的doFilter方法被声明为final, * 只好重新实现一个认证过滤器,支持localLoginUrl设置. * * @author GuoLin * */ 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; } }
以上粗体代码为修改部分,其余代码均拷贝自 org.jasig.cas.client.authentication.AuthenticationFilter,可以看到我们为原有的认证过滤器 增加了一个参数localLoginUrl。在WEB-INF/web.xml中配置:
<filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>com.baidu.cas.client.validation.RemoteAuthenticationFilter</filter- class> <init-param> <param-name>localLoginUrl</param-name> <param-value>http://GUOLIN:9080/cas-client-java-custom-login/login.jsp </param-value> </init-param> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://GUOLIN/cas-server/remoteLogin</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://GUOLIN:9080</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
此处我们将过滤器指向自己的过滤器并增加本地登陆页面路径设置。最后我们来看看登陆页面login.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>远程CAS客户端登陆页面</title> <link rel="stylesheet" type="text/css" href="<%= request.getContextPath() %>/styles/main.css" /> <script type="text/javascript"> function getParam(name) { var queryString = window.location.search; var param = queryString.substr(1, query.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>远程CAS客户端登陆页面</h1> <% if (request.getRemoteUser() == null) { %> <div id="errorMessage"></div> <form id="myLoginForm" action="https://guolin/cas-server/remoteLogin" method="post"> <input type="hidden" id="service" name="service" value="" > <input type="hidden" name="loginUrl" value="http://guolin:9080/cas-client-java-custom-login/login.jsp"> <input type="hidden" name="submit" value="true" /> <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> <% } else { %> <div class="welcome">您好:<%= request.getRemoteUser() %></div> <div id="logout"> <a href="https://GUOLIN/cas-server/remoteLogout?service=http://guolin:9080/cas-client-java-custom-login/login.jsp">单点登出</a> </div> <% } %> </body> </html>
ogin.jsp主要是为了满足上一节中的case 4和case 5,这里为了简单起见,仅有是否已登陆判断使用了服务器端代码,其他均使用客户端代码实现。以上粗体字中,我们首先将表单action指向服务器端 remoteLogin,然后在里面设置了两个重要的hidden域以传递 loginUrl和submit参数,前者用于告诉服务器失败后转向何处,后者告诉服务器端webflow现在要进行提交而不是TGT认证请求
发表评论
-
用户登录之webflow配置详解(二)
2011-10-31 11:35 12257Webflow配置文件分析 在WEB-INF文件夹下 ... -
用户登录之webflow配置详解(一)
2011-10-31 11:34 5579该部分主要是讲解在CAS server中登录流程的定义以及相关 ... -
使用 CAS 在 Tomcat 中实现单点登录
2011-09-08 16:08 1026单点登录(Single Sign On , 简称 SSO )是 ... -
CAS单点登录(SSO)完整教程
2011-09-06 16:22 2162一、教程前言 教程目的:从头到尾细细道来单点登录 服 ... -
CAS单点登陆原理
2011-09-06 15:03 5328CAS 的基本原理 ...
相关推荐
"CAS支持客户端自定义登陆页面——服务器篇" 为了让CAS支持客户端自定义登陆页面,需要在服务器端进行修改。首先,需要在/WEB-INF/web.xml文件中增加一个/remoteLogin的映射,以便将请求指向新的登陆页面。其次,...
标题中的“让CAS支持客户端自定义登陆页面——客户端篇”表明了本文主要探讨的是如何在中央认证服务(Central Authentication Service, CAS)系统中,允许客户端应用程序实现自定义登录页面的配置与实现。...
让CAS支持客户端自定义登陆页面----服务器篇--.doc
让CAS支持客户端自定义登陆页面----服务器篇.doc
【让CAS支持客户端自定义登陆页面——服务器篇】 CAS(Central Authentication Service)是一个开源的身份验证框架,它允许用户通过单一登录(Single Sign-On, SSO)访问多个应用系统。在某些场景下,用户可能希望...
### 让CAS支持客户端自定义登录页面——服务器篇 #### 概述 本文档主要介绍如何在CAS(Central Authentication Service)系统中实现客户端自定义登录页面的功能,并且着重讲解服务器端的修改步骤与注意事项。CAS...
本文主要介绍如何让CAS(Central Authentication Service)支持客户端自定义登录页面,主要集中在服务器端的修改。CAS是一种开源的身份验证框架,它提供单点登录(SSO)功能,通常用于集中管理多个应用系统的用户...
在实际的企业环境中,根据业务需求,我们可能需要对默认的CAS登录页面进行自定义,以提供更符合品牌形象或用户体验的界面。下面将详细讲解如何配置和实现CAS的自定义登录页面。 一、CAS自定义登录页面概述 CAS的...
1. **安装CAS客户端库**:通常,我们可以通过NuGet包管理器安装`Castle.Services.Ticket`或`CasClient2`等CAS客户端库。 2. **配置web.config**:在项目的web.config文件中添加相应的CAS配置节。这包括设置CAS...
4. **自定义登录界面**:如果你想自定义客户端的登录界面,可以在应用中创建一个定制的登录页面,然后配置CAS客户端库以使用该页面。你需要确保页面能够收集用户的凭证(通常是用户名和密码)并提交到CAS服务器进行...
在本项目中,Pac4j作为CAS客户端的实现,负责处理与CAS服务端的交互: 1. SSO处理:Pac4j帮助客户端应用与CAS服务端建立SSO连接,处理登录和登出请求。 2. 用户信息提取:成功认证后,Pac4j可以从CAS服务端获取用户...
- 在CAS服务器的配置文件(如`cas.properties`或`application.yml`)中,可能需要更新登录页面路径或启用自定义主题支持。 3. **客户端集成**: - CAS客户端通常是一个库,集成到需要SSO的应用中,如Spring ...
在IT行业中,集成Spring、Spring Web MVC和CAS客户端是一项常见的任务,主要用于构建安全的Web应用程序。下面将详细解释这些技术及其整合过程。 首先,Spring是一个开源的Java框架,它简化了开发过程,提供了依赖...
标题中的“自定义客户端登录CAS服务器-iframe实现”是指一种基于CAS(Central Authentication Service)的认证方式,它利用iframe在不离开当前页面的情况下实现用户的单点登录(Single Sign-On,SSO)。CAS是一种...
6. **单点登出(Single Sign-Out, SSO)**: CAS客户端还支持单点登出功能,当用户在CAS服务器上注销时,客户端应用也能同步注销用户。 7. **测试与调试**: 集成后,务必进行充分的测试,确保CAS客户端与你的应用...