Spring POST form data sends  as escape character for special characters not present in the browser request - spring

I'm using a spring jsp form to send some data back to a controller.
<head>
<meta charset="utf-8">
</head>
(...)
<spring:url value="pathA" var="formActionVar"/>
<form:form action="${formActionVar}" modelAttribute="modelA" id="formIdA" method="post">
<input name="itemA" value="${modelA.itemA}" autocomplete="off"/>
<input name="itemB" value="${modelA.itemB}" autocomplete="off"/>
<button id="sendBtn" type="submit">
</form:form>
Looking at the browser request, the form data with Content-Type: application/x-www-form-urlencoded is as follows:
itemA=12%C2%A7&itemB=potatoes
The problem is that itemA is being converted to 12§ (like an escape char to all special chars, §, £, ...) meaning that there is something in between the browser request and the server doing that conversion.
Custom class that extends CharacterEncodingFilter:
public class CustomCharEncodingFilter extends CharacterEncodingFilter {
public CustomCharEncodingFilter() {
super("UTF-8", true, true);
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
System.out.println(request.getCharacterEncoding()); // <-- null
System.out.println("Encoded param: " + request.getParameter("itemA")); // <-- 12§
request.setCharacterEncoding("UTF-8"); // <-- tested with these 2 lines but no effect
setForceEncoding(true);
System.out.println(request.getCharacterEncoding()); // <-- UTF-8
System.out.println("Encoded param: " + request.getParameter("itemA")); // <-- 12§
super.doFilterInternal(request, response, filterChain);
}
}
But as you can see the value was already previously converted somewhere disregarding the character encoding.
The weird part is that doing the request using ajax with a parsed form representation does not convert the 12§ value to 12§ and the controller gets the correct value.
Edit:
Just a quick note to mention that using enctype="multipart/form-data" in the jsp form changes the itemA value to it's clear form (12§) but still gets the value to be "server-side rendered" as 12§

Related

Content-Security-Policy nonce mismatch on error pages (e.g. 404)

