spring webflow - 结束流程和执行快照

时间:2011-08-04 02:13:02

标签: spring-webflow

我有这种安全要求,用户输入这样的网址

http://webserver.com/someapp/test/test-flow?roomId=12345

当输入该URL时,流程被创建,然后如果用户故意更改roomId参数,某些安全过滤器将检查用户是否有权访问该房间,如果有访问权限,则用户可以继续,但如果不是,则必须终止流程并且希望删除所有流快照(如果存在多个)。所以代码就像这样

从过滤器中提取:

public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;  
HttpServletResponse resp = (HttpServletResponse) response;
String roomId = req.getParameter("roomId");
if (roomId != null) {
    if (currentUserHasAccess(roomId)) {
    chain.doFilter(request, response);
    } else {
    flowExecutionManager.endFlow();
    return;
    }
}
chain.doFilter(request, response);
}

现在flowExecutionManager就像这样

public class FlowExecutionManager extends FlowExecutionListenerAdapter {
private RequestControlContext context;
private FlowDefinition definition;

@Override
public void sessionCreating(RequestContext context,
    FlowDefinition definition) {
super.sessionCreating(context, definition);
this.context = (RequestControlContext) context;
this.definition = definition;
}

public void endFlow() {
if (context != null && definition != null) {
    context.removeAllFlowExecutionSnapshots();
    context.endActiveFlowSession(definition.getId(), definition.getAttributes());
    Flow flow = (Flow)definition;
    flow.destroy();
}
}

在方法endFlow中,我尝试切换这些行的顺序

context.endActiveFlowSession(definition.getId(), definition.getAttributes());
context.removeAllFlowExecutionSnapshots();

并且无论这两行的顺序如何,我总是得到这样的NPE(仅显示堆栈跟踪的摘录)

java.lang.NullPointerException
at org.springframework.webflow.conversation.impl.SessionBindingConversationManager.getConversationContainer(SessionBindingConversationManager.java:140)
at org.springframework.webflow.conversation.impl.SessionBindingConversationManager.getConversation(SessionBindingConversationManager.java:116)
at org.springframework.webflow.execution.repository.support.AbstractFlowExecutionRepository.getConversation(AbstractFlowExecutionRepository.java:183)
at org.springframework.webflow.execution.repository.support.AbstractFlowExecutionRepository.getConversation(AbstractFlowExecutionRepository.java:170)
at org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository.removeAllFlowExecutionSnapshots(DefaultFlowExecutionRepository.java:156)
at org.springframework.webflow.engine.impl.FlowExecutionImpl.removeAllFlowExecutionSnapshots(FlowExecutionImpl.java:431)
at org.springframework.webflow.engine.impl.RequestControlContextImpl.removeAllFlowExecutionSnapshots(RequestControlContextImpl.java:230)
at com.ags.blackcorp.finances.web.FlowExecutionManager.endFlow(FlowExecutionManager.java:26)
at com.ags.blackcorp.finances.web.RoomFilter.doFilter(RoomFilter.java:100)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:378)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at com.ags.blackcorp.security.ui.webapp.AfterAuthenticationProcess.doFilterHttp(AfterAuthenticationProcess.java:55)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter.doFilterHttp(AbstractPreAuthenticatedProcessingFilter.java:69)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)

显然,行context.endActiveFlowSession(definition.getId(), definition.getAttributes());正在结束流程,但我无法删除执行快照。 任何想法我可能做错了,或任何想法如何删除执行快照。 关于最佳方法的任何想法。 提前谢谢你们。

2 个答案:

答案 0 :(得分:0)

试着回答我自己的问题,我得到了以下代码:

    public void endFlow() {
if (context != null && definition != null) {
    ExternalContextHolder.setExternalContext(contex.getExternalContext());  
    context.removeAllFlowExecutionSnapshots();
    context.endActiveFlowSession(definition.getId(), definition.getAttributes());
    Flow flow = (Flow)definition;
    flow.destroy();
   }
}

