DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Leveraging Salesforce Using Spring Boot
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • RESTful Web Services: How To Create a Context Path for Spring Boot Application or Web Service
  • How To Validate HTTP Post Request Body - Restful Web Services With Spring Framework | Spring Boot

Trending

  • Using Python Libraries in Java
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • The Smart Way to Talk to Your Database: Why Hybrid API + NL2SQL Wins
  • Enforcing Architecture With ArchUnit in Java
  1. DZone
  2. Coding
  3. Frameworks
  4. Securing REST Services With OAuth2 in Spring Boot

Securing REST Services With OAuth2 in Spring Boot

Want to learn more about securing REST services? Check out this tutorial on how to secure REST with OAuth2 and Spring Boot.

By 
Jesus J. Puente user avatar
Jesus J. Puente
·
Oct. 23, 18 · Tutorial
Likes (20)
Comment
Save
Tweet
Share
178.8K Views

Join the DZone community and get the full member experience.

Join For Free

In this post, I will explain how we can provide security for REST services in Spring Boot. The example application is the same as the previous WEB security entry (Spanish version; English version). The source code can be found on GitHub.

Explaining the OAuth2 Technology

As I said, we will use the OAuth2 protocol, so the first thing will be to explain how this protocol works.

OAuth2 has some variants, but I am going to explain what I will use in the program, and for this, I will give you an example so that you understand what we intend to do.

I will put a daily scene: payment with a credit card in a store. In this case, there are three partners: the store, the bank, and us. Something similar happens in the OAuth2 protocol. These are the steps:

  1. The client, or the buyer, asks the bank for a credit card. Then, the bank will collect our information, verify who we are, and provide us a credit card, depending on the money we have in our account or if it tells us not to waste time. In the OAuth2 protocol that grants the cards, it is called the Authentication Server.
  2. If the bank has given us the card, we can go to the store, i.e. the web server, and we present the credit card. The store does not owe us anything, but they can ask the bank, through the card reader, if they can trust us and to what extent (the credit balance). The store is the Resource Server.
  3. The store, depending on the money that the bank says we have, will allow us to make purchases. In the OAuth2 analogy, the web server will allow us to access  pages, depending on our financial status.

In case you have not noticed that authentication servers are usually used, when you go to a webpage and are asked to register, but as an option, it lets you do it through Facebook or Google, you are using this technology. Google or Facebook becomes the 'bank' that issues the 'card.' The webpage that asks you to register will use it to verify that you have 'credit' and let you enter.

Image title

Here, you can see the website of the newspaper "El Pais" where you can create an account. If we use Google or Facebook, the newspaper (the store) will rely on what those authentication providers tell them. In this case, the only thing the website needs is for you to have a credit card — regardless of the balance

Creating an Authorization Server

Now, let's see how we can create a bank, store, and everything else that you need.

Image title

First, in our project, we need to have the appropriate dependencies. We will need the starters: Cloud OAuth2, Security, and Web.

Well, let's start by defining the bank; this is what we do in the class:  AuthorizationServerConfiguration:

  @Configuration
 @EnableAuthorizationServer
 public class AuthorizacionServerConfiguration extends AuthorizationServerConfigurerAdapter {

   @Autowired
   @Qualifier ("authenticationManagerBean")
   private AuthenticationManager authenticationManager;

   @Autowired
   private TokenStore tokenStore;

 @Override
 public void configure (ClientDetailsServiceConfigurer clients) throws Exception {
 clients.inMemory ()
     .withClient ("client")
             .authorizedGrantTypes ("password", "authorization_code", "refresh_token", "implicit")
             .authorities ("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT", "USER")
             .scopes ("read", "write")
             .autoApprove (true)        
             .secret (passwordEncoder (). encode ("password"));          
 }

  @Bean
     public PasswordEncoder passwordEncoder () {
         return new BCryptPasswordEncoder ();
     }
  @Override
  public void configure (AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints
              .authenticationManager (authenticationManager)        
              .tokenStore (tokenStore);
  }

   @Bean
   public TokenStore tokenStore () {
       return new InMemoryTokenStore ();
   }
 }