We use a Content-Security-Policy nonce successfully on normal pages (Thymeleaf templates). But when we try to do the same on error pages (e.g. 404), the nonce received as a model attribute does not match the nonce specified in the Content-Security-Policy HTTP header. This mismatch causes a policy violation and therefore script errors in our custom error page (also generated from a Thymeleaf template). In the Chrome console, the errors are
Content Security Policy: The page’s settings blocked the loading of a resource at http://localhost:8080/webjars/jquery/3.6.0/jquery.min.js (“script-src”).
Content Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”).
We enable the policy in the Spring Security configuration:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return
http.headers()
.contentSecurityPolicy("script-src 'strict-dynamic' 'nonce-{nonce}'")
.and().and()
.addFilterBefore(new ContentSecurityPolicyNonceFilter(), HeaderWriterFilter.class)
.build();
}
The filter is:
public class ContentSecurityPolicyNonceFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
var nonceArray = new byte[32];
(new SecureRandom()).nextBytes(nonceArray);
var nonce = Base64Utils.encodeToString(nonceArray);
request.setAttribute("cspNonce", nonce);
chain.doFilter(request, new NonceResponseWrapper((HttpServletResponse) response, nonce));
}
}
NonceResponseWrapper is
class NonceResponseWrapper extends HttpServletResponseWrapper {
private final String nonce;
NonceResponseWrapper(HttpServletResponse response, String nonce) {
super(response);
this.nonce = nonce;
}
private String getHeaderValue(String name, String value) {
final String retVal;
if (name.equals("Content-Security-Policy") && StringUtils.hasText(value)) {
retVal = value.replace("{nonce}", nonce);
} else {
retVal = value;
}
return retVal;
}
#Override
public void setHeader(String name, String value) {
super.setHeader(name, getHeaderValue(name, value));
}
#Override
public void addHeader(String name, String value) {
super.addHeader(name, getHeaderValue(name, value));
}
}
The nonce value is provided to the page via ControllerAdvice:
#ControllerAdvice
public class ContentSecurityPolicyControllerAdvice {
#ModelAttribute
public void addAttributes(Model model, HttpServletRequest request) {
model.addAttribute("nonce", request.getAttribute("cspNonce"));
}
}
The working index page and the dysfunctional error page follow the same pattern in Thymeleaf and HTML terms:
index.html:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Application</title>
<script th:src="#{/webjars/jquery/3.6.0/jquery.min.js}" th:nonce="${nonce}"></script>
<script th:inline="javascript" th:nonce="${nonce}">
const randomNumber = /*[[${randomNumber}]]*/ -1;
$(function() {
$('#a-number').text(randomNumber);
});
</script>
</head>
<body>
<h1>Welcome</h1>
<p>Your random number is <span id="a-number">unknown</span>.</p>
</body>
</html>
error/404.html:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>404 Error</title>
<script th:src="#{/webjars/jquery/3.6.0/jquery.min.js}" th:nonce="${nonce}"></script>
<script th:nonce="${nonce}">
$(function() {
const timestampString = new Date().toISOString();
$('#timestamp').text(timestampString);
});
</script>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>The current time is <span id="timestamp">unknown</span>.</p>
</body>
</html>
Application debug output when loading the invalid URL shows
Nonce for request = qPhdJiUAAkKHrwQBvxzxUz0OUUU4UXaxLcDErhl4g7U=
Content-Security-Policy = script-src 'strict-dynamic' 'nonce-qPhdJiUAAkKHrwQBvxzxUz0OUUU4UXaxLcDErhl4g7U='
Nonce for request = OiZmhtGlYMgb4X+pcFIwM41GzEkre3YvfkLCHFqoqIU=
Nonce for view model = OiZmhtGlYMgb4X+pcFIwM41GzEkre3YvfkLCHFqoqIU=
Nonce for request = sCbXWXA0TPjw+I/dui2bmee1vKKXG1Y2Xt3G7JkuZ04=
Content-Security-Policy = script-src 'strict-dynamic' 'nonce-sCbXWXA0TPjw+I/dui2bmee1vKKXG1Y2Xt3G7JkuZ04='
Nonce for request = hsGwh4+5oqg0W51zNprrT41rHnEeJRdHHO8KTMCSwL8=
Content-Security-Policy = script-src 'strict-dynamic' 'nonce-hsGwh4+5oqg0W51zNprrT41rHnEeJRdHHO8KTMCSwL8='
In this run, the nonce interpolated into the policy is qPhdJiUAAkKHrwQBvxzxUz0OUUU4UXaxLcDErhl4g7U=, while the page is getting OiZmhtGlYMgb4X+pcFIwM41GzEkre3YvfkLCHFqoqIU= from the view model.
I've constructed a minimal, runnable (./gradlew bootRun) code base for this problem at https://gitlab.com/russell-medisens/nonce-problem.git for anyone who might take a look.
I believe I've solved this problem by changing the filter to avoid overwriting an existing nonce:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final String nonce;
final Object existingNonce = request.getAttribute(REQUEST_NONCE_ATTRIBUTE);
if (existingNonce == null) {
final var nonceArray = new byte[NONCE_SIZE];
SECURE_RANDOM.nextBytes(nonceArray);
nonce = Base64Utils.encodeToString(nonceArray);
request.setAttribute(REQUEST_NONCE_ATTRIBUTE, nonce);
System.err.format("Nonce generated in filter = %s%n", nonce);
} else {
nonce = (String) existingNonce;
System.err.format("Existing nonce retained in filter = %s%n", nonce);
}
chain.doFilter(request, new NonceResponseWrapper((HttpServletResponse) response, nonce));
}
My understanding is that when a requested page is not found, Spring performs a forward (rather than a redirect), but the filter is invoked a second time in the process of serving the substituted 404 page. This code change preserves any existing nonce so that it can be provided to the view model for the error page.

How to update data on submit button and stay on the same jsp page? [duplicate]

