I have an app build with Spring MVC and secured with Spring security, a bunch of the controllers are JSON rest services that are all protected. I\'m using LoginUrlAut
Confirming that this works perfectly fine also with Spring Boot combined with Spring security in the programmatic way to setup security without any required XML, for example:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin**").hasRole("ADMIN")
// everything else
.anyRequest().fullyAuthenticated()
.and()
.exceptionHandling().authenticationEntryPoint(new AjaxAwareAuthenticationEntryPoint("/login"));
}
I realise this is from quite some time ago, but I want to put this here to see if it helps someone else.
I followed the same idea in http://distigme.wordpress.com/2012/11/01/ajax-and-spring-security-form-based-login/ and had the same issue in that the first returned content was the login page, and the next was a HTTP 403.
I think this is the part of Spring where we hit the split between Spring XML config doing everything, or we write a bunch of code to overload what it can do for us. I prefer to do as much as I can in the XML config.
My solution was to have the XML configuration throwing a 403 error as what the blog has. I didn't write a Matching
class because my workflow required going back to the first page, so I don't use the org.springframework.security.web.savedrequest.HttpSessionRequestCache
.
<bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg name="loginFormUrl" value="/index.html" />
</bean>
<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint">
<constructor-arg>
<map>
<entry key="!hasHeader('X-Requested-With','XMLHttpRequest')" value-ref="loginUrlAuthenticationEntryPoint" />
</map>
</constructor-arg>
<property name="defaultEntryPoint">
<bean class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
</property>
</bean>
I'm a big fan of nesting beans if I don't need them elsewhere. In my $.ajax
call I put
dataType: 'json'
to make sure that if the returned content is not JSON (e.g. the login page) then the error
function is called. This will also catch a 403 error as well.
error: function (xhr, textStatus, errorThrown) {
if (xhr.status == 403 || textStatus == 'parsererror' && xhr.responseText.match('rememberMe').length > 0) {
alert('Your session has timed out.');
window.location = '<c:url value="/index.html" />';
} else
alert('Something went wrong. ' + xhr.status + ': ' + errorThrown);
}
I'm searching for the rememberMe
text to make sure it's the login page. I don't expect that on any other page.
I fixed this issue by implementing by own custom filter, placing it before ANONYMOUS_FILTER and return 403 if the Spring principal doesn't exist.