We start the class by entering it as a configuration with the  @ Configuration label and then use the  @EnableAuthorizationServer tag to tell Spring to activate the authorization server. To define the server properties, we specify that our class extends the  AuthorizationServerConfigurerAdapter, which implements the  AuthorizationServerConfigurerAdapterinterface, so Spring will use this class to parameterize the server.

We define an  AuthenticationManager type bean that Spring provides automatically, and we will collect with the @Autowiredtag. We also define a  TokenStoreobject, but to be able to inject it, we must define it, which we do in the publicfunction  TokenStore tokenStore().

The  AuthenticationManager, as I said, is provided by Spring, but we will have to configure it ourselves. Later, I will explain how it is done. The TokenStoreor  IdentifierStoreis where the identifiers that our authentication server is supplying will be stored, so when the resource server (the store) asks for the credit on a credit card, it can respond to it. In this case, we use the  InMemoryTokenStoreclass that will store the identifiers in memory. In a real application, we could use a JdbcTokenStoreto save them in a database so that if the application goes down, the clients do not have to renew their credit cards.

In the function configure (ClientDetailsServiceConfigurer clients),we specify the credentials of the bank, I say of the administrator of authentications, and the services offered. Yes, in plural, because to access the bank, we must have a username and password for each of the services offered. This is a very important concept: the user and password is from the bank, not the customer. For each service offered by the bank, there will be a single authentication, although it may be the same for different services.

I will detail the lines:

  •  clients.inMemory ()specifies that we are going to store the services in memory. In a 'real' application, we would save it in a database, an LDAP server, etc.
  •  withClient ("client")is the user with whom we will identify in the bank. In this case, it will be called 'client.' Would it have been better to call him 'user?'
  • To uthorizedGrantTypes ("password", "authorization_code", "refresh_token", "implicit") , we specify  services that configure for the defined user, for 'client.' In our example, we will only use the password service.
  •  authorities ("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT", "USER")  specifies roles or groups contained by the service offered. We will not use it in our example either, so let's let it run for the time being.
  •  scopes ("read", "write")  is the scope of the service — nor will we use it in our application.
  •  autoApprove (true) — if you must automatically approve the client's requests, we'll say yes to make the application simpler.
  •  secret (passwordEncoder (). encode ("password"))  is the password of the client. Note that the encode function that we have defined a little below is called to specify with what type of encryption the password will be saved. The encode function is annotated with the @Beantag because Spring, when we supply the password in an HTTP request, will look for a  PasswordEncoderobject to check the validity of the delivered password.

And finally, we have the function  configure (AuthorizationServerEndpointsConfigurer endpoints)  where we define which authentication controller and store of identifiers should use the end points. Clarify that the end points are the URLs where we will talk to our 'bank' to request the cards.

Now, we have our authentication server created, but we still need the way that he knows who we are and puts us in different groups, according to the credentials introduced. Well, to do this, we will use the same class that we use to protect a webpage. If you have read the previous article (in Spanish), remember that we created a class that inherited from the  WebSecurityConfigurerAdapter, where we overwrote the function  UserDetailsService userDetailsService ().

  @EnableWebSecurity
 public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  ....
     @Bean
     @Override
     public UserDetailsService userDetailsService () {

     UserDetails user = User.builder (). Username ("user"). Password (passwordEncoder (). Encode ("secret")).
     roles ("USER"). build ();
     UserDetails userAdmin = User.builder (). Username ("admin"). Password (passwordEncoder (). Encode ("secret")).
     roles ("ADMIN"). build ();
         return new InMemoryUserDetailsManager (user, userAdmin);
     }
 ....
 }


Well, users with their roles or groups are defined in the same way. We should have a class that extends  WebSecurityConfigurerAdapter and define our users.

Now, we can check if our authorizations server works. Let's see how using the excellent PostManprogram.

To speak with the 'bank' to request our credentials, and as we have not defined otherwise, we must go to the URI "/oauth/token." This is one of the end points I spoke about earlier. There is more, but in our example, since we are only going to use the service 'password,' we will not use more.

We will use an HTTP request type POST, indicating that we want to use basic validation. We will put the user and password, which will be those of the "bank," in our example, using 'client' and 'password' respectively.

Image title

In the body of the request and in form-url-encoded format, we will introduce the service to request, our username, and our password.

Image title

And, we launched the petition, which should get us an exit like this:

Image title

