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

发布于 2024-11-28 07:39:07 字数 5978 浏览 0 评论 0原文

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

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(仅显示stacktrace)

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()); 正在结束流程,但我无法删除执行快照。 任何想法我可能做错了什么,或者任何想法如何删除执行快照。 关于最佳方法的任何想法。 预先感谢大家。

I have this sort of security requirement where the user enters a url like this

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

when entering that url the flow is created and then if user deliberately changes roomId parameter some security filter will check if user has access to that room in particular, if it has access user can proceed, but if not the flow must be terminated and it is desirable to remove all flow snapshots(if several exist). So the code is like this

Extract from filter:

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);
}

Now flowExecutionManager is like this

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();
}
}

In method endFlow i've tried switching the order of these lines

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

and no matter the order of those 2 lines i always get a NPE like this (showing just an extract of stacktrace)

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)

Apparently the line context.endActiveFlowSession(definition.getId(), definition.getAttributes()); is ending the flow but i cant remove execution snapshots.
Any ideas what i might be doing wrong, or any idea how to remove execution snapshots.
Any idea regarding a best approach.
Thank u all in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

猫卆 2024-12-05 07:39:07

试图回答我自己的问题导致我看到以下代码:

    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是必要的?可以吗?

下面是一些奇怪的(或者正常吗?)行为

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

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

问:为什么需要ExternalContextHolder?

Ans:根据我的一点经验,可能需要使用ExternalContextHolder,以便spring能够访问(HttpServletRequest和HttpServletResponse)从执行请求的人发送的数据。

最后,从过滤器中删除 flowexecution 可能听起来是个好主意,但 webflow 为我们提供了更好的方法,我的意思是子类化 FlowExecutionListenerAdapter ,在这种情况下,我们覆盖方法“void requestSubscribed(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 注入并将其添加到我们的网络流配置文件。
上面的代码完全删除了流程执行,如果用户尝试返回到之前的流程,假设 e1s1,那么 webflow 会自动创建一个新的流程执行 e2s1,仅此而已。

如果你想使用过滤器方法,你需要在 doFilter 方法中使用 this.servletContext ,

    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);    

它是通过 filterConfig.getServletContext() 获得的

Trying to answer my own question led me to the following piece of code:

    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();
   }
}

The ExternalContextHolder... line avoids the NPE, and snapshots are removed and flowSession its also terminated, Yet new questions have arisen

  • why is ExternalContextHolder necessary? is it ok?.

Below is some weird(or is it normal?) behaviour im getting

Suppose i've started a flow e1s1 in browser tab1 and also e2s1 in another tab, now if im on e2s1 and click 'next' button on my wizard i get e2s2(this is right), now if i remove execution snapshots that belong to e1s1, they are removed without problems, but if go to tab where e2s2 is and i click 'previous' button so i can return to previous snapshot, snapshot e2s1 is also gone, i mean shouldnt snapshot removal be like something "per execution". I have tested new code to remove flow execution (using method removeFlowExecution from class FlowExecutionRepository) but right now i wont show it, instead ill wait if someone can throw some pointers. Anyway if nothing shows up ill be keeping anyone interested in the loop.

Once again im answering my question, hopefully this is the last answer.

Q: why is ExternalContextHolder necessary?

Ans: according to my little experience ExternalContextHolder probably among other things, is needed so spring has access(HttpServletRequest and HttpServletResponse) to the data sent from whoever is doing the request.

In the end removing flowexecution from a filter might sound like a good idea, but webflow gives us a better approach yet, i mean subclassing FlowExecutionListenerAdapter and in this case we i overrode method "void requestSubmitted(RequestContext context)" and here i check wheter or not current user has access to roomId and then i will call method endFlow(see code below)

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) is injected by spring and its added in our webflow config file.
The above code removes flow Execution completely and if user tries to return to previous flow, lets say e1s1 then webflow automatically creates a new flow execution e2s1, and thats it.

In case u want to use the filter approach all u need in doFilter method its this

    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 its obtained through filterConfig.getServletContext()

尬尬 2024-12-05 07:39:07

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

I'm sorry, I can't comment because I don't have enough points to do so, but your implementation to your use case seems to be way more complicated than it should be. As suggested by @Patrick in the comment section of your question, why couldn't you just store the roomId argument in a flowScope var? You could perform your validation in the flow file or invoke a validation method and perform necessary transitions for successful or failed validations. You shouldn't have to care about snapshots, as those are WebFlow artifacts meant to be handled by the framework...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文