Spring Security in Action

Spring Security in Action

Spring Security in Action - Spring Security is a highly customizable authentication and access-control framework for Java applications, especially for Spring-based applications. This framework is also widely used in Java development.

Spring Security in Action - Spring Security is a highly customizable authentication and access-control framework for Java applications, especially for Spring-based applications. This framework is also widely used in Java development.

Why Do We Need Spring Security?

Spring Security is integrated with the most popular framework, Spring Boot. It supports both authentication and authorization, which are the most popular approaches to dealing with security issues between the server and client-end. Like all Spring-based projects, the real power of Spring Security is found in how easily it can be extended to meet customer requirements.

Spring Security in Action

In this example, we will go through a very basic Spring Security application. There are four important classes to be introduced — HttpSecurity, WebSecurityConfigurer, UserDetailsService, and AuthenticationManager. The final application will cover the following features:

  1. Username and password verification
  2. Role control
  3. Token distribution, using JWT
  4. Token verification
  5. Password crypto

Through the process of implementation, we will cover some fundamental principles of Spring Security.

Include Spring Security Dependencies

compile "org.springframework.boot:spring-boot-starter-security"

Token Distribution

The first thing to do is define a way to grant tokens. There are several grant types to accomplish this, including "password," " authorization<em>code," " implicit," and " client</em>credentials." In our example, we used " password" as the grant type.

@Override

public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {

configurer

.inMemory()//jdbc()

.withClient(clientId)

.secret(passwordEncoder.encode(clientSecret))

.authorizedGrantTypes("password")

.scopes("read", "write")

.resourceIds(resourceIds);

}

Normally, we don't use in-memory client id and secret. In production, we can configure to read all clients from the database, as shown below:

configurer.jdbc()

After that, we need to configure token settings, including:

  1. Token store
  2. Token converter
  3. Token manager
  4. Enhancer chain

In this example, we are using JWT to manage our tokens. JWT provides convenient APIs that closely integrate with Spring Security. There is a converter used to convert and decode tokens. All you need to do is to set a signing key and then set them in the config(AuthorizationServerEndpointsConfigurer endpoints) function.

@Bean

public JwtAccessTokenConverter accessTokenConverter() {

JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

converter.setSigningKey(signingKey);

return converter;

}


@Bean

public TokenStore tokenStore() {

return new JwtTokenStore(accessTokenConverter());

}

@Override

public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

TokenEnhancerChain enhancerChain = new TokenEnhancerChain();

enhancerChain.setTokenEnhancers(Collections.singletonList(accessTokenConverter));

endpoints.tokenStore(tokenStore)

.accessTokenConverter(accessTokenConverter)

.tokenEnhancer(enhancerChain)

.authenticationManager(authenticationManager);

}

Remember that the tokenService and authenticationManager must be the same one in token verification so that the token can be decoded properly.

Token Verification

Now, you are able to implement your own security policy. A popular way is to extend WebSecurityConfigurerAdapter and rewrite security control functions based on your customer's requirements. Listed below are the important functions:

  1. The passwordEncoder() function defines the way to encode and compare the passwords.
  2. The configure(HttpSecurity http) function sets the resource strategy.
@Override

public void configure(HttpSecurity http) throws Exception {

//@formatter:off

http

.requestMatchers()

.and()

.authorizeRequests()

.antMatchers("/public/**").permitAll() //no authorization required here

.antMatchers("/demo/**").authenticated(); //need authorization

//@formatter:on

}

  1. The configure(ResourceServerSecurityConfigurer resources) function defines the security strategy.

In this config() function, we need to assign a token service to explain tokens. A typical token service is defined below.

@Bean

@Primary

public DefaultTokenServices tokenServices() {

DefaultTokenServices defaultTokenServices = new DefaultTokenServices();

defaultTokenServices.setTokenStore(tokenStore());

defaultTokenServices.setSupportRefreshToken(true);

return defaultTokenServices;

}

  1. The authenticationManager() function defines the token verification logic. This class will be used to check user authentication when a token is refreshed.

Optional: Custom Token Authorization Verification Logic

If the default token manager does not meet your requirements, which is happening all the time, you could use your own authenticationProvider by using the code below:

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.authenticationProvider(jwtAuthenticationProvider());

}

And the provider can look like this:

@Override

public Authentication authenticate(Authentication authentication) throws 

AuthenticationException {

DecodedJWT jwt = ((JwtAuthenticationToken) authentication).getToken();

//I want a token never expired.

//if (jwt.getExpiresAt().before(Calendar.getInstance().getTime()))

//throw new NonceExpiredException("Token expires");

String clientId = jwt.getSubject();

UserDetails user = userService.loadUserByUsername(clientId);

if (user == null || user.getPassword() == null)

throw new NonceExpiredException("Token expires");

Algorithm algorithm = Algorithm.HMAC256(user.getPassword());

JWTVerifier verifier = JWT.require(algorithm)

.withSubject(clientId)

.build();

try {

verifier.verify(jwt.getToken());

} catch (Exception e) {

throw new BadCredentialsException("JWT token verify fail", e);

}

return new JwtAuthenticationToken(user, jwt, user.getAuthorities());

}

Optional: Custom Verification Chain

If the authenticate() function throws an exception, we may handle it or just record it in the database. To implement it, we can just set tokenValidSuccessHandler and tokenValidFailureHandler. In the handler, you can rewrite onAuthenticationSuccess and onAuthenticationFailure with your own logic.

...

.and()

.apply(new MyValidateConfigure<>())

.tokenValidSuccessHandler(myVerifySuccessHandler())

.tokenValidFailureHandler(myVerifyFailureHandler())

...

public class JwtAuthenticationFailureHandler implements AuthenticationFailureHandler {

@Override

public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponseresponse,

AuthenticationException exception) {

response.setStatus(HttpStatus.UNAUTHORIZED.value());

}

}

UserDetailService

UserDetailServiceis the core interface that loads user-specific data. We must realize the loadUserByUsername() function to locate the user and the user's role.

REST APIs

Once all the authorization configurations have been finished, you can start to enjoy developing your project. To control your role and access, you can simply add @PreAuthorize("hasAuthority('ADMIN_USER')") in your REST API declaration.

@RequestMapping(value = "/users", method = RequestMethod.GET)

@PreAuthorize("hasAuthority('ADMIN_USER')") //user with ADMIN_USER role have this access.

public ResponseEntity<List<User>> getUsers() {

return new ResponseEntity<>(userService.findAllUsers(), HttpStatus.OK);

}

Demo

1. Apply for a Token

Client-end can either use postman or curl to get a token.

curl client-id:[email protected]:8080/oauth/token -d grant_type=password -d username=admin.admin -d password=Test!123

You will get:

{"access_token":"eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsic3ByaW5nLXNlY3VyaXR5LWRlbW8tcmVzb3VyY2UtaWQiXSwidXNlcl9uYW1lIjoiYWRtaW4uYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTU0ODQ0NTQxLCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF9VU0VSIiwiQURNSU5fVVNFUiJdLCJqdGkiOiI4MTM3Y2Q4OS0wMWMyLTRkMTgtYjA4YS05MjNkOTcxYjNhYzQiLCJjbGllbnRfaWQiOiJjbGllbnQtaWQifQ.1t_4xVT8xaAtisHaNT_nMRBLKfpiI0SZQ2bbEGxu6mk","token_type":"bearer","expires_in":43199,"scope":"read write","jti":"8137cd89-01c2-4d18-b08a-923d971b3ac4"}

2. Use the Token in Role Control

Then use the token above to post a request which needs authorization.

curl http://localhost:8080/demo/users -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsic3ByaW5nLXNlY3VyaXR5LWRlbW8tcmVzb3VyY2UtaWQiXSwidXNlcl9uYW1lIjoiYWRtaW4uYWRtaW4iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTU0ODQ0NTQxLCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF9VU0VSIiwiQURNSU5fVVNFUiJdLCJqdGkiOiI4MTM3Y2Q4OS0wMWMyLTRkMTgtYjA4YS05MjNkOTcxYjNhYzQiLCJjbGllbnRfaWQiOiJjbGllbnQtaWQifQ.1t_4xVT8xaAtisHaNT_nMRBLKfpiI0SZQ2bbEGxu6mk"

It will return:

[{"id":1,"username":"jakob.he","firstName":"Jakob","lastName":"He","roles":[{"id":1,"roleName":"STANDARD_USER","description":"Standard User"}]},{"id":2,"username":"admin.admin","firstName":"Admin","lastName":"Admin","roles":[{"id":1,"roleName":"STANDARD_USER","description":"Standard User"},{"id":2,"roleName":"ADMIN_USER","description":"Admin User"}]}]

3. Verify the Token With JWT

Put your token and signing key in jwt.io; you will get the following result.

Let's quickly go over what we have done: We have introduced Spring Security and why we need to use it. We have also implemented a complete Spring Security application that included token management, token distribution, and REST APIs that are required for web authorization.