ExternalContextHolder ...行避免了NPE,并删除了快照,并且flowSession也被终止了,但是出现了新的问题

  • 为什么ExternalContextHolder是必需的?好吗?。

下面是一些奇怪的(或者是正常的?)行为即将到来

假设我已经在浏览器tab1中启动了流e1s1,在另一个选项卡中启动了e2s1,现在如果我在e2s1上并点击我的向导上的“下一步”按钮我得到e2s2(这是正确的),现在如果我删除执行快照属于e1s1,它们被删除没有问题,但如果去e2s2的选项卡,我点击'上一步'按钮,所以我可以返回到以前的快照,快照e2s1也消失了,我的意思是不应该删除快照就像是“执行”。我已经测试了新代码以删除流程执行(使用类FlowExecutionRepository中的方法removeFlowExecution)但是现在我不会显示它,而是等待有人可以抛出一些指针。无论如何,如果没有任何表现,那就是让任何人对循环感兴趣。

我再次回答我的问题,希望这是最后的答案。

问:为什么需要ExternalContextHolder?

Ans:根据我的经验,可能还有其他一些东西,所以Spring需要访问(HttpServletRequest和HttpServletResponse)来处理从谁做的请求发送的数据。

最后从过滤器中删除流程执行可能听起来不错,但是webflow给了我们一个更好的方法,我的意思是子类化FlowExecutionListenerAdapter,在这种情况下我们覆盖了方法“void requestSubmitted(RequestContext context)”,在这里我检查当前用户是否有权访问roomId,然后我将调用方法endFlow(参见下面的代码)

public void endFlow(ExternalContext externalContext) {  
FlowUrlHandler handler = flowController.getFlowUrlHandler();
HttpServletRequest request = (HttpServletRequest) externalContext.getNativeRequest();
String stringKey = handler.getFlowExecutionKey(request);
if (stringKey != null) {
    FlowExecutorImpl flowExecutor = (FlowExecutorImpl) flowController.getFlowExecutor();
    FlowExecutionRepository repository = flowExecutor.getExecutionRepository();
    FlowExecutionKey key = repository.parseFlowExecutionKey(stringKey);
    ExternalContextHolder.setExternalContext(externalContext);
    FlowExecutionLock lock = null;
    try{
    lock = repository.getLock(key);
    }catch(NoSuchFlowExecutionException nsfee){     
    return;
    }
    lock.lock();
    try {
    FlowExecution flowExecution = repository.getFlowExecution(key);
    repository.removeFlowExecution(flowExecution);
    } finally {
    lock.unlock();
    }
}
}

flowController(org.springframework.webflow.mvc.servlet.FlowController)由spring注入,并在我们的webflow配置文件中添加。 上面的代码完全删除了流程执行,如果用户试图返回上一个流程,让我们说e1s1然后webflow自动创建一个新的流程执行e2s1,就是这样。

如果你想在doFilter方法中使用过滤器方法,那么这个

    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;  
HttpServletResponse resp = (HttpServletResponse) response;
String roomId = req.getParameter("roomId");
ExternalContext externalContext = new ServletExternalContext(
    this.servletContext, req, resp);
if (roomId != null) {
    if (!currentUserHasAccess(roomId)) {
      flowExecutionManager.endFlow();
      return;
    } 
}
chain.doFilter(request, response);    

this.servletContext通过filterConfig.getServletContext()

获得

答案 1 :(得分:0)

对不起,我无法发表评论,因为我没有足够的分数这么做,但您对用例的实施似乎比应该更复杂。正如@Patrick在您的问题的评论部分所建议的,为什么您不能将roomId参数存储在flowScope var中?您可以在流文件中执行验证,也可以调用验证方法并为成功或失败的验证执行必要的转换。您不应该关心快照,因为那些是由框架处理的WebFlow工件...