After I finished the whole process and I had my Web App up and running on Heroku, I thought I had reached the end and it would all be working smoothly. My surprise came when some pictures on my site and some pages redirections were not working properly.
The key is in the way that you specify your URLs; you cannot use the forward slash ('/') to prefix your URLs, because Heroku will understand them as absolute paths, and it will look for the page in the wrong place.
That was the case of my web app, the login page had a reference to a warning image, but this image was not being loaded when the error message appeared.
The next snippet shows the original source code for that image import.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<c:if test="${param.error != null}"> | |
<div> | |
<img src="/images/iconWarning.gif" alt="<fmt:message key='icon.warning'/>"/> | |
<label><fmt:message key="errors.password.mismatch"/></label> | |
</div> | |
</c:if> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<c:if test="${param.error != null}"> | |
<div> | |
<img src="images/iconWarning.gif" alt="<fmt:message key='icon.warning'/>"/> | |
<label><fmt:message key="errors.password.mismatch"/></label> | |
</div> | |
</c:if> |
However, another problem arose when redirecting to the login failure URL (that was being handled by Spring Security), whose solution is similar to the one stated above, but requires more customisation.
Spring Security URL Authentication Failure Handling
The default URL authentication failure handler used within Spring Security is the SimpleUrlAuthenticationFailureHandler; the next snippet shows the typical definition of the security beans to use the default handler:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<http auto-config="true" lowercase-comparisons="false"> | |
<intercept-url pattern="/images/**" filters="none"/> | |
<intercept-url pattern="/styles/**" filters="none"/> | |
<intercept-url pattern="/scripts/**" filters="none"/> | |
<intercept-url pattern="/app/**" access="ROLE_ADMIN,ROLE_USER"/> | |
<form-login login-page="/login" | |
authentication-failure-url="/login?error=true" | |
login-processing-url="/j_security_check"/> | |
</http> |
Apart from that, the redirection uses a RedirectStrategy object, which modifies the URL string specified in the bean.
The solution I propose to solve this issue is to create a custom class to handle the authentication failure, and then use that class as a reference for the form-login bean.
The new definition of the security bean is as follows:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<http auto-config="true" lowercase-comparisons="false"> | |
<intercept-url pattern="/images/**" filters="none"/> | |
<intercept-url pattern="/styles/**" filters="none"/> | |
<intercept-url pattern="/scripts/**" filters="none"/> | |
<intercept-url pattern="/app/**" access="ROLE_ADMIN,ROLE_USER"/> | |
<form-login login-page="/login" | |
authentication-failure-handler-ref="noSlashUrlHandler" | |
login-processing-url="/j_security_check"/> | |
</http> | |
<!-- redirect url for failure of authentication --> | |
<beans:bean id="noSlashUrlHandler" | |
class="com.herokuapp.authentication.NoSlashAuthenticationFailureHandler"> | |
<beans:constructor-arg value="login?error=true"></beans:constructor-arg> | |
</beans:bean> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NoSlashAuthenticationFailureHandler implements AuthenticationFailureHandler { | |
protected final Log logger = LogFactory.getLog(getClass()); | |
private String defaultFailureUrl; | |
public NoSlashAuthenticationFailureHandler() { | |
} | |
public NoSlashAuthenticationFailureHandler(String defaultFailureUrl) { | |
setDefaultFailureUrl(defaultFailureUrl); | |
} | |
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, | |
AuthenticationException exception) throws IOException, ServletException { | |
if (defaultFailureUrl == null) { | |
logger.debug("No failure URL set, sending 401 Unauthorized error"); | |
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage()); | |
} else { | |
logger.debug("Redirecting to " + defaultFailureUrl); | |
response.sendRedirect(defaultFailureUrl); | |
} | |
} | |
/** | |
* The URL which will be used as the failure destination. | |
* @param defaultFailureUrl the failure URL, for example "loginFailed.jsp". | |
*/ | |
public void setDefaultFailureUrl(String defaultFailureUrl) { | |
this.defaultFailureUrl = defaultFailureUrl; | |
} | |
} |
Julio,
ReplyDeleteGreat post, Thanks for taking time to share this info on the net.
I am experimenting with an app, same scenario on Heroku, spring MVC, spring security.
However I figured out the forward slash was the culprit and quickly fixed it for resources redirection but my current challenge is, how do i create the link to home page on my nav bar. where it is simple / (using
"a href='s : url value="/" >Home a" (was not able to include tags as html not allowed here)
if i remove the / then the page goes no where.
I am yet to come to the security related redirect issues you mentioned, I would say your above post will save me hours of time when i encounter that.
Thanks again for your help.