I hope this article is helpful!

Set up Web App with Spring Boot and Spring Security

Set up Web App with Spring Boot and Spring Security

Download the Spring Boot Web App Example Project. Run the Initial Web App. Add Project Dependencies for Your Spring Boot + Spring Security Web App. Understand Your Spring Boot App. Set Up Okta for OAuth 2.0 Single Sign-On. Configure Your Spring Boot App for Single SignOn (SSO) Refine Our Permissions

Developers know that securing web apps can be a pain. Doing it right is tough. The worst part is that “right” is a moving target. Security protocols change. Vulnerabilities are found in dependencies and patches are released. Tons of often complex boilerplate code has to be generated. The software-as-service paradigm has proliferated over the last decade, and while I love reinventing the wheel as much as the next developer (because, clearly, I’m gonna write it better than the yahoo they hired), security is an area where I’m happy to offload this work to specialists. Enter Okta.

In this tutorial, you’re going to use Spring Boot to build a simple web application with a user registration system and a login system. It will have the following features:

  • Login and registration pages
  • Password reset workflows
  • Restricting access according to group membership
Download the Spring Boot Web App Example Project

The first thing you’re going to need is a free Okta account. If you don’t already have one

The next thing will be to download the example project for this tutorial from GitHub.

git clone https://github.com/oktadeveloper/okta-spring-simple-app-example.git spring-app

This project uses Gradle, as the build tool, and the Thymeleaf templating system.

Run the Initial Web App

Once you have downloaded the example code from the GitHub repository, checkout out the Start tag using the following git command: git checkout tags/Start.

The app at this point it not protected at all. There is no authorization or authentication enabled (even though the necessary dependencies are included in the build.gradle file). Go ahead and run the example by opening a terminal and, from the project root directory, running the command ./gradlew bootRun (The bootRun command is a task provided by the Gradle Spring Boot plugin, added to the build.gradle file in the buildscript section at the top of the file).

Navigate to http://localhost:8080 in your favorite browser, and you should see this:

And if you click on the “Restricted” button:

Add Project Dependencies for Your Spring Boot + Spring Security Web App

The project dependencies are defined in the build.gradle file (see below). There’s a lot going on in this file, and this tutorial isn’t going to try and explain the Gradle build system to you. Feel free to check out their documentation. I just want to point out a few things.

First off, notice that we’re including the okta-spring-boot-starter. This project greatly simplifies integrating Okta with your Spring Boot application. It’s entirely possible to use Okta and Spring Boot without this starter. In fact, up to the point where Groups and Roles are introduced, the differences are minor (mostly involve application.yml changes). However, once you start to trying to integrate Groups and Roles, the Okta Spring Boot Starter saves a lot of coding. If you’d like to look a little deeper, take a look at the Okta Spring Boot Starter GitHub project.

The rest of the dependencies deal with Spring and Spring Boot. You’ll notice none of the org.springframework.bootdependencies have version numbers. This is because of some behind-the-scenes magic being done by the Spring io.spring.dependency-management Gradle plugin. The Spring Boot Version is set by the build script property springBootVersion near the top of the build.gradle file. Based on this version number, the Spring dependency management plugin decides what versions of dependencies to include.

We’re also bringing in the org.springframework.boot Gradle plugin, which adds the bootRun task that we’ll use to run the app.

  • spring-boot-starter-security and spring-boot-starter-web are core Spring Boot dependencies.
  • spring-security-oauth2-autoconfigure is required to use the @EnableOAuth2Sso annotation that we use to hook OAuth and Single Sign-On into our app.
  • spring-boot-starter-thymeleaf and thymeleaf-extras-springsecurity4 bring in the Thymeleaf templating system and integrate it with Spring Security.
buildscript {  
   ext {  
      springBootVersion = '2.0.5.RELEASE'  
  }  
   repositories {  
      mavenCentral()  
   }  
   dependencies {  
      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")  
   }  
}  

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.okta.springboot'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
mavenCentral()
}

dependencies {
compile('com.okta.spring:okta-spring-boot-starter:0.6.0')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4')
compile('org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.5.RELEASE')
testCompile('org.springframework.boot:spring-boot-starter-test') "
testCompile('org.springframework.security:spring-security-test')
}

/*
This is required to resolve a logging dependency conflict between the
okta-spring-boot-starter and the various spring dependencies.
*/
configurations.all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
exclude group: 'org.springframework.boot', module: 'logback-classic'
}

Understand Your Spring Boot App

The Java web application has only three class files and a few templates. Obviously Spring Boot is doing a lot of heavy hitting going on in the background, but what’s going on in our class files?

The application entry point is in the SpringSimpleApplication class:

@SpringBootApplication
public class SpringSimpleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSimpleApplication.class, args);
}
}

Two important things are happening here that get things rolling: 1) we use the @SpringBootApplication annotation, and 2) our main method calls the SpringApplication.run() method. This is the entry point to the entire Spring/Spring Boot system.

The SpringSecurityWebAppConfig class is a way to use Java code to configure how Spring Boot handles web app security. Here we use the HttpSecurity object to remove authorization from all endpoints. By default, the Spring Boot behavior is the opposite: all endpoints require authorization.

@Configuration
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {

@Override  
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().permitAll();          
}

}

The @Configuration annotation tells Spring that we are using the class as a source of programmatic configuration, allowing us to override the configure() method.

The last Java class, SimpleAppController, is our only controller object. Controllers in a Spring Boot web application are where URL requests are mapped to Java code. The @Controller annotation tells Spring that this class is a controller.

@Controller
class SimpleAppController {

@RequestMapping("/")  
String home() {  
    return "home";  
}  

@RequestMapping("/restricted")  
String restricted() {  
    return "restricted";  
}  

}

Connections between class methods and URLs are made using the @RequestMapping annotation.

We have two mappings:

  1. “home” mapping
  2. “restricted” mapping

Remember that initially nothing is actually “restricted”, so don’t get confused by that. You’ll lock that mapping down in a bit.

Also notice that the classes return a simple text string, but this is getting auto-magically turned into a full html file. This is part of the Thymeleaf dependency that is included in the build.gradle file. These strings are assumed to be template file names, which are by default paths in the templates directory on the classpath.

Thus “home” is mapped to the src/main/resources/templates/home.html template file. When the web app is packaged in the the final jar, the entire resources folder is copied into the classpath, so that the templates directory is accessible at runtime.

Set Up Okta for OAuth 2.0 Single Sign-On

Now you’re going to set up authorization for our app. Okta makes this super easy. You should have already signed up for a free developer.okta.com account. Now you’re going to create an OpenID Connect (OIDC) application to use with OAuth 2.0 Single Sign-On (SSO).

That might be a lot of jargon and acronyms, if you’re not already familiar with them. Very simply, OAuth 2.0 is an industry standard for authorization - a standardized and tested method by which authorization servers and applications can communicate to facilitate user authorization. OpenID Connect is a layer on top of OAuth 2.0 that standardizes and simplifies the authorization procedure as well as providing user authentication. Together they provide a proven way for an application to interact with a remote server that provides authentication and authorization services (such as Okta).

To create an OIDC app, open your Okta developer dashboard. Click on the Applications top menu item, and then click on Add Application.

You should see the following screen. Click on the icon for the Web option. Click Next.

You need to update a few of the initial configuration options. First change the name to something more descriptive. I used “Okta Spring Boot Simple Web App.” Next update the Login redirect URIs to http://localhost:8080/login. Click Done.

This will take you to the new application’s general configuration tab. Scroll down and note the Client ID and Client secret. You’ll need these later.

That’s all you need to do to set up Okta for OAuth! Now let’s return to the Spring Boot app and hook our new OIDC application into the Spring Boot application.

Configure Your Spring Boot App for Single Sign-On (SSO)

Now you need to configure the Spring Boot app to interact with the Okta servers. This is super easy. We need to do two things:

  1. Add the @EnableOAuth2Sso annotation
  2. Update the application.yml configuration

First add the @EnableOAuth2Sso annotation to the SpringSecurityWebAppConfig class.

@EnableOAuth2Sso
@Configuration
public class WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

@Override  
protected void configure(HttpSecurity http) throws Exception {  
    http.authorizeRequests().anyRequest().permitAll();          
}  

}

The @EnableOAuth2Sso annotation does a TON of stuff. It’s worth digging into to understand what’s going on. You can check out Spring’s docs on the annotation itself, and their Spring Boot and OAuth2 tutorial.

One thing I want to point out (bc this has been bugging me a while and I just figured it out) is that you can put this annotation on other classes in the project. However, if you do, be aware that Spring is going to create a WebSecurityConfigurerAdapter and add it to the security chain. Since we’re also creating a WebSecurityConfigurerAdapter, there will be two of them, and you’ll get an error about conflicting chain orders. This is because both WebSecurityConfigurerAdapters will by default use the same chain order. You can resolve this error by adding an @Order(101) annotation to our customized class. However, even better is to add the @EnableOAuth2Sso annotation to our WebSecurityConfigurerAdapter class, WebSecurityConfigurerAdapter, and Spring will use that class instead of creating a duplicate one.

