Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

Securing a GWT app using Spring security

$
0
0

Spring security is one of the most used frameworks for securing java web applications. While it remains biased towards Servlets and server side java applications, it can also be used to secure single page applications like GWT based applications. In this post, we will go through an example of how to secure a GWT application using Spring Security.

SPA vs Server Side:

The main difference between a single page application and a server side application is where the rendering and routing is done. A server side application in java makes use of Servlets (or JSPs) to render views, so a transition from one view to another is done through the server, which makes it possible to filter and check each view/context change. For a single page application, the rendering and the routing is done through a script which loaded only once, so there is no way for the server to know which route/view the user is in, unless explicitly done by the developer. Because of the asynchronous nature of Ajax, a SPA would not know what to do with a redirect response (HTTP 302), so it is up to the developer to manually handle redirects by changing the URL.

Securing an SPA:

There are basically two ways to secure a single page application: either stateless or stateful authentication. In stateless authentication, the server does not keep track of the application state and no session is opened and maintained. This is common for applications where the server side is a Rest API. Stateless authentication can be done using OAuth or even HTTP basic authentication. In the stateful authentication, the server keeps track of the application state using a session. This done using cookies and HTTP headers. In the next example, we are going to implement stateful authentication to demonstrate how all this works.

Example: securing a GWT application using Spring security

In this example, we will secure the gwt-polymer-starter app which we are going to generate using the gwt-polymer-starter archetype. We will use a maven project with one server side module and one client side module: gwt-spring-security-server, gwt-spring-security-client.

Prerequisites :

GWT 2.8

net.ltgt.gwt.maven plugin for maven

Spring Security 4.2 dependencies

Spring MVC 4.3.4

Before getting started, let’s do an inventory of the resources that we want to secure:

GwtSpringSecurity.html is the page where our GWT app is loaded

GwtSpringSecurity.nocache.js which is our GWT script

in fact we want to secure all the GwtSpringSecurity directory which contains all our GWT genrated scripts.

We want to have a login page which, upon successful authentication, would redirect to our application, and in case a non authenticated user tries to access the GwtSpringSecurity.html page, he needs to be redirected to the login. Let’s configure security.

Spring security provides an easy mean for configuring security as a Java class by extending WebSecurityConfigurerAdapter :

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("gwidgets").password("gwidgets").roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/GwtSpringSecurity.html", "/GwtSpringSecurity/**", "/user").authenticated()
.and().formLogin().defaultSuccessUrl("/GwtSpringSecurity.html")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login")
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}

Explanation:

In this configuration, we configured the resources that need authentication. We also told spring security that we are going to use its default login page so that we can have csrf protection for free, and where to redirect after a successful authentication. We also configured the logout endpoint.

Because our application is a demo, we used in memory authentication to store users details. This configuration can be customized to fit the application need. For example, it can be configured to retrieve user details from a directory or a database.

Finally, we have set our protection against cross-site request forgery (explanation of what csrf is can be found in spring docs ) using CookieCsrfTokenRepository because we want to be able to access the csrf token from our javascript. Using JSPs and Servlets, the csrf token is set from the server side by Spring Security. From a GWT app, there is no way to acess the csrf token, as our app resides in the front end, so the solution is to use a cookie that can be accessed from our Javascript and sent as a header (the convention for the header is X-XSRF-TOKEN) with requests. The reason for calling the .withHttpOnlyFalse(), is that we want our csrf cookie to be accessed from Javascript

. We will see later on the use of the csrf cookie.

That’s pretty much all what we need to configure security for our app.

Displaying the current user on our GWT app :

Ideally, we would want our GWT app to display the name of the current logged in user. In order to so, we need to add an endpoint to retrieve the current user. Starting from version 3.2, Spring Security introduced a useful annotation that allows to bind the current user to a request mapping: @AuthenticationPrincipal .

@Controller
public class AppController {
@RequestMapping("/user")
public ResponseEntity user(@AuthenticationPrincipal User user) {
return new ResponseEntity(user.getUsername(), HttpStatus.OK);
}
}

From the client side, we are going to add a HTTP call to the /user end point:

RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "/user");
try {
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
}
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
currentUser.setInnerText(response.getText());
}
}
});
} catch (RequestException e) {
}

The logout issue:

We defined our /logout endpoint that should be called ( both GET and POST are accepted by Spring Security) when we want to leave our application. When we try to log out by calling this endpoint , we get this message on the console :


Securing a GWT app using Spring security

Because we did not include our csrf code with our request, Spring rejects it as a protection. Let’s inspect the cookies in the browser:


Securing a GWT app using Spring security

In addition to the JSESSIONID which is used to keep track of the session, there is an XSRF-TOKEN cookie which contains the token to be submited with our logout request as a header. Now we need to adjust our request to include this code in a header named X-XSRF-TOKEN.

RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, "/logout");
String crsfCookie = Cookies.getCookie("XSRF-TOKEN");
builder.setHeader("X-XSRF-TOKEN", crsfCookie);

We have resolved the csrf token issue, but there is still another issue that arises when the /logout endpoint is called: our javascript does not know how to handle a 302 HTTP status which literally means redirect towards the /login page. As a result, we are still on the same page, even after clicking on the logout, as shown here in Chrome console:


Securing a GWT app using Spring security

We have received a 302 status for redirecting, in addition to the login page as text/html, but javascript would not know what to do with such a response unless explicitely specified by the developper. The workaround for this issue is to manually change the url to /login when receiving the 302, something like:

Window.Location.replace("/login");

Final thoughts:

We have seen how to provide basic security to a GWT application using Spring Security. Compared to other JavaScript frameworks like Angular Js, GWT generates everything from one script, so it would be meaningless trying to check security after each route change because in any case the app script is already (down)loaded in the browser, unlike Angular Js which can have page parts as different .html and .js files.

In this post, we explored stateful authentication which makes use sessions and cookies to keep track of the current state of the application. We have also seen how to workaround the csrf issue that can arise for a GWT app and for single page applications in general. We will explore stateless application in future posts.

Links:

source code: https://github.com/zak905/gwt-spring-security

Intersting video from SpringOne about new features in Spring 4.1: https://www.youtube.com/watch?v=H94Wbd8ARKM&t=3009s

How to use Spring Security with Angular Js: https://spring.io/guides/tutorials/spring-security-and-angular-js/


Viewing all articles
Browse latest Browse all 12749

Trending Articles