I'm using Spring Security for securing my Spring Data REST webapplication with AngularJS.
My SecurityConfig is declared like this:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.authorizeRequests()
// public ressources
.antMatchers("/index.html", "/templates/public/**", "/js/**", "/").permitAll()
.antMatchers(HttpMethod.GET, "/api/security/user").permitAll()
.antMatchers("/api/**").hasAnyRole("ADMIN", "NORMALUSER")
.anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint(basicAuthenticationEntryPointHandler)
.and().logout().logoutUrl("/logout").logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()).deleteCookies("JSESSIONID").permitAll().and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
I generally followed this link for reaching my goal.
Now, if I would login myself for the first time, a angular-request for $cookies.get("XSRF-TOKEN")
returns undefined and every request to my database isn't blocked.
But after logging out and logging in again, it returns me the cookie and every database-request is allowed.
And this happens only every second login, so:
undefined
cookie
undefined
cookie
undefined
...
So now I hope that there's anyone who can help me without going deeper into my structure, but if it's required, I will do so.
Thanks in advance.
Edit 1: Here are the requests:
In relation to the process: I'm logging the tokens in my CokieCsrfTokenRepository().loadToken(), and there all tokens are shown..
Edit 2: My Angular Service, which I'm calling by every login:
function authenticationService($rootScope, $http, $location, $filter, $cookies){
function authenticate(credentials, callback) {
var headers = credentials ? {authorization : "Basic "
+ btoa(credentials.username + ":" + credentials.password)
} : {};
$http.get('api/security/user', {headers : headers}).success(function(data, status, get, header) {
if (data.name) {
$rootScope.authenticated = true;
$rootScope.session = data;
console.log($cookies.get("XSRF-TOKEN")) // -> returns every second login cookie
} else {
$rootScope.authenticated = false;
$rootScope.session = null;
}
console.log($rootScope.session)
callback && callback();
}).error(function() {
$rootScope.authenticated = false;
callback && callback();
});
}
return {
authenticate: function(credentials){
authenticate(credentials, function() {
if ($rootScope.authenticated == true) {
$rootScope.authenticationError = false;
if ($rootScope.session.principal.admin == true){
$location.path("/admin/manage");
} else{
$location.path("/user");
}
} else {
$rootScope.authenticationError = true;
$location.path("/login");
}
});
},
// called by refreshing browser
checkLoggedIn: function(){
authenticate();
},
logout: function(){
$http.post('/logout', {})["finally"](function() {
$rootScope.authenticated = false;
$rootScope.session = null;
$location.path("/login");
});
}
};
}
Edit 3: I mentioned now, that if the cookie is undefined, the method loadToken() from this link is called only after logging out and refreshing the browser (first login). Then the token is shown and I'm logged in, again. But after every second try it still works great..
Edit 4: So no I recognized, that after the first forbidden POST-request (in Edit3 it's the /logout) the token arrives my template. After refreshing the browser, all my requests are allowed, because now the cookie will be send for every request. But how to fix this?
My solution:
//WebSecurityConfigurerAdapter:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
....
.addFilterAfter(new CsrfCustomFilter(), CsrfFilter.class).and()
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
public class CsrfCustomFilter 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);
}
}