The second change you need to make is update the src/main/resources/application.yml file, filling in some Okta-specific configuration options for the OAuth SSO values take from our Okta OIDC application.

You’ll need to fill in your Client ID and Client secret from the application you created above. You’ll also need to change the issuer URL so that it reflects your Okta preview URL, something like dev-123456.oktapreview.com.

server:
port: 8080

spring:
resources: static-locations: "classpath:/static/"

okta:
oauth2:
issuer: https://{yourOktaDomain}/oauth2/default
clientId: {yourClientId}
clientSecret: {yourClientSecret}
rolesClaim: groups

Refine Our Permissions

Now you’re going to want to update the SpringSecurityWebAppConfig class so that you have a public home page and a restricted “restricted” page. We do this by using Spring’s fluent API for the HttpSecurity object.

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableOAuth2Sso
@Configuration
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {

@Override  
protected void configure(HttpSecurity http) throws Exception {  
    http.authorizeRequests()  
            .antMatchers("/").permitAll() // allow all at home page
            .antMatchers("/img/**").permitAll()  // allow all to access static images
            .anyRequest().authenticated();  // authenticate everything else!
}  

}

Restart your app and now you should be able to:

  1. See the home page without authenticating
  2. NOT see the /restricted page without authenticating
  3. Be able to authenticate using Okta Single Sign-On

This point in the tutorial corresponds to the OktaOAuthSSO tag in the GitHub repository.

Take a Look at the Thymeleaf Templates

The Thymeleaf templates are pretty self explanatory, on the whole, but I did want to point out a couple things. Thymeleaf templates are fully valid HTML5, which is nice. If you want to dig deeper, you can head over to their website and their documentation.

What I wanted to point out is how the template brings in authentication information. To do this, we’re using the thymeleaf-extras-springsecurity plugin. This is included in the build.gradle file with the following line:

compile ("org.thymeleaf.extras:thymeleaf-extras-springsecurity4")

And is included in the template file as an XML namespace attribute on the main <html> tag.

xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"

This plugin is what allows us to check if a user is authenticated using the th:if attribute with a custom SPEL expression (Spring Expression Language). It also allows us to insert authentication properties. Below you see a span <span th:text="${#authentication.name}"></span> that is used to insert the name of the authenticated user.

<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<!--// <th:block th:include="fragments/head :: head"/> //-->
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="box col-md-6 col-md-offset-3">
<div class="okta-header">
<img src="img/logo.png"/>
</div>

        &lt;!--/* displayed if account IS NOT null, indicating that the user IS logged in */--&gt;  
        &lt;div th:if="${#authorization.expression('isAuthenticated()')}"&gt;  
            &lt;h1 th:inline="text"&gt;Hello, &lt;span th:text="${#authentication.name}"&gt;&lt;/span&gt;!&lt;/h1&gt;  
            &lt;a href="/restricted" class="btn btn-primary"&gt;Restricted&lt;/a&gt;  
        &lt;/div&gt;  

        &lt;!--/* displayed if account IS null, indicating that the user IS NOT logged in */--&gt;  
        &lt;div th:unless="${#authorization.expression('isAuthenticated()')}"&gt;  
            &lt;h1&gt;Who are you?&lt;/h1&gt;  
            &lt;a href="/restricted" class="btn btn-primary"&gt;Restricted&lt;/a&gt;  
        &lt;/div&gt;  
    &lt;/div&gt;  
&lt;/div&gt;  

</div>
</body>
</html>

The thymeleaf-extras-springsecurity plugin has some other nice features as well. If you want to dig a little deeper, check out the project repository on GitHub.

Secure Access By Group Membership

The next step in our tutorial is to add Group-based authentication using user groups that we’ll create and define on Okta. A very common example of this is to have an “admin” section of a website and a “user” section of a website, along with perhaps a public home page open to everybody. In this example, “admin” and “user” would correspond to two different groups of which an authenticated user could be a member. What we want to do is be able to restrict access to URL endpoints based on user group membership, and to be able to assign users to these groups.

A side note: groups vs roles. What’s the difference?

  • A “group” is a collection of users, and permissions are assigned to the group. Generally speaking group membership is relatively static, at least throughout the duration of a session.
  • A “role” is a set of permissions that a user can inherit when he/she acts under that role. Roles are generally more dynamic in nature. Users can have many roles. Roles frequently are activated or deactivated depending on complex criteria and often may change throughout a user session.

In practice, for simple authorization systems, they’re pretty similar. The main difference is that groups classify based on individual identity, whereas roles classify based on permissible activities. You’ll probably see apps and tutorials on the wild and woolly internet that ignore this difference, as it’s functionally somewhat subtle. (But now you know. And you can get on the comment thread for the tutorial in question and write a comment correcting the author.)

Configure Authorization Groups in Okta

Go to your developer.okta.com dashboard. From the top menu, go to Users and click on Groups.

Click on the Add Group button.

Name the group “Admin” and give it a description (I put “Administrators,” doesn’t matter what you put here really, just something descriptive).

Click on the group Name to open the group and click on the Add Members button. Add your user to the Admin group.

Next add a new user that’s not an admin.

  • Go to Users from the top menu and click on People.
  • Click Add Person.
  • Fill out the popup form:
  • First name: Not
  • Last name: Admin
  • Username: [email protected]
  • No groups or secondary email
  • Password: Set by admin
  • Assign a password
  • Uncheck “User must change password on first login”
  • Click Save

The next thing you’ll need to do is add a “groups” claim to the default authorization server.

  • From the top menu, go to API and click on Authorization Servers”
  • Click on the default authorization server.
  • Click on the Claims tab.
  • Click the Add Claim button.
  • Update the popup form to match the image below
  • Name: groups
  • Token type: Access
  • Value type: Groups
  • Filter: Regex .*
  • Don’t disable
  • Include in any scope

What you’re doing here is telling Okta to include a “groups” claim in the access token that is sent to your application. This is the OAuth method of Okta telling your application about the groups your authenticated user is a member of. Somewhat confusingly, these will be called “authorities” on the Spring application side, which is an abstract term for groups/roles/privileges communicated by the OAuth server to the app.

Now we have two users. Your primary user, which has been added to the Admin group, and a new user that is not in the admin group. We’ve also configured Okta to add the groups claim to the access token. Now all we have to do is make a few changes to the app code!

Update Your Spring Boot + Spring Security App to Use Group-based Authorization

This is where the Okta Spring Boot Starter really starts to shine. Normally if you wanted to map the security groups and groups claims that we are sending in the token to groups in the app, you’d have to write an extractor class or two to handle the extraction, as well as perhaps a group class. The Okta Spring Boot Starter handles all of this for you!

The first thing you’re going to want to do is add the following annotation to your SpringSecurityWebAppConfig class.

@EnableGlobalMethodSecurity(prePostEnabled = true)

Like so:

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

@EnableOAuth2Sso
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
/* class contents omitted for brevity */
}

This annotation enables the next annotation that we’re going to use, the @PreAuthorize annotation. This annotation allows us to use a Spring Expression Language (SpEL) predicate to determine if the controller method is authorized. The predicate expression is executed before the app even enters the controller method (hence the “pre”-authorize).

In the SimpleAppController class, add a new method called admin like so:

import org.springframework.security.access.prepost.PreAuthorize;

@Controller
class SimpleAppController {

/* other controllers omitted for clarity */ 

@RequestMapping("/admin")  
@PreAuthorize("hasAuthority('Admin')")  
String admin() {  
    return "admin";  
}  

}

Just to recap a little, this method does the following:

  • create a mapping for the /admin url endpoint;
  • assign the /admin endpoint an authorization scheme based on SpEL;
  • and simply return the name of a Thymeleaf template, assumed to be in the /templates directory (which we’ll create next).

Create the new admin template page. In the src/main/resources/templates directory, create a new file called admin.html with the following contents:

<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<!--// <th:block th:include="fragments/head :: head"/> //-->
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="box col-md-6 col-md-offset-3">
<div class="okta-header">
<img src="img/logo.png"/>
</div>

        &lt;h1&gt;Welcome to the admin page!&lt;/h1&gt;  

        &lt;a href="/" class="btn btn-primary"&gt;Go Home&lt;/a&gt;  

    &lt;/div&gt;  
&lt;/div&gt;  

</div>
</body>
</html>

