Spring Security: Implementation Basics

Sabyasachi Mohanty
6 min readApr 17, 2021

… Continuing from this

After reading, you will —

  1. … be able to create a basic secured app.
  2. … understand the basics of the various methods and classes used.
  3. … understand why not to use this example in production.

Let’s try to go step by step and build a secured app —

Step 1: Add spring security starter dependency to pom.xml and run the web application —

Spring security starter dependency

Step 2: Post step 1, run the server and try to hit any endpoint. I bet you will see the following message —

Login page

But the problem is, I didn't do anything in my code, to get this login page. I don't even have an HTML page. It’s not magic, it's spring security. Only by adding spring security starter dependency to the pom.xml, the whole application is protected by a username and password.

But now what is my username and password? How do I log in?

Step 3: Getting the password — Re-run the app again and keep an eye on the server logs. You shall see something like this —

An autogenerated password in the log

This is a password auto-generated by spring every time the server is started. Now we have our password, but what is the username? Spring always use the username as “user”.

Step 4: Let’s now try to enter “user” as username and the auto-generated string as our password —

Voila! It works!

Upon entering the creds, we will see the request is getting processed.

Step 5: Now, if we want to use our own username and password, what now?

User-defined credentials

By adding these details in the application.properties file, spring-security would no longer generate its auto-generated password. Thus using these credentials, one can log in to the application.

Step 6: Now how do we log out? — spring-security also takes care of this through http://<hostname>/logout Eg. http://localhost:9092/logout

Is there any other way to pass these creds to Spring Security? What if I have multiple users?

The answer is Yes! There is another way of achieving this. Let’s discuss that —

Step 7: Comment the lines that you have just added in step 6 to your application.properties file.

Step 8: Let’s create a class and name it “SecurityConfiguration” (You can give any name). This extends “WebSecurityConfigurerAdapter” class.

Custom security configuration class

“WebSecurityConfigurerAdapter” comes with many methods but of all these, we just need two methods to satisfy our purpose —

1. public void configure(AuthenticationManagerBuilder auth) throws Exception — this will be used to pass our user credentials and also helps during authentication.

2. protected void configure(HttpSecurity http) throws Exception — this will be used for authorization purpose.

Step 9: Defining the first method of step 8 —

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("root")
.password("root")
.roles("USER")
.and()
.withUser("admin")
.password("admin")
.roles("ADMIN");
}

So far, we have not created any classes which would take care of the authentication. We are simply passing our configurations to the spring security classes, asking it to use them instead of the default configs for authentication. This is the reason after step 5, upon restarting the server, you will observe spring security isn't creating any password for us and the auto-generated security password is missing.

What are we doing in this method? — AuthenticationManagerBuilder class is the main class that tells us who is a legitimate user of the web app. More on this you will find here. So using the reference variable auth, we are building the object.

First, we are telling spring security that this is an in-memory authentication i.e. there is no involvement of an external data store. Next, by adding .withUser(“root”), we are passing the username of the user and with .password(“root”), we are passing the password of the user. .roles(“USER”) helps in passing the role of the user. If there are multiple users for the web app, then they all can be joined using .and().

Step 10: Adding Authorization — this is pretty straight forward.

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/users").hasAnyRole("USER", "ADMIN")
.antMatchers("/").permitAll()
.and().formLogin();
}

Here, we are overriding another configure method that uses HttpSecurity as a parameter. In this method, we are telling spring security one user must have sufficient roles to access certain endpoints.

.antMatchers(“/admin”).hasRole(“ADMIN”)” — Here, using antMatchers we are defining the pattern of the endpoint and only a user having the role of an admin can access this endpoint. We can define multiple roles for a single endpoint using hasAnyRole(“…”).

permitAll() — this is used when we want the endpoint to be accessible for all. Pattern — “/**” refers to all the endpoints of the web app.

The whole chaining has to be written from most to least restrictive. Spring security checks one by one from the top and compares the request URL with the patterns passed. If it matches, it then checks the user’s role with the role passed with the endpoint pattern. If the role mismatch happens, then an exception is raised, else the user is allowed to access it.

Step 11: Password Encoders —

@Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}

PasswordEncoder — this is important as it encodes the password in an incoming request to the format defined here and therefore, a bean class needs to be defined.

Generally, passwords must not be stored anywhere in the app in text format. It must always be encoded. Even in databases as well, passwords must be encoded as users use the same password for different accounts. For the trial purpose I am returning NoOpPasswordEncoder.getInstance(); through the password encoder method. This keeps the password in text format. You will find this to be struck out and this is not because it's deprecated, rather spring security is trying to discourage us from using it.

Step 12: @EnableWebSecurity — we have to add this tag on top of the class.

Enable spring-security

What if we write this? — this will tell spring security that we have defined our own configurations to authenticate into the application.

What if we do not write this? — Spring security would not know if we have defined a custom security class and thus will use its default configurations. Upon running the server, in the server log, you shall see the auto-generated security password.

Step 13: Run the server and try it out!

Observations —

  1. We performed an in-memory authentication — there was no involvement of an external data source.
  2. Clearly, this is not production-ready. As in production we wouldn’t be seeing one or two or say, ten users. There will be multiple users having multiple roles. Also, even if we have just ten users, adding it here in the code, is barbaric.
  3. User addition and role editing should be dynamic and on the go. Performing a code change and deploying it every time a new user is added or role change happens, is simply unimaginable.
  4. Spring security discourages its developers from keeping the password in text format, yet we have kept it. So, it's a big NO-NO!

Next post, I will show how to connect an external data source and store the user details there, explain various classes and methods that I use for this and how do they collaborate and of course, about password encoding as well.

Big thanks to JavaBrains: https://www.youtube.com/channel/UCYt1sfh5464XaDBH0oH_o7Q

--

--