That 'access_token' " 8279b6f2-013d-464a-b7da-33fe37ca9afb " is our credit card and is the one that we must present to our resource server (the store) in order to see pages (resources) that are not public.

Creating a Resource Server (ResourceServer)

Now that we have our credit card, we will create the store that accepts that card.

In our example, we are going to create the server of resources and authentication in the same program with Spring Boot, which will be in charge without having to configure anything. If, as usual in real life, the resource server is in one place and the authentication server in another, we should indicate to the resource server which is our 'bank' and how to talk to it. But, we'll leave that for another entry.

The only class of the resource server is  ResourceServerConfiguration:

  @EnableResourceServer
 @RestController
 public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter
 {
 .....
 }


Observe the @EnableResourceServerannotation that will cause Spring to activate the resource server. The tag  @RestControlleris because, in this same class, we will have the resources, but they could be perfectly in another class.

Finally, note that the class extends  ResourceServerConfigurerAdapter. This is because we are going to overwrite methods of that class to configure our resource server.

As I said before, since the authentication and resources server is in the same program, we only have to configure the security of our resource server. This is done in the function:

  @Override
 public void configure (HttpSecurity http) throws Exception {
 http
 .authorizeRequests (). antMatchers ("/ oauth / token", "/ oauth / authorize **", "/ publishes"). permitAll ();  
 // .anyRequest (). authenticated (); 

 http.requestMatchers (). antMatchers ("/ private") // Deny access to "/ private"
 .and (). authorizeRequests ()
 .antMatchers ("/ private"). access ("hasRole ('USER')") 
 .and (). requestMatchers (). antMatchers ("/ admin") // Deny access to "/ admin"
 .and (). authorizeRequests ()
 .antMatchers ("/ admin"). access ("hasRole ('ADMIN')");
 }


In the previous entry, when we defined the security on the web, we explained a function called  configure (HttpSecurity http). How is it much like this? Well, it is basically the same, and in fact, it receives an  HttpSecurity object that we must configure.

I explain this code line to line:

  • http.authorizeRequests().antMatchers("/oauth/token", "/oauth/authorize**", "/publica").permitAll() allow all requests to "/ oauth / token", "/ oauth / authorize ** "," / publishes "without any validation."
  • anyRequest().authenticated() This line is commented. If not, all of the resources would be accessible only if the user has been validated.
  • requestMatchers().antMatchers("/privada") Deny access to the url "/ private"
  • authorizeRequests().antMatchers("/privada").access("hasRole('USER')") allow access to "/ private" if the validated user has the role 'USER'
  • requestMatchers().antMatchers("/admin") Deny access to the url "/ admin"
  • authorizeRequests().antMatchers("/admin").access("hasRole('ADMIN')") allow access to "/ admin" if the validated user has the role 'ADMIN'

Once we have our resource server created, we must only create services, which is done with these lines:

  @RequestMapping ("/ publishes")
   public String publico () {
    return "Public Page";
   }
   @RequestMapping ("/ private")
   public String private () {
          return "Private Page";
   }
   @RequestMapping ("/ admin")
   public String admin () {
     return "Administrator Page";
   }


As you can see, there are three basic functions that return their corresponding Strings.

Let's see now how validation works.

First, we check that we can access "/publica" without any validation:

Image title

Right. This works!!

If I try to access the page "/private," I receive an error "401 unauthorized," which indicates that we do not have permission to see that page, so we will use the token issued by our authorizations server for the user 'user' to see what happens.

Image title

If we can see our private page, then let's try the administrator's page:

Image title


Right, we cannot see it. So, we're going to request a new token from the credential administrator, but identify ourselves with the user 'admin.'

The token returned is: " ab205ca7-bb54-4d84-a24d-cad4b7aeab57." We use it to see what happens.

Well, that's it! We can go shopping safely! Now, we just need to set up the store and have the products.

And, that's it. See you in the next article!

Spring Framework authentication security Web Service REST Web Protocols Spring Boot

Published at DZone with permission of Jesus J. Puente. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Leveraging Salesforce Using Spring Boot
  • Spring Boot - How To Use Native SQL Queries | Restful Web Services
  • RESTful Web Services: How To Create a Context Path for Spring Boot Application or Web Service
  • How To Validate HTTP Post Request Body - Restful Web Services With Spring Framework | Spring Boot

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!