You may be asking yourself what the SpEL expression used in the @PreAuthorize annotation means. Why is the SpEL expression hasAuthority and not hasGroup? A correct answer is somewhat complicated, having to do with the fact that Spring calls permissions privileges and authorities in different contexts, which can be mapped to groups and roles in the app. When using Spring Boot and OAuth, an ‘authority’ is often equated with a ‘role’, which is fine. But you said we’re using groups, not roles? Right. Practically speaking, in this instance, it doesn’t matter because Okta knows we’re talking about groups and the app knows we’re talking about groups, and in the middle we just use the groups claim and the authorities fields to communicate the text strings that represent the groups the user is a member of.

A helpful hint:

If you want to inspect the authentication information that the Spring Boot App is receiving, you can add the following line in one of the controller methods before the return statement.

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

Set a breakpoint on this line, or right after it, really, and run the app with a debugger that allows you to inspect the authentication object. It’s a great way to learn and debug problems.

Try Out Your New Spring Boot + Spring Security Web App!

That’s pretty much it. You should be able to restart the app and log in with two different users. Only the user that was added to the Admin group should be able to access the admin page. You’ll have to directly navigate to http://localhost:8080/admin (as we didn’t add a link or a button). If you try to navigate to the admin page with the other user, you’ll see the beautiful whitelabel error page showing a 403 / Unauthorized error.

Keep in mind that when switching between users you’ll have to stop the app, log out of your developer.okta.com account, and restart the app. You can also use an incognito window in your browser.

This part of the tutorial corresponds to the GroupsAuth tag, which you can checkout using the following command git checkout tags/GroupsAuth.

Thanks for reading. If you liked this post, share it with all of your programming buddies!

Further reading

☞ Spring & Hibernate for Beginners (includes Spring Boot)

☞ Spring Framework Master Class - Learn Spring the Modern Way!

☞ Master Microservices with Spring Boot and Spring Cloud

☞ Spring Boot and OAuth2: Getting the Authorization Code

☞ An Introduction to Spring Boot

☞ How to build GraphQL APIs with Kotlin, Spring Boot, and MongoDB?

☞ Build a Rest API with Spring Boot using MySQL and JPA

☞ Angular 8 + Spring Boot 2.2: Build a CRUD App Today!

☞ Spring Boot vs. Spring MVC vs. Spring: How Do They Compare?

☞ Top 4 Spring Annotations for Java Developer in 2019


Originally published on developer.okta.com

How to build a secure Grails 4 Application using Spring Security Core

How to build a secure Grails 4 Application using Spring Security Core

In this Grails 4 tutorial, we will show you how to build a secure Grails 4 application using Spring Security Core Plugin. We will add the login and register function to the Grails 4 application.

In this Grails 4 tutorial, we will show you how to build a secure Grails 4 application using Spring Security Core Plugin. We will add the login and register function to the Grails 4 application. The purpose of using the Spring Security plugin has simplified the integration of Spring Security Java (we have written this tutorial). The usage of this Grails 4 Spring Security plugin similar to Grails 2 or 3, but there's a lot of updates on the Spring Security code and its dependencies to match the compatibilities.

Table of Contents:

The flow of this tutorial is very simple. We have a secure Product list that only accessible to the authorized user with ROLE_USER and Product CRUD for the user with ROLE_ADMIN. Any access to this Product resource will be redirected to the Login Page as default (if no authorized user). In the login page, there will be a link to the registration page that will register a new user.

The following tools, frameworks, libraries, and dependencies are required for this tutorial:

  1. JDK 8
  2. Grails 4
  3. Grails Spring Security Core Plugin
  4. Terminal or Command Line
  5. Text Editor or IDE

Before starting the main steps, make sure you have downloaded and installed the latest Grails 4. In Mac, we are using the SDKMan. For that, type this command in the Terminal to install SDKMan.

curl -s https://get.sdkman.io | bash

Follow all instructions that showed up during installation. Next, open the new Terminal window or tab then type this command.

source "$HOME/.sdkman/bin/sdkman-init.sh"

Now, you can install Grails 4 using SDKMan.

sdk install grails 4.0.1

Set that new Grails 4 as default. To check the Grails version, type this command.

grails -version
| Grails Version: 4.0.1
| JVM Version: 1.8.0_92
Create Grails 4 Application

Same as previous Grails version, to create a new Grails application, simply type this command.

grails create-app com.djamware.gadgethouse

That command will create a new Grails 4 application with the name "gadgethouse" with the package name "com.djamware". Next, go to the newly created project folder then enter the Grails 4 interactive console.

cd ./gadgethouse
grails

In the Grails interactive console, type this command to run this Grails application for the first time.

grails> run-app

Here's the new Grails 4 look like.

Install Grails Spring Security Core Plugin

Now, we will install and configure Grails Spring Security Core Plugin. For the database, we keep H2 in-memory database (You can change to other relational database configuration). To install the Grails Spring Security Core Plugin, open and edit build.gradle then add this dependency in dependencies array.

compile "org.grails.plugins:spring-security-core:4.0.0.RC2"

Next, stop the running Grails application using this command in the Grails interactive console.

stop-app

Compile the Grails application, to install the Spring Security Core plugin.

compile
Create User, Role, and Product Domain Class

We will use the Grails s2-quickstart command to create User and Role domain class for authentication. Type this command in Grails interactive console.

s2-quickstart com.djamware User Role

That command will create User and Role domain class with the package name "com.djamware". Next, open and edit grails-app/domain/com/djamware/Role.groovy to add default field after the bracket closing when this domain calls.

String toString() {
  authority
}

Next, create a domain class using regular Grails command for Product.

create-domain-class com.djamware.Product

Next, we need to create an additional field in the user domain class. For that, open and edit grails-app/domain/com/djamware/User.groovy then add a fullName field after the password field.

String fullname

Also, add a constraint for that field.

static constraints = {
    password nullable: false, blank: false, password: true
    username nullable: false, blank: false, unique: true
    fullname nullable: false, blank: false
}

Next, open and edit grails-app/domain/com/djamware/Product.groovy then replace that domain class with these Groovy codes.

package com.djamware

class Product {

    String prodCode
    String prodName
    String prodModel
    String prodDesc
    String prodImageUrl
    String prodPrice

    static constraints = {
        prodCode nullable: false, blank: false
        prodName nullable: false, blank: false
        prodModel nullable: false, blank: false
        prodDesc nullable: false, blank: false
        prodImageUrl nullable: true
        prodPrice nullable: false, blank: false
    }

    String toString() {
        prodName
    }
}
Create CustomUserDetailsService

Because we have added a field in the previous User domain class, we need to create a custom UserDetails. Create a new Groovy file src/main/groovy/com/djamware/CustomUserDetails.groovy then add these lines of Groovy codes that add full name field to the Grails UserDetails.

package com.djamware

import grails.plugin.springsecurity.userdetails.GrailsUser
import org.springframework.security.core.GrantedAuthority

class CustomUserDetails extends GrailsUser {

   final String fullname

   CustomUserDetails(String username, String password, boolean enabled,
                 boolean accountNonExpired, boolean credentialsNonExpired,
                 boolean accountNonLocked,
                 Collection<GrantedAuthority> authorities,
                 long id, String fullname) {
      super(username, password, enabled, accountNonExpired,
            credentialsNonExpired, accountNonLocked, authorities, id)

      this.fullname = fullname
   }
}

Next, type this command in the Grails interactive console to create a new Grails service.

grails> create-service com.djamware.CustomUserDetails

Open that file then replace all Groovy codes with these codes.

package com.djamware

import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import grails.plugin.springsecurity.userdetails.NoStackUsernameNotFoundException
import grails.gorm.transactions.Transactional
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException

class CustomUserDetailsService implements GrailsUserDetailsService {

   /**
    * Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least
    * one role, so we give a user with no granted roles this one which gets
    * past that restriction but doesn't grant anything.
    */
   static final List NO_ROLES = [new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)]

   UserDetails loadUserByUsername(String username, boolean loadRoles)
         throws UsernameNotFoundException {
      return loadUserByUsername(username)
   }

   @Transactional(readOnly=true, noRollbackFor=[IllegalArgumentException, UsernameNotFoundException])
   UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

      User user = User.findByUsername(username)
      if (!user) throw new NoStackUsernameNotFoundException()

      def roles = user.authorities

      // or if you are using role groups:
      // def roles = user.authorities.collect { it.authorities }.flatten().unique()

      def authorities = roles.collect {
         new SimpleGrantedAuthority(it.authority)
      }

      return new CustomUserDetails(user.username, user.password, user.enabled,
            !user.accountExpired, !user.passwordExpired,
            !user.accountLocked, authorities ?: NO_ROLES, user.id,
            user.fullname)
   }
}

Next, register that new CustomUserDetailsService in the grails-app/conf/spring/resources.groovy.

import com.djamware.UserPasswordEncoderListener
import com.djamware.CustomUserDetailsService
// Place your Spring DSL code here
beans = {
    userPasswordEncoderListener(UserPasswordEncoderListener)
    userDetailsService(CustomUserDetailsService)
}
Override Login Auth View