How can I display an error message in the very same JSP when a user submits a wrong input? I do not intend to throw an exception and show an error page.
Easiest would be to have placeholders for the validation error messages in your JSP.
The JSP /WEB-INF/foo.jsp:
<form action="${pageContext.request.contextPath}/foo" method="post">
<label for="foo">Foo</label>
<input id="foo" name="foo" value="${fn:escapeXml(param.foo)}">
<span class="error">${messages.foo}</span>
<br />
<label for="bar">Bar</label>
<input id="bar" name="bar" value="${fn:escapeXml(param.bar)}">
<span class="error">${messages.bar}</span>
<br />
...
<input type="submit">
<span class="success">${messages.success}</span>
</form>
In the servlet where you submit the form to, you can use a Map<String, String> to get hold of the messages which are to be displayed in JSP.
The Servlet #WebServlet("foo"):
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/WEB-INF/foo.jsp").forward(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String> messages = new HashMap<String, String>();
request.setAttribute("messages", messages); // Now it's available by ${messages}
String foo = request.getParameter("foo");
if (foo == null || foo.trim().isEmpty()) {
messages.put("foo", "Please enter foo");
} else if (!foo.matches("\\p{Alnum}+")) {
messages.put("foo", "Please enter alphanumeric characters only");
}
String bar = request.getParameter("bar");
if (bar == null || bar.trim().isEmpty()) {
messages.put("bar", "Please enter bar");
} else if (!bar.matches("\\d+")) {
messages.put("bar", "Please enter digits only");
}
// ...
if (messages.isEmpty()) {
messages.put("success", "Form successfully submitted!");
}
request.getRequestDispatcher("/WEB-INF/foo.jsp").forward(request, response);
}
In case you create more JSP pages and servlets doing less or more the same, and start to notice yourself that this is after all a lot of repeated boilerplate code, then consider using a MVC framework instead.
See also:
Our Servlets wiki page
What is the best practice for validating parameters in JSP?
I see tag "form-validation", so maybe you should just use JavaScript and client form validation? If you need validation with JSP, handle form data, and redisplay the form with an error message or accept form data, if it's correct.
I'm not quite sure what you mean by "display error message". If you have a standard error handling, then you can always check for options:
<%
if(wrong option selected)
throw new Exception("Invalid option selected");
%>
Of course, this is just the notion; preferably, you'd have your own exception class. But then again, I'm not quite sure what you're after.

Spring portlet ajax call, No parameter found

