刚刚接触spring webflow,相关的资料并非不少,而且大都是那种入门的 .xml文件的配置。html
用到的CAS 最新的4.0版本用的就是web-flow流,前台页面使用的是表单提交。因而我就碰到了一个问题,问题描述以下:前端
第一次输入错误的用户名密码提示用户名密码错误,第二次输入了正确的用户名和密码仍然报错,校验不经过。web
在使用cas以前的登陆页面若是输入用户名和密码错误的话,会弹出alert提示框,这个是使用ajax请求实现的。而ajax请求的通常都是那种spring mvc的controller,而且能够异步根据返回值回掉。可是cas的源码里面使用的是表单提交,表单提交不是异步的,因此除非加iframe,不然无法回调函数来刷新页面。而加了iframe以后也存在许多的改动,后续的工做量也不可评估。(为何必定要回调呢,由于cas的页面表单里不仅有用户名和密码输入框,还有个讨厌的隐藏元素:loginticket 和 flowExecutionKey,cas处理每次请求都须要从新生成一个 loginticket票据)。ajax
想过几种解决方案,刚开始都是想webflow和mvc整合,两种方式:spring
1. 将login-webflow.xml中的end-state标签中的externalRedirect修改为个人某个controller并在参数里面带上错误信息,controller返回一个对象给到前端(通常是code:-1;msg:"";date:****这样的json串的形式)。那么前端ajax调用login这个webflow的时候,就会重定向到我定义的这个controller,而且拿到这个controller的返回值进行回调。express
<end-state id="redirectView" view="externalRedirect:http://user/res?code=-1&msg="error"" />json
可是这样有一个问题,弹出alert框以后点击肯定,alert框消失了,再从新输入正确的用户名和密码,仍然会报错,缘由是loginticket验证失败。关于loginticket校验的,能够从cas的源码中看到:mvc
AuthenticationViaFormAction: public final Event submit(final RequestContext context, final Credential credential, final MessageContext messageContext) throws Exception { // Validate login ticket final String authoritativeLoginTicket = WebUtils.getLoginTicketFromFlowScope(context); final String providedLoginTicket = WebUtils.getLoginTicketFromRequest(context); if (!authoritativeLoginTicket.equals(providedLoginTicket)) { logger.warn("Invalid login ticket {}", providedLoginTicket); messageContext.addMessage(new MessageBuilder().code("error.invalid.loginticket").build()); return newEvent(ERROR); } ……
是去拿flow流中的lt和请求中的票据lt进行比较的。然而表单中的票据没有更新成最新的,个人webflow结束以后才返回的错误信息,那天然拿不到webflow里面生成的票据了。异步
先抛开这个问题不谈,说第二种整合mvc的方法:ide
在end-state标签中加入output,output中输出一个对像;
第三种:继承AbstractFlowHandler方法,
public class OcLoginFlowEndHandler extends AbstractFlowHandler { public String handleExecutionOutcome(FlowExecutionOutcome outcome, HttpServletRequest request, HttpServletResponse response) { if (outcome.getId().equals("redirectView")) { return "/user/loginRes?code=" + outcome.getOutput().get("bookingId"); } else { return "/hotels/index"; } } }
然而这都没有解决个人问题。由于出了那个webflow容器拿到的lt老是不对的。
那怎么在login-webflow这个闭环中返回错误信息给前台页面呢?
查到有一种方式:http://docs.spring.io/spring-webflow/docs/2.3.x/reference/htmlsingle/#end-state-element
使用一个viewstate,来展现popup 对话框:
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
不过这个加了这个viewstate以后也得从新定向到login的viewstate才行。
最后我采用的方案:
修改login的viewstate:
<view-state id="viewLoginForm" view="/platform/login" model="credential"> <binder> <binding property="username" /> <binding property="password" /> </binder> <on-entry> <set name="requestScope.messages" value="messageContext.allMessages" /> <set name="viewScope.commandName" value="'credential'" /> </on-entry> <transition on="submit" bind="true" validate="true" to="realSubmit"> <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credential)" /> </transition> </view-state>
添加绿色的那一行,这个view在render到velocity页面的时候,直接把requestScope里的messages给放进去,前端控制,若是messages为空,就不展现,若是不为空,就展现出来。
<label style="float: left; display: inline-block; font-size: 12px; color: red; text-decoration: none; margin: 5px 0;"> #foreach(${message} in ${messages} ) #if(${message.text} == "Invalid credentials.") 用户名、密码或验证码错误! #end #end </label>
done!
那么看下效果:
再输入正确的用户名和密码就不会报那个loginticket验证失败的错误而且能成功登陆到系统中去了。
不足的是,其实很明显的看到页面刷新了一下。