We will customize the login page to make UI better and add a link to the Register page. For that, create a login folder under views then create an auth.gsp file in that folder. Open and edit grails-app/views/login/auth.gsp then add these lines of GSP HTML tags.

<html>
<head>
    <meta name="layout" content="${gspLayout ?: 'main'}"/>
    <title><g:message code='springSecurity.login.title'/></title>
</head>

<body>
    <div class="row">
      <div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
        <div class="card card-signin my-5">
          <div class="card-body">
            <h5 class="card-title text-center">Please Login</h5>
            <g:if test='${flash.message}'>
                <div class="alert alert-danger" role="alert">${flash.message}</div>
            </g:if>
            <form class="form-signin" action="${postUrl ?: '/login/authenticate'}" method="POST" id="loginForm" autocomplete="off">
              <div class="form-group">
                  <label for="username">Username</label>
                <input type="text" class="form-control" name="${usernameParameter ?: 'username'}" id="username" autocapitalize="none"/>
              </div>

              <div class="form-group">
                  <label for="password">Password</label>
                <input type="password" class="form-control" name="${passwordParameter ?: 'password'}" id="password"/>
                <i id="passwordToggler" title="toggle password display" onclick="passwordDisplayToggle()">&#128065;</i>
              </div>

              <div class="form-group form-check">
                  <label class="form-check-label">
                      <input type="checkbox" class="form-check-input" name="${rememberMeParameter ?: 'remember-me'}" id="remember_me" <g:if test='${hasCookie}'>checked="checked"</g:if>/> Remember me
                </label>
              </div>
              <button id="submit" class="btn btn-lg btn-primary btn-block text-uppercase" type="submit">Sign in</button>
              <hr class="my-4">
              <p>Don't have an account? <g:link controller="register">Register</g:link></p>
            </form>
          </div>
        </div>
      </div>
    </div>
    <script type="text/javascript">
        document.addEventListener("DOMContentLoaded", function(event) {
            document.forms['loginForm'].elements['username'].focus();
        });
        function passwordDisplayToggle() {
            var toggleEl = document.getElementById("passwordToggler");
            var eyeIcon = '\u{1F441}';
            var xIcon = '\u{2715}';
            var passEl = document.getElementById("password");
            if (passEl.type === "password") {
                toggleEl.innerHTML = xIcon;
                passEl.type = "text";
            } else {
                toggleEl.innerHTML = eyeIcon;
                passEl.type = "password";
            }
        }
    </script>
</body>
</html>

Next, we will make this login page as default or homepage when the application opens in the browser. For that, open and edit grails-app/controllers/UrlMappings.groovy then replace this line.

"/"(view: "index")

With this line.

"/"(controller:'login', action:'auth')
Add User Info and Logout to the Navbar

Now, we have to implement POST logout to the Navbar. This logout button active when the user logged in successfully along with user info. For that, modify grails-app/views/layout/main.gsp then replace all GSP HTML tags with these.

<!doctype html>
<html lang="en" class="no-js">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <title>
        <g:layoutTitle default="Grails"/>
    </title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <asset:link rel="icon" href="favicon.ico" type="image/x-ico"/>

    <asset:stylesheet src="application.css"/>

    <g:layoutHead/>
</head>

<body>

<nav class="navbar navbar-expand-lg navbar-dark navbar-static-top" role="navigation">
    <a class="navbar-brand" href="/#"><asset:image src="grails.svg" alt="Grails Logo"/></a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" aria-expanded="false" style="height: 0.8px;" id="navbarContent">
        <ul class="nav navbar-nav ml-auto">
            <g:pageProperty name="page.nav"/>
            <sec:ifLoggedIn>
              <li class="nav-item dropdown">
                  <a class="nav-link dropdown-toggle" href="#" id="navbardrop" data-toggle="dropdown">
                    <sec:loggedInUserInfo field='fullname'/>
                  </a>
                  <div class="dropdown-menu navbar-dark">
                    <g:form controller="logout">
                      <g:submitButton class="dropdown-item navbar-dark color-light" name="Submit" value="Logout" style="color:gray" />
                    </g:form>
                  </div>
              </li>
            </sec:ifLoggedIn>
        </ul>
    </div>

</nav>

<div class="container">
    <g:layoutBody/>
</div>

<div class="footer row" role="contentinfo">
    <div class="col">
        <a href="http://guides.grails.org" target="_blank">
            <asset:image src="advancedgrails.svg" alt="Grails Guides" class="float-left"/>
        </a>
        <strong class="centered"><a href="http://guides.grails.org" target="_blank">Grails Guides</a></strong>
        <p>Building your first Grails app? Looking to add security, or create a Single-Page-App? Check out the <a href="http://guides.grails.org" target="_blank">Grails Guides</a> for step-by-step tutorials.</p>

    </div>
    <div class="col">
        <a href="http://docs.grails.org" target="_blank">
            <asset:image src="documentation.svg" alt="Grails Documentation" class="float-left"/>
        </a>
        <strong class="centered"><a href="http://docs.grails.org" target="_blank">Documentation</a></strong>
        <p>Ready to dig in? You can find in-depth documentation for all the features of Grails in the <a href="http://docs.grails.org" target="_blank">User Guide</a>.</p>

    </div>

    <div class="col">
        <a href="https://grails-slack.cfapps.io" target="_blank">
            <asset:image src="slack.svg" alt="Grails Slack" class="float-left"/>
        </a>
        <strong class="centered"><a href="https://grails-slack.cfapps.io" target="_blank">Join the Community</a></strong>
        <p>Get feedback and share your experience with other Grails developers in the community <a href="https://grails-slack.cfapps.io" target="_blank">Slack channel</a>.</p>
    </div>
</div>

<div id="spinner" class="spinner" style="display:none;">
    <g:message code="spinner.alt" default="Loading&hellip;"/>
</div>

<asset:javascript src="application.js"/>

</body>
</html>

As you see, there are built in Grails Spring Security TagLib sec:ifLoggedIn and <sec:loggedInUserInfo field='fullname'/>. The <sec:loggedInUserInfo field='fullname'/> only working when you implementing CustomUserDetailsService.

Create Register Controller and View

Back to the Grails interactive console to create a controller for the Register page.

grails> create-controller com.djamware.Register

Open and edit that Groovy file then replace all Groovy codes with these codes that have 2 methods of Register landing page and register action.

package com.djamware

import grails.validation.ValidationException
import grails.gorm.transactions.Transactional
import grails.plugin.springsecurity.annotation.Secured
import com.djamware.User
import com.djamware.Role
import com.djamware.UserRole

@Transactional
@Secured('permitAll')
class RegisterController {

    static allowedMethods = [register: "POST"]

    def index() { }

    def register() {
        if(!params.password.equals(params.repassword)) {
            flash.message = "Password and Re-Password not match"
            redirect action: "index"
            return
        } else {
            try {
                def user = User.findByUsername(params.username)?: new User(username: params.username, password: params.password, fullname: params.fullname).save()
                def role = Role.get(params.role.id)
                if(user && role) {
                    UserRole.create user, role

                    UserRole.withSession {
                      it.flush()
                      it.clear()
                    }

                    flash.message = "You have registered successfully. Please login."
                    redirect controller: "login", action: "auth"
                } else {
                    flash.message = "Register failed"
                    render view: "index"
                    return
                }
            } catch (ValidationException e) {
                flash.message = "Register Failed"
                redirect action: "index"
                return
            }
        }
    }
}

Next, add the index.gsp inside grails-app/views/register/. Open and edit that file then add these lines of GSP HTML tags.

<html>
<head>
    <meta name="layout" content="${gspLayout ?: 'main'}"/>
    <title>Register</title>
</head>

<body>
    <div class="row">
    <div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
      <div class="card card-signin my-5">
        <div class="card-body">
          <h5 class="card-title text-center">Register Here</h5>
                    <g:if test='${flash.message}'>
                        <div class="alert alert-danger" role="alert">${flash.message}</div>
                    </g:if>
              <form class="form-signin" action="register" method="POST" id="loginForm" autocomplete="off">
                      <div class="form-group">
                          <label for="role">Role</label>
              <g:select class="form-control" name="role.id"
                    from="${com.djamware.Role.list()}"
                    optionKey="id" />
                </div>

            <div class="form-group">
                    <label for="username">Username</label>
              <input type="text" placeholder="Your username" class="form-control" name="username" id="username" autocapitalize="none"/>
            </div>

            <div class="form-group">
                          <label for="password">Password</label>
              <input type="password" placeholder="Your password" class="form-control" name="password" id="password"/>
            </div>

            <div class="form-group">
                          <label for="password">Re-Enter Password</label>
              <input type="password" placeholder="Re-enter password" class="form-control" name="repassword" id="repassword"/>
            </div>

                      <div class="form-group">
                          <label for="username">Full Name</label>
              <input type="text" placeholder="Your full name" class="form-control" name="fullname" id="fullname" autocapitalize="none"/>
            </div>

            <button id="submit" class="btn btn-lg btn-primary btn-block text-uppercase" type="submit">Register</button>
            <hr class="my-4">
            <p>Already have an account? <g:link controller="login" action="auth">Login</g:link></p>
          </form>
        </div>
      </div>
    </div>
  </div>
    <script type="text/javascript">
        document.addEventListener("DOMContentLoaded", function(event) {
            document.forms['loginForm'].elements['username'].focus();
        });
    </script>
