RSS

Spring Security Basics

30 Mar

Episode 63

Welcome to the next installment of the series about Spring-based web applications development. So far we have covered Angular JS fronted, Spring core, webservices, database access and unit tests. Today we are going to take care of our application security – basic authentication and authorization, users, roles, custom login form and method level security.

70d42d4aaa6aede4b84bde43e3dead63

Spring Security project started as Acegi security around 2004 and initially focused on custom authorization, using standard Java Enterprise Edition container managed authentication. Version 1.0.0 became official Spring sub-project in 2006 and year later was re-branded to Spring Security. Say hello to Alice, Bob and Eve.

Foundations

We should briefly clarify some security terminology, which might sometimes by confusing:

Identification is stating a subject identity, like user name, without yet providing any proof for that (Hi, I’m Alice).

Authentication is a process of proving that the subject’s identity is true, for example by providing a password associated with the user (I’m Bob and I can prove that).

Authorization refers to granting rights to resources and depends upon successful authentication in the first place (Eve is an administrator, so she has access to admin panel. Seems legit!).

We will now look at basic authentication using passwords. To be secure in production environment, the communication should be performed over HTTPS. Otherwise, anyone can intercept username / password and use them later to impersonate the poor user. As usual, the code is available on my GitHub repository. This tag refers to current state of the project.

Hello Secure World!

We will start with adding spring-boot-starter-security dependency to our pom.xml:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

When we restart the application and go to localhost:8080/SpringAngularIntro/start, we will be prompted for user name and password. Wait, what? There are no users yet! Well actually, there is one. Spring creates a default user with name “user” and displays password in console upon application startup. Look for line like this: “Using default security password: 29da1a27-b102-4be1-ba8e-1643b8fdca72”. Let’s use those credentials in the basic authentication pop-up, and we should be able to access the page.

What if the authentication pop-up does not appear the second time we try to access the url? Don’t panic, we can clear the browser’s cache (for example: ctrl + shift + del in Firefox). We can also provide user and password explicitly in url, in the following manner: user:password@url.

castle_gate_by_jonathandufresne-d7uxbmw

That’s the default security. If we want something else, we should add new configuration class, for example:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests().anyRequest().permitAll();
	}
}

@Configuration is a familiar annotation we talked about in Spring Core episode. @EnableWebSecurity does pretty much what the name says. Extending WebSecurityConfigurerAdapter gives us access to method and objects we can use to change default security policy. In particular we will override the configure method and use injected HttpSecurity object. Here we state, that we want to permit all requests.

Not very useful yet. Let’s add two other html pages (content is not really important) to our FrontendController:

@RequestMapping("/admin")
public ModelAndView admin(Model model) {
	return new ModelAndView("/Admin.html");
}

@RequestMapping("/user")
public ModelAndView user(Model model) {
	return new ModelAndView("/User.html");
}

We can now modify the configure method to grant access to everyone to the start page, but restrict admin page to authenticated users (only our default user as for now).

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests().antMatchers("/start").permitAll();
	http.authorizeRequests().antMatchers("/admin").authenticated();
	http.httpBasic();
}

Users and Roles

To add users to our system we can override the configureGlobal method of WebSecurityConfigurerAdapter:

@Override
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
	auth.inMemoryAuthentication()
		.withUser("admin").password("admin1").roles("ADMIN")
		.and()
		.withUser("user").password("user1").roles("USER");
}

We are defining two users, their passwords and roles using simple inMemoryAuthentication. Choice that is more realistic might be jdbcAuthentication, but let’s skip it for the sake of simplicity. We can now modify the configure method again and specify which role is required to access given resource.

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests().antMatchers("/start").permitAll();
	http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN");
	http.authorizeRequests().antMatchers("/user").hasRole("USER");
	http.httpBasic();
}

fantasy_castle_by_jbrown67-d7mu200

Login Form

Basic authentication pop-up is not especially elegant. We might want to use our own beautiful login page. In order to do that, let’s modify the configure method again:

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests().anyRequest().authenticated();
	http.formLogin().loginPage("/login").permitAll();
	http.logout().permitAll();
	http.csrf().disable();
}

We specify login page url and permit access to login and logout to everyone.  The default url for logout is simply “/logout”. To simplify the form, we will disable CSRF support (don’t do it production code). We need another mapping in FrontendController:

@RequestMapping("/login")
public ModelAndView login(Model model) {
	return new ModelAndView("/Login.html");
}

As well as the Login.html form:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form action="login" method="post">
<div>User Name : <input type="text" name="username"/></div>
<div>Password: <input type="password" name="password"/></div>
<div><input type="submit" value="Sign In"/></div>
</form>

</body>
</html>

Which should contain username and password and submit a post request to login page.

Methods Security

Instead of securing urls, we can apply policies to methods. Let’s add a new method in our WebServiceController, we talked about in Spring webservices episode:

@PreAuthorize("hasRole('ADMIN')")
@RequestMapping("/securegreeting")
public Greeting getSecureGreeting() {
	return new Greeting("Hello!");
}

We can use SpEL  expressions to define access policy as the annotation parameter. In order for this to work, we need to add @EnableGlobalMethodSecurity(prePostEnabled = true) annotation on our SecurityConfiguration class.

What’s Next

Aside from urls and methods, Spring also provides fine grained access control to objects fields. Plethora of authentication mechanisms are supported, including standard digest authentication, X.509 certificates, LDAP, OpenID, OAuth, CAS, JAAS, Kerberos, Jasypt or Atlassian Crowd.

In the next week, we will most likely leave Spring for a while, and get back to Amazon Web Services. Stay tuned and remember about the new “subscribe” widget ;)

BERK2

Image sources:

 
Leave a comment

Posted by on March 30, 2017 in Spring, Technology

 

Tags: , , ,

Leave a comment