Skip to content

Commit 6d976a1

Browse files
author
Dave Syer
committed
Convert logout sample to jquery
XSRF cookies a bit ugly.
1 parent a14c601 commit 6d976a1

File tree

4 files changed

+118
-68
lines changed

4 files changed

+118
-68
lines changed

click/README.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ at the end of the `<body>`):
4040
[source,html]
4141
----
4242
<script type="text/javascript">
43-
$.get("/user", null, function(data) {
43+
$.get("/user", function(data) {
4444
$("#user").html(data.userAuthentication.details.name);
4545
$(".unauthenticated").hide()
4646
$(".authenticated").show()

click/src/main/resources/static/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h1>Login</h1>
2222
Logged in as: <span id="user"></span>
2323
</div>
2424
<script type="text/javascript">
25-
$.get("/user", null, function(data) {
25+
$.get("/user", function(data) {
2626
$("#user").html(data.userAuthentication.details.name);
2727
$(".unauthenticated").hide()
2828
$(".authenticated").show()

logout/README.adoc

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,17 @@ JavaScript:
3232

3333
.index.html
3434
----
35-
angular
36-
.module("app", [])
37-
...
38-
.controller("home", function($http, $location) {
39-
var self = this;
40-
...
41-
self.logout = function() {
42-
$http.post('/logout', {}).success(function() {
43-
self.authenticated = false;
44-
$location.path("/");
45-
}).error(function(data) {
46-
console.log("Logout failed")
47-
self.authenticated = false;
48-
});
49-
};
50-
});
35+
var logout = function() {
36+
$.post("/logout", function() {
37+
$("#user").html('');
38+
$(".unauthenticated").show();
39+
$(".authenticated").hide();
40+
})
41+
return true;
42+
}
5143
----
5244

53-
The `logout()` function does a POST to `/logout` and then clears the
54-
`authenticated` flag. Now we can switch over to the server side to
45+
The `logout()` function does a POST to `/logout` and then clears the dynamic content. Now we can switch over to the server side to
5546
implement that endpoint.
5647

5748
== Adding a Logout Endpoint
@@ -78,14 +69,17 @@ requires a token to be included in the request. The value of the token
7869
is linked to the current session, which is what provides the
7970
protection, so we need a way to get that data into our JavaScript app.
8071

81-
AngularJS also has built in support for CSRF (they call it XSRF), but
82-
it is implemented in a slightly different way than the out-of-the box
83-
behaviour of Spring Security. What Angular would like is for the
84-
server to send it a cookie called "XSRF-TOKEN" and if it sees that, it
85-
will send the value back as a header named "X-XSRF-TOKEN". To teach
86-
Spring Security about this we need to add a filter that creates the
87-
cookie and also we need to tell the existing CRSF filter about the
88-
header name. In the `WebSecurityConfigurer`:
72+
Many JavaScript frameworks have built in support for CSRF (e.g. in
73+
Angular they call it XSRF), but it is often implemented in a slightly
74+
different way than the out-of-the box behaviour of Spring
75+
Security. For instance in Angular the front end would like the server
76+
to send it a cookie called "XSRF-TOKEN" and if it sees that, it will
77+
send the value back as a header named "X-XSRF-TOKEN". We can implement
78+
the same behaviour with our simple jQuery client, and then the server
79+
side changes will work with other front end implementations with no or
80+
very few changes. To teach Spring Security about this we need to add a
81+
filter that creates the cookie and also we need to tell the existing
82+
CRSF filter about the header name. In the `WebSecurityConfigurer`:
8983

9084
.SocialApplication.java
9185
[source,java]
@@ -98,6 +92,44 @@ protected void configure(HttpSecurity http) throws Exception {
9892
}
9993
----
10094

95+
== Adding the CSRF Token in the Client
96+
97+
Since we are not using a higher level framework in this sample, we
98+
need to explicitly add the CSRF token, which is available as a cookie. The code is simple, if a bit long winded:
99+
100+
.index.html
101+
----
102+
$.ajaxSetup({
103+
beforeSend : function(xhr, settings) {
104+
if (settings.type == 'POST' || settings.type == 'PUT'
105+
|| settings.type == 'DELETE') {
106+
function getCookie(name) {
107+
var cookieValue = null;
108+
if (document.cookie && document.cookie != '') {
109+
var cookies = document.cookie.split(';');
110+
for (var i = 0; i < cookies.length; i++) {
111+
var cookie = jQuery.trim(cookies[i]);
112+
// Does this cookie string begin with the name we want?
113+
if (cookie.substring(0, name.length + 1) == (name + '=')) {
114+
cookieValue = decodeURIComponent(cookie
115+
.substring(name.length + 1));
116+
break;
117+
}
118+
}
119+
}
120+
return cookieValue;
121+
}
122+
if (!(/^http:.*/.test(settings.url) || /^https:.*/
123+
.test(settings.url))) {
124+
// Only send the token to relative URLs i.e. locally.
125+
xhr.setRequestHeader("X-XSRF-TOKEN",
126+
getCookie('XSRF-TOKEN'));
127+
}
128+
}
129+
}
130+
});
131+
----
132+
101133
== Ready To Roll!
102134

103135
With those changes in place we are ready to run the app and try out

logout/src/main/resources/static/index.html

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,48 +8,66 @@
88
<meta name="viewport" content="width=device-width" />
99
<base href="/" />
1010
<link rel="stylesheet" type="text/css"
11-
href="/webjars/bootstrap/css/bootstrap.min.css" />
11+
href="/webjars/bootstrap/css/bootstrap.min.css" />
1212
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
1313
<script type="text/javascript"
14-
src="/webjars/bootstrap/js/bootstrap.min.js"></script>
14+
src="/webjars/bootstrap/js/bootstrap.min.js"></script>
1515
</head>
16-
<body ng-app="app" ng-controller="home as home">
17-
<h1>Login</h1>
18-
<div class="container" ng-show="!home.authenticated">
19-
With Facebook: <a href="/login">click here</a>
20-
</div>
21-
<div class="container" ng-show="home.authenticated">
22-
Logged in as: <span ng-bind="home.user"></span>
23-
<div>
24-
<button ng-click="home.logout()" class="btn btn-primary">Logout</button>
25-
</div>
26-
</div>
27-
<script type="text/javascript" src="/webjars/angularjs/angular.min.js"></script>
28-
<script type="text/javascript">
29-
angular
30-
.module("app", [])
31-
.config(
32-
function($httpProvider) {
33-
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
34-
}).controller("home", function($http, $location) {
35-
var self = this;
36-
$http.get("/user").success(function(data) {
37-
self.user = data.userAuthentication.details.name;
38-
self.authenticated = true;
39-
}).error(function() {
40-
self.user = "N/A";
41-
self.authenticated = false;
42-
});
43-
self.logout = function() {
44-
$http.post('logout', {}).success(function() {
45-
self.authenticated = false;
46-
$location.path("/");
47-
}).error(function(data) {
48-
console.log("Logout failed")
49-
self.authenticated = false;
50-
});
51-
};
52-
});
53-
</script>
16+
<body>
17+
<h1>Login</h1>
18+
<div class="container unauthenticated">
19+
With Facebook: <a href="/login">click here</a>
20+
</div>
21+
<div class="container authenticated" style="display: none">
22+
Logged in as: <span id="user"></span>
23+
<div>
24+
<button onClick="logout()" class="btn btn-primary">Logout</button>
25+
</div>
26+
</div>
27+
<script type="text/javascript">
28+
$
29+
.ajaxSetup({
30+
beforeSend : function(xhr, settings) {
31+
if (settings.type == 'POST' || settings.type == 'PUT'
32+
|| settings.type == 'DELETE') {
33+
function getCookie(name) {
34+
var cookieValue = null;
35+
if (document.cookie && document.cookie != '') {
36+
var cookies = document.cookie.split(';');
37+
for (var i = 0; i < cookies.length; i++) {
38+
var cookie = jQuery.trim(cookies[i]);
39+
// Does this cookie string begin with the name we want?
40+
if (cookie.substring(0, name.length + 1) == (name + '=')) {
41+
cookieValue = decodeURIComponent(cookie
42+
.substring(name.length + 1));
43+
break;
44+
}
45+
}
46+
}
47+
return cookieValue;
48+
}
49+
if (!(/^http:.*/.test(settings.url) || /^https:.*/
50+
.test(settings.url))) {
51+
// Only send the token to relative URLs i.e. locally.
52+
xhr.setRequestHeader("X-XSRF-TOKEN",
53+
getCookie('XSRF-TOKEN'));
54+
}
55+
}
56+
}
57+
});
58+
$.get("/user", function(data) {
59+
$("#user").html(data.userAuthentication.details.name);
60+
$(".unauthenticated").hide();
61+
$(".authenticated").show();
62+
});
63+
var logout = function() {
64+
$.post("/logout", function() {
65+
$("#user").html('');
66+
$(".unauthenticated").show();
67+
$(".authenticated").hide();
68+
})
69+
return true;
70+
}
71+
</script>
5472
</body>
5573
</html>

0 commit comments

Comments
 (0)