</body>
</html>
Create the Secure Product CRUD Scaffolding

Now, we will make Product CRUD scaffolding and make them secured and accessible to ROLE_USER and ROLE_ADMIN. To create CRUD scaffolding, simply run this command inside Grails interactive console.

grails>generate-all com.djamware.Product

That command will generate controller, service, and view for a Product domain class. Next, open and edit grails-app/controllers/ProductController.groovy then add the Secure annotation like these.

package com.djamware

import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*
import grails.plugin.springsecurity.annotation.Secured

class ProductController {

    ProductService productService

    static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]

    @Secured(['ROLE_ADMIN', 'ROLE_USER'])
    def index(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        respond productService.list(params), model:[productCount: productService.count()]
    }

    @Secured(['ROLE_ADMIN', 'ROLE_USER'])
    def show(Long id) {
        respond productService.get(id)
    }

    @Secured('ROLE_ADMIN')
    def create() {
        respond new Product(params)
    }

    @Secured('ROLE_ADMIN')
    def save(Product product) {
        if (product == null) {
            notFound()
            return
        }

        try {
            productService.save(product)
        } catch (ValidationException e) {
            respond product.errors, view:'create'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message', args: [message(code: 'product.label', default: 'Product'), product.id])
                redirect product
            }
            '*' { respond product, [status: CREATED] }
        }
    }

    @Secured('ROLE_ADMIN')
    def edit(Long id) {
        respond productService.get(id)
    }

    @Secured('ROLE_ADMIN')
    def update(Product product) {
        if (product == null) {
            notFound()
            return
        }

        try {
            productService.save(product)
        } catch (ValidationException e) {
            respond product.errors, view:'edit'
            return
        }

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message', args: [message(code: 'product.label', default: 'Product'), product.id])
                redirect product
            }
            '*'{ respond product, [status: OK] }
        }
    }

    @Secured('ROLE_ADMIN')
    def delete(Long id) {
        if (id == null) {
            notFound()
            return
        }

        productService.delete(id)

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.deleted.message', args: [message(code: 'product.label', default: 'Product'), id])
                redirect action:"index", method:"GET"
            }
            '*'{ render status: NO_CONTENT }
        }
    }

    protected void notFound() {
        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.not.found.message', args: [message(code: 'product.label', default: 'Product'), params.id])
                redirect action: "index", method: "GET"
            }
            '*'{ render status: NOT_FOUND }
        }
    }
}

Next, we will make Product controller as a default landing page after succeful login. For that, open and edit grails-app/conf/application.groovy then add this configuration.

grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/product'
Run and Test Grails 4 Spring Security Core

Now, we will run and test the Grails 4 Spring Security Core application. In the Grails interactive console run this command.

grails>run-app

Open the browser then go to http://localhost:8080 and you will see this page.




That it's, the Grails 4 Tutorial: Spring Security Core Login Example. You can find the full working source code in our GitHub.

Thanks!

Securing RESTful API with Spring Boot, Security, and Data MongoDB

Securing RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB

A comprehensive step by step tutorial on securing or authentication RESTful API with Spring Boot, Security, and Data MongoDB. Previously, we have shown you how to securing Spring Boot, MVC and MongoDB web application. In this tutorial, the secure endpoint will restrict the access from an unauthorized request. Every request to secure endpoint should bring authorization token with it. Of course, there will be an endpoint for login which will get authorization token after successful login.

Table of Contents:

The following software, tools, and frameworks are required for this tutorial:

We assume that you already installed all required software, tools, and frameworks. So, we will not cover how to install that software, tools, and frameworks.

1. Generate a New Spring Boot Gradle Project

To create or generate a new Spring Boot Application or Project, simply go to Spring Initializer. Fill all required fields as below then click on Generate Project button.

The project will automatically be downloaded as a Zip file. Next, extract the zipped project to your java projects folder. On the project folder root, you will find build.gradle file for register dependencies, initially it looks like this.

buildscript {
&nbsp;&nbsp; &nbsp;ext {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;springBootVersion = '2.1.2.RELEASE'
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;repositories {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;mavenCentral()
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;dependencies {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
&nbsp;&nbsp; &nbsp;}
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.djamware'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
&nbsp;&nbsp; &nbsp;mavenCentral()
}

dependencies {
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-security'
&nbsp;&nbsp; &nbsp;implementation 'org.springframework.boot:spring-boot-starter-web'
&nbsp;&nbsp; &nbsp;testImplementation 'org.springframework.boot:spring-boot-starter-test'
&nbsp;&nbsp; &nbsp;testImplementation 'org.springframework.security:spring-security-test'
}

Now, you can work with the source code of this Spring Boot Project using your own IDE or Text Editor. We are using Spring Tool Suite (STS). In STS, import the extracted zipped file as Existing Gradle Project.

Next, we have to add the JWT library to the build.gradle as the dependency. Open and edit build.gradle then add this line to dependencies after other implementation.

implementation 'io.jsonwebtoken:jjwt:0.9.1'

Next, compile the Gradle Project by type this command from Terminal or CMD.

./gradlew compile

Or you can compile directly from STS by right-clicking the project name then choose Gradle -> Refresh Gradle Project. Next, open and edit src/main/resources/application.properties then add these lines.

spring.data.mongodb.database=springmongodb
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

2. Create Product, User and Role Model or Entity Classes

We will be creating all required models or entities for products, user and role. In STS, right-click the project name -> New -> Class. Fill the package with com.djamware.SecurityRest.models, the name with Product, and leave other fields and checkbox as default then click Finish Button.

Next, open and edit src/main/java/com/djamware/SecurityRest/models/Product.java then add this annotation above the class name that will point to MongoDB collection.

@Document(collection = "products")

Inside Product class, add these variables.

@Id
String id;
String prodName;
String prodDesc;
Double prodPrice;
String prodImage;

Add constructors after the variable or fields.

public Product() {
}

public Product(String prodName, String prodDesc, Double prodPrice, String prodImage) {
&nbsp;&nbsp; &nbsp;super();
&nbsp;&nbsp; &nbsp;this.prodName = prodName;
&nbsp;&nbsp; &nbsp;this.prodDesc = prodDesc;
&nbsp;&nbsp; &nbsp;this.prodPrice = prodPrice;
&nbsp;&nbsp; &nbsp;this.prodImage = prodImage;
}

Generate or create Getter and Setter for each field.

public String getId() {
&nbsp;&nbsp; &nbsp;return id;
}

public void setId(String id) {
&nbsp;&nbsp; &nbsp;this.id = id;
}

public String getProdName() {
&nbsp;&nbsp; &nbsp;return prodName;
}

public void setProdName(String prodName) {
&nbsp;&nbsp; &nbsp;this.prodName = prodName;
}

public String getProdDesc() {
&nbsp;&nbsp; &nbsp;return prodDesc;
}

public void setProdDesc(String prodDesc) {
&nbsp;&nbsp; &nbsp;this.prodDesc = prodDesc;
}

public Double getProdPrice() {
&nbsp;&nbsp; &nbsp;return prodPrice;
}

public void setProdPrice(Double prodPrice) {
&nbsp;&nbsp; &nbsp;this.prodPrice = prodPrice;
}

public String getProdImage() {
&nbsp;&nbsp; &nbsp;return prodImage;
}

public void setProdImage(String prodImage) {
&nbsp;&nbsp; &nbsp;this.prodImage = prodImage;
}

Using STS you can organize imports automatically from the menu Source -> Organize Imports then you can see the imports after the package name.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

You can do the same way as the above step for User and Role class. Here’s the User class looks like.

package com.djamware.SecurityRest.models;

import java.util.Set;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "users")
public class User {

&nbsp;&nbsp; &nbsp;@Id
&nbsp;&nbsp; &nbsp;private String id;
&nbsp;&nbsp; &nbsp;@Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)
&nbsp;&nbsp; &nbsp;private String email;
&nbsp;&nbsp; &nbsp;private String password;
&nbsp;&nbsp; &nbsp;private String fullname;
&nbsp;&nbsp; &nbsp;private boolean enabled;
&nbsp;&nbsp; &nbsp;@DBRef
&nbsp;&nbsp; &nbsp;private Set<Role> roles;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getFullname() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setFullname(String fullname) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.fullname = fullname;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public boolean isEnabled() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEnabled(boolean enabled) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.enabled = enabled;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public Set<Role> getRoles() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return roles;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRoles(Set<Role> roles) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.roles = roles;
&nbsp;&nbsp; &nbsp;}

}

