mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-16 14:33:09 +00:00
Update HAL browser to use DSPACE-XSRF-TOKEN header and store token in custom MyHalBrowserCsrfToken Cookie. Minor comment fixes to TokenRepo
This commit is contained in:
@@ -30,9 +30,9 @@ import org.springframework.web.util.WebUtils;
|
||||
*
|
||||
* How it works:
|
||||
*
|
||||
* 1. Backend generates XSRF token & stores in a *server-side* cookie named DSPACE-XSRF-COOKIE. This cookie is
|
||||
* only readable to clients on the same domain. But, it is returned (by user's browser) on every subsequent request
|
||||
* to backend. See "saveToken()" method below.
|
||||
* 1. Backend generates XSRF token & stores in a *server-side* cookie named DSPACE-XSRF-COOKIE. By default, this cookie
|
||||
* is not readable to JS clients (HttpOnly=true). But, it is returned (by user's browser) on every subsequent
|
||||
* request to backend. See "saveToken()" method below.
|
||||
* 2. At the same time, backend also sends the generated XSRF token in a header named DSPACE-XSRF-TOKEN to client.
|
||||
* See "saveToken()" method below.
|
||||
* 3. Client MUST look for DSPACE-XSRF-TOKEN header in a response from backend. If found, the client MUST store/save
|
||||
@@ -148,14 +148,6 @@ public class DSpaceCsrfTokenRepository implements CsrfTokenRepository {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Second, verify either the header or param has been sent. This is a customization for DSpace.
|
||||
// Because the server-side cookie is ALWAYS sent back, we need to verify the client has also sent the token in
|
||||
// some other way. This ensures that we only *change* the Token when it has been used or attempted to be used.
|
||||
//if (!StringUtils.hasLength(request.getHeader(this.headerName)) &&
|
||||
// !StringUtils.hasLength(request.getParameter(this.parameterName))) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// If we got here, we know a token exists in the cookie and *either* the header or the parameter.
|
||||
// So, this just sends the token info back so that it can be validated by Spring Security.
|
||||
return new DefaultCsrfToken(this.headerName, this.parameterName, token);
|
||||
|
@@ -75,7 +75,7 @@
|
||||
<tr>
|
||||
<td><strong><%= HAL.truncateIfUrl(rel) %></strong></td>
|
||||
<td><%= link.title || '' %></td>
|
||||
<td><%= link.name ? 'name: ' + link.name : 'index: ' + i %></a></td>
|
||||
<td><%= link.name ? 'name: ' + link.name : 'index: ' + i %></td>
|
||||
<td>
|
||||
<% if (HAL.isUrl(rel)) { %>
|
||||
<a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>"><i class="icon-book"></i></a>
|
||||
@@ -250,6 +250,7 @@ Content-Type: application/json
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<!-- Customized (to use WebJars) for DSpace -->
|
||||
<script src="webjars/jquery/dist/jquery.min.js"></script>
|
||||
<script src="browser/vendor/js/underscore.js"></script>
|
||||
<script src="browser/vendor/js/backbone.js"></script>
|
||||
@@ -260,6 +261,7 @@ Content-Type: application/json
|
||||
<script src="browser/js/hal.js"></script>
|
||||
<script src="browser/js/hal/browser.js"></script>
|
||||
|
||||
<!-- Customized for DSpace -->
|
||||
<script src="js/hal/http/client.js"></script>
|
||||
<script src="browser/js/hal/resource.js"></script>
|
||||
|
||||
|
@@ -12,23 +12,33 @@ HAL.Http.Client = function(opts) {
|
||||
this.defaultHeaders = {'Accept': 'application/hal+json, application/json, */*; q=0.01'};
|
||||
var authorizationHeader = getAuthorizationHeader();
|
||||
authorizationHeader ? this.defaultHeaders.Authorization = authorizationHeader : '';
|
||||
// If we find a CSRF header (in a cookie), send it back in X-XSRF-Token header
|
||||
var csrfToken = getCSRFToken();
|
||||
csrfToken ? this.defaultHeaders['X-XSRF-Token'] = csrfToken : '';
|
||||
// Write all headers to console (for easy debugging)
|
||||
console.log(this.defaultHeaders);
|
||||
//console.log(this.defaultHeaders);
|
||||
this.headers = this.defaultHeaders;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get CSRF Token by parsing it out of the DSPACE-XSRF-COOKIE (server-side) cookie set by our DSpace server webapp
|
||||
* Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie.
|
||||
* This cookie is set in login.html after a successful login occurs.
|
||||
**/
|
||||
function getCSRFToken() {
|
||||
var cookie = document.cookie.match('(^|;)\\s*' + 'DSPACE-XSRF-COOKIE' + '\\s*=\\s*([^;]+)');
|
||||
if(cookie != undefined) {
|
||||
var cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserCsrfToken' + '\\s*=\\s*([^;]+)');
|
||||
if (cookie != null) {
|
||||
return cookie.pop();
|
||||
} else {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current response headers to see if the CSRF Token has changed. If a new value is found in headers,
|
||||
* save the new value into our "MyHalBrowserCsrfToken" cookie.
|
||||
**/
|
||||
function checkForUpdatedCSRFTokenInResponse(jqxhr) {
|
||||
// look for DSpace-XSRF-TOKEN header & save to our MyHalBrowserCsrfToken cookie (if found)
|
||||
var updatedCsrfToken = jqxhr.getResponseHeader('DSPACE-XSRF-TOKEN');
|
||||
if (updatedCsrfToken != null) {
|
||||
document.cookie = "MyHalBrowserCsrfToken=" + updatedCsrfToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +48,13 @@ function getCSRFToken() {
|
||||
**/
|
||||
function getAuthorizationHeader() {
|
||||
var cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserToken' + '\\s*=\\s*([^;]+)');
|
||||
if(cookie != undefined) {
|
||||
if (cookie != null) {
|
||||
return 'Bearer ' + cookie.pop();
|
||||
} else {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function downloadFile(url) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
@@ -89,6 +100,9 @@ HAL.Http.Client.prototype.get = function(url) {
|
||||
},
|
||||
headers: this.headers,
|
||||
success: function(resource, textStatus, jqXHR) {
|
||||
// NOTE: A GET never requires sending an CSRF Token, but the response may send an updated token back.
|
||||
// So, we need to check if a token came back in this GET response.
|
||||
checkForUpdatedCSRFTokenInResponse(jqXHR);
|
||||
self.vent.trigger('response', {
|
||||
resource: resource,
|
||||
jqxhr: jqXHR,
|
||||
@@ -111,6 +125,18 @@ HAL.Http.Client.prototype.request = function(opts) {
|
||||
opts.dataType = 'json';
|
||||
opts.xhrFields = opts.xhrFields || {};
|
||||
opts.xhrFields.withCredentials = opts.xhrFields.withCredentials || true;
|
||||
opts.headers = opts.headers || {};
|
||||
// If CSRFToken exists, append as a new X-XSRF-Token header
|
||||
var csrfToken = getCSRFToken();
|
||||
if (csrfToken != null) {
|
||||
opts.headers['X-XSRF-Token'] = csrfToken;
|
||||
}
|
||||
|
||||
// Also check response to see if CSRF Token has been updated
|
||||
opts.success = function(resource, textStatus, jqXHR) {
|
||||
checkForUpdatedCSRFTokenInResponse(jqXHR);
|
||||
};
|
||||
|
||||
self.vent.trigger('location-change', { url: opts.url });
|
||||
return jqxhr = $.ajax(opts);
|
||||
};
|
||||
|
@@ -71,7 +71,13 @@
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var successHandler = function(result, status, xhr) {
|
||||
// look for Authorization header & save to a MyHalBrowserToken cookie
|
||||
document.cookie = "MyHalBrowserToken=" + xhr.getResponseHeader('Authorization').split(" ")[1];
|
||||
// look for DSpace-XSRF-TOKEN header & save to a MyHalBrowserCsrfToken cookie (if found)
|
||||
var csrfToken = xhr.getResponseHeader('DSPACE-XSRF-TOKEN');
|
||||
if (csrfToken!=null) {
|
||||
document.cookie = "MyHalBrowserCsrfToken=" + csrfToken;
|
||||
}
|
||||
toastr.success('You are now logged in. Please wait while we redirect you...', 'Login Successful');
|
||||
setTimeout(function() {
|
||||
window.location.href = window.location.pathname.replace("login.html", "");
|
||||
@@ -101,7 +107,9 @@
|
||||
beforeSend: function (xhr, settings) {
|
||||
// If CSRF token found in cookie, send it back as X-XSRF-Token header
|
||||
var csrfToken = getCSRFToken();
|
||||
xhr.setRequestHeader('X-XSRF-Token', csrfToken ? csrfToken : '');
|
||||
if (csrfToken != null) {
|
||||
xhr.setRequestHeader('X-XSRF-Token', csrfToken);
|
||||
}
|
||||
},
|
||||
success : successHandler,
|
||||
error : function(result, status, xhr) {
|
||||
@@ -139,14 +147,15 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSRF Token by parsing it out of the DSPACE-XSRF-COOKIE server-side cookie set by our DSpace server webapp
|
||||
* Get CSRF Token by parsing it out of the "MyHalBrowserCsrfToken" cookie.
|
||||
* This cookie is set in login.html after a successful login occurs.
|
||||
**/
|
||||
function getCSRFToken() {
|
||||
var cookie = document.cookie.match('(^|;)\\s*' + 'DSPACE-XSRF-COOKIE' + '\\s*=\\s*([^;]+)');
|
||||
if(cookie != undefined) {
|
||||
var cookie = document.cookie.match('(^|;)\\s*' + 'MyHalBrowserCsrfToken' + '\\s*=\\s*([^;]+)');
|
||||
if (cookie != null) {
|
||||
return cookie.pop();
|
||||
} else {
|
||||
return undefined;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +173,9 @@
|
||||
beforeSend: function (xhr, settings) {
|
||||
// If CSRF token found in cookie, send it back as X-XSRF-Token header
|
||||
var csrfToken = getCSRFToken();
|
||||
xhr.setRequestHeader('X-XSRF-Token', csrfToken ? csrfToken : '');
|
||||
if (csrfToken != null) {
|
||||
xhr.setRequestHeader('X-XSRF-Token', csrfToken);
|
||||
}
|
||||
},
|
||||
success : successHandler,
|
||||
error : function() {
|
||||
|
Reference in New Issue
Block a user