<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script><script src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js"></script>
</head>
<body>
<p>Hi,</p>
<p>I will suppose that you have done it in Angular1 but I will</p>
<p>First of all I want to explain the <b>mechanism</b> and how spring security do protect the resources when the CSRF filter is set, then I will post some code snippets. A BackEnd server secured by the Spring Security Framework: where the CSRF protection
is enabled :all it wants is a token sent to it in a header called <b>"X-CSRF"</b>. The value of the CSRF token was available server side in the HttpRequest attributes from the initial request that loaded the home page. To get it to the client we could
render it using a dynamic HTML page on the server, or expose it via a custom endpoint, or else we could send it as a <b>cookie</b>. </p>
<p>When we worked on Angular1 we knew that Angular has a built in support for CSRF (which it calls "XSRF") based on cookies. We will talk about this support later...</p>
<p>Today with Angular2 we don't have a special support for CSRF, but when we understand how it woks we can implements our own solution and we can even share it as an open source solution on github to support the community.("and this is what I am preparing
myself to do.. :p )"</p>
<p>Angular wants the cookie to be named as <b>"XSRF-TOKEN"</b>. And when Spring Security provides it as a request attribute, we just need to transfer the value from a request attribute to a cookie. ( we will create a <b> CsrfHeaderFilter.java </b> class
that will do this conversion).
</p>
<p>Then We have to add this <b> CsrfHeaderFiler</b> that we have created it to create the cookie and send it to our client App, just after the CsrfFilter.class provided by SS "Spring Security".</p>
<p>Now be careful: as the BackEnd server is waiting to receive the value of the token that we have sent in a http request header named "X-CSRF-TOKEN" by default. Angular1 as I told you has a support built in for this mechanism and has always send the value
stored in the X-XRSF-TOKEN cookie as a http request header called "X-XRSF-TOKEN". But the problem is that our server is waiting for token in an other name witch is the "X-CSRF-TOKEN" by default like I told you just before. Then to solve the problem
we customise the CSRF filter provided by SS.</p>
<p>code snippet here: the methode that change the received header X-XSRF-TOKEN sent by Angular..</p> <pre class="prettyprint">private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}</pre>
<h2>Angular2</h2>
<p>Now that we learnt about the mechanism and how it works this CSRF protection "dance" from BackEnd (SS) to FrontEnd(Angular1) we have two solutions :</p>
<ul>
<li>Do it in Angular2 like the Angular1 way by creating a cookie named "X-XRSF-TOKEN" then we send it in a header request with the same name.</li>
<li>Or we do it like the SS "Spring security" way, I mean like it expected it to be received as a cookie. So we send it with a name "XSRF-TOKEN" then we will save it in the cookie storage to our browser, and just before requesting any resource on the
server we have to send back its value as "X-CSRF-TOKEN" a default name expected from the SS.</li>
</ul>
<p>If I can give you one solution the second will not be much harder to have.. , So I will go with the first solution just to do not change the behavior of our SS server. </p>
<p>some code snippet: our CsrfHeaderFilter.java witch have to create the cookie for our SPA. </p> <pre class="prettyprint">public class CsrfHeaderFilter 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);
}
}</pre>
<p> the whole SS code that enables you to create a CSRF filter and customise it to be handled by a SPA based on Angular1. </p> <pre class="prettyprint">@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests()
.antMatchers("/index.html", "/home.html", "/login.html", "/").permitAll().anyRequest()
.authenticated().and().csrf()
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class);
}
private Filter csrfHeaderFilter() {
return new 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);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}</pre>
<p>Now our SPA based on Angular2 but having the same behavior of an Angular1 SPA</p> <pre class="prettyprint">import {Injectable, provide} from 'angular2/core';
import {BaseRequestOptions, RequestOptions} from 'angular2/http'
@Injectable()
export class ExRequestOptions extends BaseRequestOptions {
constructor() {
super();
this.headers.append('X-XSRF-TOKEN', this.getCookie('XSRF-TOKEN'));
}
appendHeaders(headername: string, headervalue: string) {
this.headers.append(headername, headervalue);
}
getCookie(name) {
let value = "; " + document.cookie;
let parts = value.split("; " + name + "=");
if (parts.length == 2)
return parts.pop().split(";").shift();
}
}</pre>
<p>And then when we want to use our custom service in our http requests:</p> <pre class="prettyprint">let options = new ExRequestOptions();
options.appendHeaders('Content-Type', 'application/json');
return this.http.post('.....URL', JSON.stringify(registration),
options)</pre>
<p>Don't forget about the bootstrapping !: </p> <pre class="prettyprint">import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'
import {HTTP_PROVIDERS, RequestOptions} from 'angular2/http';
import 'rxjs/Rx';
import {ExRequestOptions} from './transportBoxes/exRequestOptions';
import {provide} from 'angular2/core';
bootstrap(AppComponent,[ HTTP_PROVIDERS,
provide(RequestOptions, {useClass: ExRequestOptions})]);</pre>
<p>And finally that is it... you did it. We have ported the same built in CSRF support for Angular1 to Angular2 without touching our SS server.</p>
</body>
</html>
// Code goes here
/* Styles go here */