Background
A user must submit report parameters to the server. The server redirects the user to a URL. That URL runs Oracle Reports to produce a PDF (or web page).
The system uses a relatively slow authentication mechanism. But I'm not looking to restart the web flow, so the authentication should not otherwise interfere with the redirect.
JSP
The "Run Report" button is coded as follows:
<button type="submit" id="run" name="_eventId_run">
<fmt:message key="form.button.report.run" />
</button>
The rest of the page binds the parameters to a map in the DAO, such as:
<form:radiobutton path="parameters['service']" class="service" value="..." />
Submitting the form shows that the bind variables are set in the DAO map correctly. Further, the report is able to generate the URL used for redirection.
The form itself resembles:
<form:form modelAttribute="report" action="${flowExecutionUrl}"
method="post">
<fieldset>
<%-- secret tokens --%>
<tiles:insertAttribute name="hidden" />
<%-- includes the requested report form parameters --%>
<jsp:include page="${reportKey}.jsp" />
<%-- includes the aforementioned submit button %-->
<tiles:insertAttribute name="reportButtons" />
</fieldset>
</form:form>
Flow
The flow has three view states: list reports, enter parameters, and run report. The last two are pertinent:
<view-state id="parameters" model="report" view="flow/reports/parameters">
<transition on="run" to="redirect">
<evaluate expression="reportService.run(report)" result="flowScope.url" />
</transition>
</view-state>
<view-state id="redirect" view="externalRedirect:#{flowScope.url}"/>
The reportService.run(report) method is being called. The report parameters are being bound. The return result is, indeed, the correct URL.
Java
The report service itself is reasonably trivial:
public String run(Report report) {
Map<String, String> parameters = report.getParameters();
String url = getConfigurationValue("ReportUrl");
for (String key : parameters.keySet()) {
info("Report Parameter: {} = {}", key, parameters.get(key));
}
return url;
}
Again, the correct url is being returned. There is no controller class. The Report DAO could hardly be simpler:
public class Report extends DAOBase implements Serializable {
/** Values to pass into the report. */
private Map<String, String> parameters;
public Report() {
setParameters(createParameters());
}
// standard accessors and serial version not shown
}
Problem
It appears that the application is performing a POST-Redirect-GET (PRG). The GET does not direct the browser to the URL set by flowScope.url. (I have not verified that flowScope.url contains valid data.) When the POST operation completes, the application is redirected to the parameter flow, rather than the run button redirecting to the redirect flow.
From the schema definition, everything seems correct.
Questions
Using Spring 4.1.2, what needs to change so that the externalRedirect sends the browser to the URL returned by reportService.run(...)?
Does the form need to supply a different value for flowExecutionUrl?
Attempts
Here are some of the various changes that have been made to no avail:
Use $ in the EL, instead of # (i.e., externalRedirect:${flowScope.url})
Use <end-state id="redirect" view="externalRedirect:#{flowScope.url}"/>
Use <view-state id="redirect" view="externalRedirect:http://google.com"/>
Use form:button instead of button
Changing the redirect view-state and removing the expression evaluation causes the redirect to fire. For example:
<transition on="run" to="redirect">
<!-- <evaluate expression="reportService.run(report)" result="flowScope.url" /> -->
</transition>
...
<view-state id="redirect" view="externalRedirect:http://google.com"/>
This, of course, means that the report parameters are never used, which won't work.
Remove the evaluation from the transition:
<transition on="run" to="redirect"/>
Call the run method within an EL statement to generate the URL:
<view-state id="redirect" view="externalRedirect:#{reportService.run(report)}"/>
Related
I have a use case where I need to redirect to an external URL via spring webflow, the url might change every time the user clicks on the link. I am trying to use externalRedirect within the flow.xml and passing in the url in a localAttributeMap and returning it with the event.
The summary.xhtml code looks something like this.
<h:commandLink action="redirect" target="_blank">
Redirect to new page
</h:commandLink>
The flow.xml is something like this:
<view-state id="summary" view="/flows/forms/summary.xhtml">
<transition on="redirect" to="retrieveUri" />
</view-state>
<action-state id="retrieveUri">
<evaluate expression="redirectAction.execute(requestContext, formContext)" />
<transition on="successRedirect" to="externalView" />
</action-state>
<view-state id="externalView" view="externalRedirect:#{currentEvent.attributes.redirectUrl}">
</view-state>
On the code base I am using I am forced to only use Actions which extend MultiAction and I can't use any other service have return types like String. My action class with the execute method which returns an Event looks something like below:
public Event execute(RequestContext context, FormContext formContext) {
LocalAttributeMap lam = new LocalAttributeMap() ;
lam.put("redirectUrl", "https://google.com") ;
//Above would of course be replaced with some service layer call but I need to redirect to this url and that too via classes extending MultiAction.
return new Event(this, " successRedirect", lam) ;
}
However this does not seem to work for me and I am getting the below errors:
Caused by: org.springframework.binding.expression.EvaluationExpression : An ElException occured getting the value for expression 'currentEvent.attributes.redirectUrl' on context [class org.springftamework.webflow.engine.impl.RequestControlContextImpl]
at org.springframework.binding.expression.spel.SpringElExpression.getValue(SpringElExpression.java:92)
at org.springframework.webflow.action.ExternalRedirectAction.doExecute(ExternalRedirectAction.java:42)
Caused by: org.springframework.expression.spel.SpelEvaluationExpression : EL1007E:(pos 13): Property or field 'attributes' cannot be found on null
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:208)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:85)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:78)
Being new to spring webflow, I am not sure of the issue and would appreciate some help in understanding the issue and whether is the correct way of using this.
The version of spring-webflow is 2.3.0.RELEASE within the project
Bit of a background:
We have an existing spring webflow that we want to have ajaxified so that the page can be displayed
in a "lightbox" (at a different URL) where the user can interact with the flow in a similar way to the full existing page.
The normal registration form sits at
http://localhost:8080/csso/customer/registration?execution=e1s1.
(csso is the application name)
The webflow in Spring Webflow has an id of /customer/registration.
<flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<flow-location id="customer/registration" path="/WEB-INF/views/customer/registration/registration-flow.xml"/>
</flow-registry>
registration-flow.xml
<view-state id="create" model="customer" view="customer/registration/create">
<on-render>
<evaluate expression="customer.setAcceptTermsAndConditions(false)"/>
</on-render>
<transition on="submit" to="confirm" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
Now my problem is I need to access this page in two different ways.
Directly. Which works currently.
As an include in a different JSP page
(tabEntry.jsp accessed with controller /tabEntry) in such a way that the flow of the form is not interrupted.
How can I include /customer/registration inside tabEntry in such a way that clicking on submit goes through the same flow?
Problems faced:
Including /customer/registration by jsp:include doesn't work since /customer/registration is not a controller. Also #include doesn't work since a call to /customer/redirection includes a redirect which sets the execution key for webflow
The form action is automatically set to /tabEntry but it should be set to the second state of /customer/registation to continue with the flow.
Happy to accept design changes instead of hammering it technically.
I am using Spring 3.x and WebFlow 2.0. I am trying to submit the form with a new event id.
<view-state id="projectSearch" view="project.projectSearch" model="searchCommand">
<transition on="search" to="projectSearch" bind="true" >
<evaluate expression="formAction.findProjects(flowRequestContext, searchCommand)"/>
</transition>
</view-state>
and
public Event findProjects(RequestContext context, SearchCommand command) throws Exception
{
return success();
}
On form submit I set the event key to search. With above configuration I do not receive any request to findProject() method. But if I remove model="" attribute from view-state I get the request received by the method. In this case I do not get form values.
Can anybody please direct me the correct path.
Thanks,
Vishal
It appeared to be jQuery for submit was causing the problem. If I comment the jQuery submit it works. I changed few lines and removed javascript need for form submit.
I'm new to Stripes and appreciate every hint that brings me nearer to a functioning web-app!
technological setup: java, dynamic web project, stripes, jsp
scenario:
users can login (index.jsp). After correct email-adress and password (LoginFormActionBean.java), the user is forwarded to a welcoming page (loggedin.jsp).
The content on this welcoming page is something like "welcome < username >, you've been successfully logged in!".
implementation:
i have a form in the index.jsp where i take the user input and pass it to a method in the LoginFormActionBean.java --> works!
in the corresponding method i check whether the user is correct and if so, i insert the user in the ActionBeanContext:
getContext.setUser(loggedinUser);
after that i forward to the loggedin.jsp:
return new ForwardResolution("/loggedin.jsp");
the loggedin.jsp contains following important lines:
<jsp:useBean id="loggedinBean" class="mywebapp.controller.LoggedinBean" scope="session" />
...
${loggedinBean.context.user} //show the whole user object
...
<s:form beanclass="mywebapp.controller.LoggedinBean" name="ButtonForm">
<s:submit name="foo" value="PrintUser" />
</s:form>
<s:form beanclass="mywebapp.controller.LoggedinBean" name="TextForm">
<s:text name="user" />
</s:form>
...
the LoggedinBean.java contains a MyActionBeanContext attribute (like the LoginFormActionBean.java).
to get the userobject out of the context i use:
public String getUser(){
return getContext().getUser().toString();
}
furthermore the LoggedinBean.java contains a method, which is annotated with #DefaultHandler and forwards to loggedin.jsp (the same page)
result:
now, what happens is: after logging in correctly, i'm forwarded to the loggedin.jsp,
the line "${loggedinBean.context.user}" is empty and so is the < s:text >-field.
BUT after clicking the "PrintUser" Button, the < s:text >-field in the "TextForm"-form is filled with the user object of the logged in user!
conclusion:
what i think happens, is that the "setContext()" method of the LoggedinBean.java is not called before i manually execute a method in the bean. Because the "setContext()" method in the bean is not called before i press the button!
the online documentation says to use a context attribute in a JSP just write "${actionBean.context.user}". But the context is null!
even the book "pragmatic stripes"(2008) gives no more information about using the ActionBeanContext.
question:
what happens there?
how can i get the "${loggedinBean.context.user}" line to display the logged in user at all?
and how can i get the < s:text >-field to display the user object after loading the JSP, but without pressing the button?
i hope my problem is clear and my remarks are satisfying
I would like to recommend the usage of the MVC pattern. This pattern will lead to an implementation were the Action Beans will act as controllers that handle all http requests and the JSP pages will become passive views with little logic, only accessible via the Action Bean controllers (no direct access to JSP pages any more!).
If you use this pattern, you always have an "actionBean" available in your JPS and thus you can refer to ${actionBean.context} (see: getContext).
While developing an application it's quite useful to be able to quickly login as different users, with different roles, to see how the application presents itself.
Typing usernames and entering password is no fun, and a waste of time. What I'd like to do is:
add a page/panel with a list of available usernames;
clicking on a username will generate an event for Spring security which allows it to recognize the user as authenticated, without entering passwords;
after clicking the link I am authenticated as the specified user.
N.B.: Passwords are hashed and submitted in plain-text using forms, so encoding the passwords in the links is not an option.
Obviously this feature will only be present at development time.
How can I achieve this?
Use InMemoryDaoImpl for development mode. It is very easy to create users and passwords stored in memory:
<bean id="userDetailsService" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
admin=admin,ROLE_ADMIN,ROLE_USER
user1=user1,ROLE_USER
user2=user2,ROLE_USER
</value>
</property>
</bean>
In development mode inject this to your authentication provider. In production replace it with the proper DB or LDAP implementation.
I have done it this way for an web application:
I have a configuration parameter in context.xml of the server (of course only in the development server). This parameter contains a coma seperated list of usernames and passwords.
The login page (jsp(x)) simply add a extra form and submit button for each username, password item form the context parameter. So if a user clicks on that button the normal login process with the predefined login data is trigged.
Server context.xml
...
<Context>
...
<Parameter name="quickLogin"
value="admin:passwd,user:otherPasswd"
override="false" />
</Context>
login.jspx
...
<!-- Login for debugging purposes -->
<c:forTokens items="${initParam.quickLogin}" delims="," var="loginPassword">
<c:set var="login" value="${fn:split(loginPassword, ':')[0]}" />
<c:set var="password" value="${fn:split(loginPassword, ':')[1]}" />
<form name="debugLogin" action="${form_url}" method="POST" >
<crsf:hiddenCrsfNonce/>
<input type="hidden" name='j_username' value="${fn:escapeXml(login)}" />
<input type="hidden" name='j_password' value="${fn:escapeXml(password)}" />
<input type="submit" value="${fn:escapeXml(login)} login" />
</form>
</c:forTokens>
...
As I understand you would like to have the SpringSecurity authenticate you automatically if some specific URL is requested (and you would have a link to this URL in your panel/page).
How about writing a custom filter:
public class YourSpecialDevelopmentTimeFilter
extends AuthenticationProcessingFilter
implements SyncSecurityFilter
....
that would override:
protected boolean requiresAuthentication(
HttpServletRequest request, HttpServletResponse response)
and return true depending on some parameters in the request?
Of course another concern is not to have this functionality in the production environments. That is always a risky thing to have different code-base for dev and prod.