And the Role class will be like this.

package com.djamware.SecurityRest.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.IndexDirection;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "roles")
public class Role {

&nbsp;&nbsp; &nbsp;@Id
&nbsp; &nbsp; private String id;
&nbsp; &nbsp; @Indexed(unique = true, direction = IndexDirection.DESCENDING, dropDups = true)

&nbsp; &nbsp; private String role;
&nbsp;&nbsp; &nbsp;public String getId() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setId(String id) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.id = id;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getRole() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return role;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setRole(String role) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.role = role;
&nbsp;&nbsp; &nbsp;}

}

3. Create Product, User and Role Repository Interfaces

Next steps to create Product, User, and Role Repository Interfaces. From the STS, right-click the project name -> New -> Interface then fill all required fields and checkboxes as below before click Finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/repositories/ProductRepository.java then add extends to MongoDB CRUD Repository.

public interface ProductRepository extends CrudRepository<Product, String> {

}

Inside the class name add this method.

@Override
void delete(Product deleted);

Organize all required imports.

import org.springframework.data.repository.CrudRepository;
import com.djamware.SecurityRest.models.Product;

The same way can be applied to User and Role repositories. So, the User Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.User;

public interface UserRepository extends MongoRepository<User, String> {

&nbsp;&nbsp; &nbsp;User findByEmail(String email);
}

And the Role Repository Interface will look like this.

package com.djamware.SecurityRest.repositories;

import org.springframework.data.mongodb.repository.MongoRepository;
import com.djamware.SecurityRest.models.Role;

public interface RoleRepository extends MongoRepository<Role, String> {

&nbsp;&nbsp; &nbsp;Role findByRole(String role);
}

4. Create a Custom User Details Service

To implements authentication using existing User and Role from MongoDB, we have to create a custom user details service. From the STS, right-click the project name -> New -> Class File then fill all required fields and checkboxes as below before clicking the finish button.

Next, open and edit src/main/java/com/djamware/SecurityRest/services/CustomUserDetailsService.java then give an annotation above the class name and implement the Spring Security User Details Service.

@Service
public class CustomUserDetailsService implements UserDetailsService {
}

Next, inject all required beans at the first line of the class bracket.

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private PasswordEncoder bCryptPasswordEncoder;

Add a method to find a user by email field.

public User findUserByEmail(String email) {
&nbsp; &nbsp; return userRepository.findByEmail(email);
}

Add a method to save a new user.

public void saveUser(User user) {
&nbsp; &nbsp; user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
&nbsp; &nbsp; user.setEnabled(true);
&nbsp; &nbsp; Role userRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; user.setRoles(new HashSet<>(Arrays.asList(userRole)));
&nbsp; &nbsp; userRepository.save(user);
}

Override the Spring Security User Details to load User by email.

@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

&nbsp; &nbsp; User user = userRepository.findByEmail(email);
&nbsp; &nbsp; if(user != null) {
&nbsp; &nbsp; &nbsp; &nbsp; List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
&nbsp; &nbsp; &nbsp; &nbsp; return buildUserForAuthentication(user, authorities);
&nbsp; &nbsp; } else {
&nbsp; &nbsp; &nbsp; &nbsp; throw new UsernameNotFoundException("username not found");
&nbsp; &nbsp; }
}

Add a method to get a set of Roles that related to a user.

private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
&nbsp; &nbsp; Set<GrantedAuthority> roles = new HashSet<>();
&nbsp; &nbsp; userRoles.forEach((role) -> {
&nbsp; &nbsp; &nbsp; &nbsp; roles.add(new SimpleGrantedAuthority(role.getRole()));
&nbsp; &nbsp; });

&nbsp; &nbsp; List<GrantedAuthority> grantedAuthorities = new ArrayList<>(roles);
&nbsp; &nbsp; return grantedAuthorities;
}

Add a method for authentication purpose.

private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
&nbsp; &nbsp; return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}

5. Configure Spring Boot Security Rest

Now, the main purpose of this tutorial is configuring Spring Security Rest. First, we have to create a bean for JWT token generation and validation. Right-click the project name -> New -> Class File. Fill the package name as com.djamware.SecurityRest.configs and the Class name as JwtTokenProvider then click the Finish button. Next, open and edit that newly created class file then give it an annotation above the class name.

@Component
public class JwtTokenProvider {
}

Add variables and injected bean inside the class bracket at the top lines.

@Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";

@Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h

@Autowired
private CustomUserDetailsService userDetailsService;

Add a method for initialization.

@PostConstruct
protected void init() {
&nbsp; &nbsp; secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}

Add a method to create a JWT token.

public String createToken(String username, Set<Role> set) {
&nbsp; &nbsp; Claims claims = Jwts.claims().setSubject(username);
&nbsp; &nbsp; claims.put("roles", set);
&nbsp; &nbsp; Date now = new Date();
&nbsp; &nbsp; Date validity = new Date(now.getTime() + validityInMilliseconds);
&nbsp; &nbsp; return Jwts.builder()//
&nbsp; &nbsp; &nbsp; &nbsp; .setClaims(claims)//
&nbsp; &nbsp; &nbsp; &nbsp; .setIssuedAt(now)//
&nbsp; &nbsp; &nbsp; &nbsp; .setExpiration(validity)//
&nbsp; &nbsp; &nbsp; &nbsp; .signWith(SignatureAlgorithm.HS256, secretKey)//
&nbsp; &nbsp; &nbsp; &nbsp; .compact();
}

Add a method to load User by username.

public Authentication getAuthentication(String token) {
&nbsp; &nbsp; UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
&nbsp; &nbsp; return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}

Add a method to get the username by JWT token.

public String getUsername(String token) {
&nbsp; &nbsp; return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}

Add a method to resolve JWT token from request headers of Authorization that has a Bearer prefix.

public String resolveToken(HttpServletRequest req) {
&nbsp; &nbsp; String bearerToken = req.getHeader("Authorization");
&nbsp; &nbsp; if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
&nbsp; &nbsp; &nbsp; &nbsp; return bearerToken.substring(7, bearerToken.length());
&nbsp; &nbsp; }
&nbsp; &nbsp; return null;
}

Add a method to validate a JWT token.

public boolean validateToken(String token) {
&nbsp; &nbsp; try {
&nbsp; &nbsp; &nbsp; &nbsp; Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
&nbsp; &nbsp; &nbsp; &nbsp; if (claims.getBody().getExpiration().before(new Date())) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; return true;
&nbsp; &nbsp; } catch (JwtException | IllegalArgumentException e) {
&nbsp; &nbsp; &nbsp; &nbsp; throw new JwtException("Expired or invalid JWT token");
&nbsp; &nbsp; }
}

Finally, organize imports like below.

package com.djamware.SecurityRest.configs;

import java.util.Base64;
import java.util.Date;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.djamware.SecurityRest.models.Role;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

Next, create a JWT filter class with the name JwtTokenFilter in configs package that extends Spring GenericFilterBean. Replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

public class JwtTokenFilter extends GenericFilterBean {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
&nbsp; &nbsp; &nbsp; &nbsp; throws IOException, ServletException {
&nbsp; &nbsp; &nbsp; &nbsp; String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
&nbsp; &nbsp; &nbsp; &nbsp; if (token != null && jwtTokenProvider.validateToken(token)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SecurityContextHolder.getContext().setAuthentication(auth);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; filterChain.doFilter(req, res);
&nbsp; &nbsp; }
}

Next, create a class with the name JwtConfigurer for JWT configuration in configs package then replace all codes with these lines of codes.

package com.djamware.SecurityRest.configs;

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

&nbsp;&nbsp; &nbsp;private JwtTokenProvider jwtTokenProvider;

&nbsp; &nbsp; public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
&nbsp; &nbsp; &nbsp; &nbsp; this.jwtTokenProvider = jwtTokenProvider;
&nbsp; &nbsp; }

&nbsp; &nbsp; @Override
&nbsp; &nbsp; public void configure(HttpSecurity http) throws Exception {
&nbsp; &nbsp; &nbsp; &nbsp; JwtTokenFilter customFilter = new JwtTokenFilter(jwtTokenProvider);
&nbsp; &nbsp; &nbsp; &nbsp; http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
&nbsp; &nbsp; }
}

Finally, we have to configure the Spring Security by creating a Java class file inside configs package with the name WebSecurityConfig. Give annotations to this class and extends Spring WebSecurityConfigurerAdapter.

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}

Inject JWT token provider inside this class.

@Autowired
JwtTokenProvider jwtTokenProvider;

Add an override method to configure Authentication Manager Builder.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
&nbsp;&nbsp; &nbsp;UserDetailsService userDetailsService = mongoUserDetails();
&nbsp;&nbsp; &nbsp;auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());

}

Next, add an override method to configure Spring Security Http Security.