I am trying to build a sample spring portlet, with LR 6.2 GA1.
Below is the source for the same https://docs.google.com/file/d/0By1kU5o_jlrublhUNXIxQ24wODQ/edit
On the ajax the parameters are not being fetched.The parameters always remain blank.
#Controller(value = "ProjectSearch")
#RequestMapping("VIEW")
public class ProjectSearch {
Log log_ = LogFactoryUtil.getLog(ProjectSearch.class);
#RenderMapping
public String handleRenderRequest(final RenderRequest request,
final RenderResponse response, Model model) {
System.out.println("ProjectSearch.handleRenderRequest()");
return "search_form";
}
#ResourceMapping("getProjectNameSuggestion")
public void getNameSuggestion(ResourceRequest request,
ResourceResponse response) throws IOException {
Map<String, String[]> map = request.getParameterMap();
for (Map.Entry<String, String[]> element : map.entrySet()) {
log_.info(element.getKey());
}
String entityName = ParamUtil.getString(request, "query");
log_.info("Entity name==>" + entityName);
}
}
#RenderMapping
public String handleRenderRequest(final RenderRequest request,
final RenderResponse response, Model model) {
System.out.println("ProjectSearch.handleRenderRequest()");
return "search_form";
}
#ResourceMapping("getProjectNameSuggestion")
public void getNameSuggestion(ResourceRequest request,
ResourceResponse response) throws IOException {
Map<String, String[]> map = request.getParameterMap();
for (Map.Entry<String, String[]> element : map.entrySet()) {
log_.info(element.getKey());
}
String entityName = ParamUtil.getString(request, "query");
log_.info("Entity name==>" + entityName);
}
}
Output-->05:23:24,148 INFO [http-bio-8080-exec-119][ProjectSearch:41] Entity name==>
Could any body tell me what is that I am doing wrong??
Solution:
Configure Requires Name Spaced Parameters to false in liferay-portlet.xml
Now need to do require Name spaced parameters to false then only form data is mapped in Action Request and Render Request. And also form data will be binding to model object or command object.
The following is configuration we need to do in liferay-portlet.xml file
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Required Name Space Parameter Behavior in Liferay
Liferay 6.2 we have to append portlet Name space for every name of input element i.e. form input elements or request parameters names otherwise portlet action class ignore the parameters which does not have portlet name space to names.
Scenario
Jsp page
In the following form we are not appending portlet name space to form input element names.
<portlet:actionURL var="addEmployeeActionURL" name="addEmployee">
<portlet:param name="<%=ActionRequest.ACTION_NAME%>" value="addEmployee"/>
</portlet:actionURL>
<form action="<%=addEmployeeActionURL%>" name="emplyeeForm" method="POST">
Employee Name<br/>
<input type="text" name="employeeName" id="employeeName"/><br/>
Employee Address<br/>
<input type="text" name="employeeAddress" id="employeeName"/><br/>
<input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/>
</form>
Portlet Class Action Method
public class EmplyeePortletAction extends MVCPortlet {
public void addEmployee(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException, PortletException {
String employeeName=ParamUtil.getString(actionRequest,"employeeName");
String employeeAddress=ParamUtil.getString(actionRequest,"employeeAddress");
}
}
In above case employeeName and employeeAddress form input data not accessible in portlet class action .The form elements name are not appended with portlet name space such scenarios portlet class ignore those request parameters or form inputs
Solution:1
Need to append tag to every input element name.
Jsp page
<portlet:actionURL var="addEmployeeActionURL" name="addEmployee">
<portlet:param name="<%=ActionRequest.ACTION_NAME%>" value="addEmployee"/>
<portlet:param name="requestParam" value=" requestParamValue"/>
</portlet:actionURL>
<form action="<%=addEmployeeActionURL%>" name="emplyeeForm" method="POST">
Employee Name<br/>
<input type="text" name="<portlet:namespace/>employeeName" id="<portlet:namespace/>employeeName"/><br/>
Employee Address<br/>
<input type="text" name="<portlet:namespace/>employeeAddress" id="<portlet:namespace/>employeeName"/><br/>
<input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/>
</form>
Portlet Class Action Method
public class EmplyeePortletAction extends MVCPortlet {
public void addEmployee(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException, PortletException {
String employeeName=ParamUtil.getString(actionRequest,"employeeName");
String employeeAddress=ParamUtil.getString(actionRequest,"employeeAddress");
String requestParamValue=ParamUtil.getString(actionRequest,"requestParam");
}
}
Solution:2
We can make it false to following tag value in liferay-portlet.xml file
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Solution:3
We can use alloy tag library form tags. When we use AUI tags it will append portlet name space to each input element name.
Jsp page
<%# taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<aui:input type="text" name="employeeAddress" id="employeeName"/><br/>
<aui:input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/
<input type="text" name="<portlet:namespace/>employeeAddress" id="<portlet:namespace/>employeeName"/>
Is same As
<aui:input type="text" name="employeeAddress" id="employeeName"/>
http://www.liferaysavvy.com/2013/12/liferay-spring-portlet.html
http://www.liferaysavvy.com/2014/04/liferay-mvc-portlet-development.html

With Spring Security 3.2.0.RELEASE, how can I get the CSRF token in a page that is purely HTML with no tag libs

Today I upgraded from Spring Security 3.1.4 with the separate java config dependency, to the new 3.2.0 release which includes java config. CSRF is on by default and I know I can disable it in my overridden configure method with "http.csrf().disable()". But suppose I don't want to disable it, but I need the CSRF token on my login page where no JSP tag libs or Spring tag libs are being used.
My login page is purely HTML that I use in a Backbone app that I've generated using Yeoman. How would I go about including the CSRF token that's contained in the HttpSession in either the form or as a header so that I don't get the "Expected CSRF token not found. Has your session expired?" exception?
You can obtain the CSRF using the request attribute named _csrf as outlined in the reference. To add the CSRF to an HTML page, you will need to use JavaScript to obtain the token that needs to be included in the requests.
It is safer to return the token as a header than in the body as JSON since JSON in the body could be obtained by external domains. For example your JavaScript could request a URL processed by the following:
CsrfToken token = (CsrfToken) request.getAttribute("_csrf");
// Spring Security will allow the Token to be included in this header name
response.setHeader("X-CSRF-HEADER", token.getHeaderName());
// Spring Security will allow the token to be included in this parameter name
response.setHeader("X-CSRF-PARAM", token.getParameterName());
// this is the value of the token to be included as either a header or an HTTP parameter
response.setHeader("X-CSRF-TOKEN", token.getToken());
Your JavaScript would then obtain the header name or the parameter name and the token from the response header and add it to the login request.
Although #rob-winch is right I would suggest to take token from session. If Spring-Security generates new token in SessionManagementFilter using CsrfAuthenticationStrategy it will set it to Session but not on Request. So it is possible you will end up with wrong csrf token.
public static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class.getName().concat(".CSRF_TOKEN");
CsrfToken sessionToken = (CsrfToken) request.getSession().getAttribute(DEFAULT_CSRF_TOKEN_ATTR_NAME);
Note: I'm using CORS and AngularJS.
Note²: I found Stateless Spring Security Part 1: Stateless CSRF protection which would be interesting to keep the AngularJS' way to handle CSRF.
Instead of using Spring Security CSRF Filter which is based on answers (especially #Rob Winch's one), I used the method described in The Login Page: Angular JS and Spring Security Part II.
In addition to this, I had to add Access-Control-Allow-Headers: ..., X-CSRF-TOKEN (due to CORS).
Actually, I find this method cleaner than adding headers to the response.
Here is the code :
HttpHeaderFilter.java
#Component("httpHeaderFilter")
public class HttpHeaderFilter extends OncePerRequestFilter {
#Autowired
private List<HttpHeaderProvider> providerList;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
providerList.forEach(e -> e.filter(request, response));
if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
response.setStatus(HttpStatus.OK.value());
}
else {
filterChain.doFilter(request, response);
}
}
}
HttpHeaderProvider.java
public interface HttpHeaderProvider {
void filter(HttpServletRequest request, HttpServletResponse response);
}
CsrfHttpHeaderProvider.java
#Component
public class CsrfHttpHeaderProvider implements HttpHeaderProvider {
#Override
public void filter(HttpServletRequest request, HttpServletResponse response) {
response.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "X-CSRF-TOKEN");
}
}
CsrfTokenFilter.java
#Component("csrfTokenFilter")
public class CsrfTokenFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CsrfToken csrf = (CsrfToken)request.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null && !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
}
web.xml
...
<filter>
<filter-name>httpHeaderFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>httpHeaderFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
...
security-context.xml
...
<custom-filter ref="csrfTokenFilter" after="CSRF_FILTER"/>
...
app.js
...
.run(['$http', '$cookies', function ($http, $cookies) {
$http.defaults.transformResponse.unshift(function (data, headers) {
var csrfToken = $cookies['XSRF-TOKEN'];
if (!!csrfToken) {
$http.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken;
}
return data;
});
}]);
I use thymeleaf with Spring boot. I had the same problem. I diagnosed problem viewing source of returned html via browser. It should be look like this:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<form method="post" action="/login">
<div><label> User Name : <input type="text" name="username" /> </label></div>
<div><label> Password: <input type="password" name="password" /> </label></div>
<input type="hidden" name="_csrf" value=<!--"aaef0ba0-1c75-4434-b6cf-62c975dcc8ba"--> />
<div><input type="submit" value="Sign In" /></div>
</form>
</body>
</html>
If you can't see this html code. You may be forgot to put th: tag before name and value. <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}"> Invalid username and password. </div>
<div th:if="${param.logout}"> You have been logged out. </div>
<form th:action="#{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

