开发者

spring webflow - end flow and execution snapshots

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.springframewor开发者_C百科k.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.


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


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...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