@Override
protected void configure(HttpSecurity http) throws Exception {
&nbsp;&nbsp; &nbsp;http.httpBasic().disable().csrf().disable().sessionManagement()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/auth/login").permitAll().antMatchers("/api/auth/register").permitAll()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.antMatchers("/api/products/**").hasAuthority("ADMIN").anyRequest().authenticated().and().csrf()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.disable().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint()).and()
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;.apply(new JwtConfigurer(jwtTokenProvider));
}

Next, declare all required beans for this configuration.

@Bean
public PasswordEncoder bCryptPasswordEncoder() {
&nbsp;&nbsp; &nbsp;return new BCryptPasswordEncoder();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
&nbsp;&nbsp; &nbsp;return super.authenticationManagerBean();
}

@Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
&nbsp;&nbsp; &nbsp;return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;"Unauthorized");
}

@Bean
public UserDetailsService mongoUserDetails() {
&nbsp;&nbsp; &nbsp;return new CustomUserDetailsService();
}

6. Create Product and Authentication Controllers

Now it’s time for REST API endpoint. All RESTful API will be created from each controller. Product controller will handle CRUD endpoint of product and Authentication controller will handle login and register endpoint. Right-click project name -> New -> Class then fill the package with com.djamware.SecurityRest.controllers and the class name as ProductController. Open and edit the newly created class file then replace all codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.models.Product;
import com.djamware.SecurityRest.repositories.ProductRepository;

@RestController
public class ProductController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp; &nbsp; ProductRepository productRepository;

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products")
&nbsp; &nbsp; public Iterable<Product> product() {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findAll();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.POST, value="/api/products")
&nbsp; &nbsp; public String save(@RequestBody Product product) {
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(product);

&nbsp; &nbsp; &nbsp; &nbsp; return product.getId();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.GET, value="/api/products/{id}")
&nbsp; &nbsp; public Optional<Product> show(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; return productRepository.findById(id);
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.PUT, value="/api/products/{id}")
&nbsp; &nbsp; public Product update(@PathVariable String id, @RequestBody Product product) {
&nbsp; &nbsp; &nbsp;&nbsp; &nbsp;Optional<Product> prod = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdName() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdName(product.getProdName());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdDesc() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdDesc(product.getProdDesc());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdPrice() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdPrice(product.getProdPrice());
&nbsp; &nbsp; &nbsp; &nbsp; if(product.getProdImage() != null)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prod.get().setProdImage(product.getProdImage());
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.save(prod.get());
&nbsp; &nbsp; &nbsp; &nbsp; return prod.get();
&nbsp; &nbsp; }

&nbsp; &nbsp; @RequestMapping(method=RequestMethod.DELETE, value="/api/products/{id}")
&nbsp; &nbsp; public String delete(@PathVariable String id) {
&nbsp; &nbsp; &nbsp; &nbsp; Optional<Product> product = productRepository.findById(id);
&nbsp; &nbsp; &nbsp; &nbsp; productRepository.delete(product.get());

&nbsp; &nbsp; &nbsp; &nbsp; return "product deleted";
&nbsp; &nbsp; }
}

For login, we need to create a POJO to mapping required fields of User. Create a new class file with the name AuthBody inside controllers package then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

public class AuthBody {

&nbsp;&nbsp; &nbsp;private String email;
&nbsp; &nbsp; private String password;

&nbsp;&nbsp; &nbsp;public String getEmail() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setEmail(String email) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.email = email;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public String getPassword() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return password;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;public void setPassword(String password) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.password = password;
&nbsp;&nbsp; &nbsp;}

}

Finally, create a controller for authentication with the name AuthController inside the controllers’ package. Open and edit that newly created file then replace all Java codes with these lines of codes.

package com.djamware.SecurityRest.controllers;

import static org.springframework.http.ResponseEntity.ok;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.djamware.SecurityRest.configs.JwtTokenProvider;
import com.djamware.SecurityRest.models.User;
import com.djamware.SecurityRest.repositories.UserRepository;
import com.djamware.SecurityRest.services.CustomUserDetailsService;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;AuthenticationManager authenticationManager;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;JwtTokenProvider jwtTokenProvider;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;UserRepository users;

&nbsp;&nbsp; &nbsp;@Autowired
&nbsp;&nbsp; &nbsp;private CustomUserDetailsService userService;

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/login")
&nbsp;&nbsp; &nbsp;public ResponseEntity login(@RequestBody AuthBody data) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String username = data.getEmail();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, data.getPassword()));
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String token = jwtTokenProvider.createToken(username, this.users.findByEmail(username).getRoles());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("username", username);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("token", token);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;} catch (AuthenticationException e) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("Invalid email/password supplied");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;}

&nbsp;&nbsp; &nbsp;@SuppressWarnings("rawtypes")
&nbsp;&nbsp; &nbsp;@PostMapping("/register")
&nbsp;&nbsp; &nbsp;public ResponseEntity register(@RequestBody User user) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;User userExists = userService.findUserByEmail(user.getEmail());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if (userExists != null) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throw new BadCredentialsException("User with username: " + user.getEmail() + " already exists");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;userService.saveUser(user);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Map<Object, Object> model = new HashMap<>();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;model.put("message", "User registered successfully");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return ok(model);
&nbsp;&nbsp; &nbsp;}
}

7. Run and Test Spring Boot Security Rest using Postman

Before run and test the application, we have to populate a Role data first. Open and edit src/main/java/com/djamware/SecurityRest/SecurityRestApplication.java then add these lines of codes inside the initialization method.

@Bean
CommandLineRunner init(RoleRepository roleRepository) {

&nbsp; &nbsp; return args -> {

&nbsp; &nbsp; &nbsp; &nbsp; Role adminRole = roleRepository.findByRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; if (adminRole == null) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Role newAdminRole = new Role();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newAdminRole.setRole("ADMIN");
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; roleRepository.save(newAdminRole);
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; };

}

Next, make sure you have run the MongoDB server on your local machine then run the Gradle application using this command.

./gradlew bootRun

Or in STS just right-click the project name -> Run As -> Spring Boot App. Next, open the Postman application then change the method to GET and address to localhost:8080/api/products then click Send button.

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "timestamp": "2019-03-07T13:16:34.935+0000",
&nbsp; &nbsp; "status": 401,
&nbsp; &nbsp; "error": "Unauthorized",
&nbsp; &nbsp; "message": "Unauthorized",
&nbsp; &nbsp; "path": "/api/products"
}

Next, change the method to POST then address to localhost:8080/api/auth/register then fill the body with raw data as below image then click Send button.

You will get the response in the bottom panel of Postman.

{
&nbsp; &nbsp; "message": "User registered successfully"
}

Next, change the address to localhost:8080/api/auth/login and change the body as below then click Send button.

{ "email":"[email&nbsp;protected]", "password": "q1w2we3r4" }

You will see this response in the bottom panel of Postman.

{
&nbsp; &nbsp; "username": "[email&nbsp;protected]",
&nbsp; &nbsp; "token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo"
}

Copy the token then back to the GET product. Add a header with the name Authorization and the value that paste from a token that gets by login with additional Bearer prefix (with space) as below.

Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpbmZvQGRqYW13YXJlLmNvbSIsInJvbGVzIjpbeyJpZCI6IjVjODBjNjIzYjIwMTkxNGIyYTY5N2U4ZCIsInJvbGUiOiJBRE1JTiJ9XSwiaWF0IjoxNTUxOTY0OTc3LCJleHAiOjE1NTE5Njg1Nzd9.j5CHZ_LCmeQtdxQeH9eluxVXcOsHPWV1p8WnBn0CULo

You should see this response after clicking the Send button.

[
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp; &nbsp; "id": "5c80dc6cb20191520567b68a",
&nbsp; &nbsp; &nbsp; &nbsp; "prodName": "Dummy Product 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodDesc": "The Fresh Dummy Product in The world part 1",
&nbsp; &nbsp; &nbsp; &nbsp; "prodPrice": 100,
&nbsp; &nbsp; &nbsp; &nbsp; "prodImage": "https://dummyimage.com/600x400/000/fff"
&nbsp; &nbsp; }
]

You can test the POST product with the token in headers using the same way.

That it’s, the Securing RESTful API with Spring Boot, Security, and Data MongoDB tutorial. You can get the full source code from our GitHub.

Learn More

Build a Simple CRUD App with Spring Boot and Vue.js

Creating RESTful APIs with NodeJS and MongoDB Tutorial

MongoDB with Python Crash Course - Tutorial for Beginners

How to build RESTful APIs with ASP.NET Core

Understanding the basics of RESTful APIs

Developing RESTful APIs with Lumen (A PHP Micro-framework)

Java Programming Masterclass for Software Developers

Java In-Depth: Become a Complete Java Engineer!

JSP, Servlets and JDBC for Beginners: Build a Database App

JSP, Servlet, JSLT + Hibernate: A complete guide