Flash attributes in Spring MVC 3.1 not visible to redirected JSP

I am using Spring 3.1's new Flash Attribute support to set flash attributes on a RedirectAttributes object in a Controller and then invoking a redirect. That redirect request is in turn caught by a filter which then sends it on its merry way to the JSP that it's intended for. The problem: I can't see the flash attributes either from within the filter's doFilter() method or from the JSP. Non-flash (URL) attributes make it just fine.
Controller that does the redirect:
#RequestMapping("/pages/login")
public String login (HttpServletRequest request, Map<String, Object> model, RedirectAttributes redirectAttributes) {
model.put("userId", "batman");
String redirectUrl = request.getParameter("redirectUrl");
if (redirectUrl != null) {
redirectAttributes.addAttribute("attr1","ababab");
redirectAttributes.addFlashAttribute("flashAttr1", "flashflash");
for (Iterator<String> iterator = model.keySet().iterator(); iterator.hasNext();) {
String key = iterator.next();
redirectAttributes.addFlashAttribute(key, model.get(key));
}
return "redirect:"+redirectUrl;
} else {
return "pages/login";
}
}
The filter which picks up the redirect doesn't do anything interesting in this case:
public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
//if (httpRequest.getSession().getAttribute("userId") == null) {
//...do some stuff here which invokes controller above as well as the redirect
//} else {
chain.doFilter(request, response);
//}
}
The page that gets redirected to following the filter:
...
<title>Test Web App 1</title>
</head>
<body>
<p>Flash attribute: <c:out value="${flashAttr1}"/></p>
<p>Welcome <c:out value="${userId}"/>!</p>
</body>
</html>
Neither flashAttr1 nor userId end up being populated in the page. The attr1 non-flash attribute the controller set does appear in the URL params of the page, so that seems to work.
Here is some output from log4j after I set springfamework.web to DEBUG:
19:15:44,406 DEBUG http-8080-1 view.ContentNegotiatingViewResolver:494 - Returni
ng redirect view [org.springframework.web.servlet.view.RedirectView: name 'redir
ect:http://my_hostname:8080/test-webapp-1/protected/protected_page.jsp';
URL [http://my_hostname:8080/test-webapp-1/protected/protected_page.jsp]]
19:15:44,406 DEBUG http-8080-1 servlet.DispatcherServlet:1155 -
Rendering view [org.springframework.web.servlet.view.RedirectView: name
'redirect:http://my_hostname:8080/test-webapp-1/protected/protected_page.jsp';
URL [http://my_hostname:8080/test-webapp-1/protected/protected_page.jsp]] in
DispatcherServlet with name 'dispatcher'
19:15:44,421 DEBUG http-8080-1 support.DefaultFlashMapManager:199 - Saving Flash
Map=[Attributes={userId=batman, flashAttr1=flashflash}, targetRequestPath=/test-
webapp-1/protected/protected_page.jsp, targetRequestParams={attr1=[ababab]}]
19:15:44,421 DEBUG http-8080-1 servlet.DispatcherServlet:913 - Successfully comp
leted request
Following a brief stop at the filter I've shown above, I am taken to the page with URL
http://my_hostname:8080/test-webapp-1/protected/protected_page.jsp?attr1=ababab
But neither of the attributes I expect that JSP to find are displayed. I have also debugged through the doFilter() method shown above and failed to find the flash attributes in the request's session.
I'm not sure exactly what's wrong at this point. Everything works as expected except for those flash attributes. If there is anything else I should provide to make the situation more clear, I will be happy to.
Ran into this issue a few months ago with AJAX-related redirects. If you use a read-only HTTP POST pattern, you can specify a #ResponseStatus to simulate a POST. Also be sure to have your method return a View or ModelAndView (as opposed to String) so that Spring knows to look up the Flash scope for the given #RequestMapping.
Pseudocode:
#RequestMapping(...)
#ResponseStatus(OK)
public ModelAndView login (...) {
...
}

Resources