Security

Spring Security Angular 6 Basic Authentication Example

1. Introduction

In this post, we feature a comprehensive Example on Spring Security, Spring Boot and Angular to demonstrate how you can secure your Angular application using Basic Authentication via Spring Security. You will learn to use Spring Boot for quickly building a Java backend layer and then adding a maven dependency for making it secure. We will use Angular as a modern TypeScript based platform for building web based applications.

Before diving deep, we would like to showcase the application that we are going to build in the next sections.
 
 

Spring Security Angular 6 Basic Authentication - Application Overview
Application Overview

Above is an online book store that displays the listing of books. All the information displayed comes from a REST endpoint exposed using the Spring Boot Application. Initially, we will keep the endpoint non-secure so that the Angular app is able to display the books. Then, as a next step, we will secure this REST endpoint and make the necessary changes to our Angular app for accessing the data using basic authentication.

Tip
We will build this application using a step by step approach that will help you to follow along but if you are a seasoned developer, you may jump directly to the end to see the working code below.

2. Technologies used

The example code in this article was built and run using:

  • Angular 6
  • Spring Boot 2.0.5.RELEASE
  • Java 1.8.0_102
  • Maven 3.2.5
  • Visual Studio Code 1.27.1
  • IntelliJ IDEA 14.0.3

3. Build Java back-end using Spring Boot

There are many ways for creating a Spring Boot project. You may create using:

We will not delve deeper into each of the above ways and will use the first mechanism i.e. Spring Initializr

3.1 Create the starter project

Go to https://start.spring.io, enter the group and artifact and choose Web as the dependency. Press Generate Project.

Spring Security Angular 6 Basic Authentication - Spring Initializr Screen
Spring Initializr Screen

You will get a .zip file that you need to unzip in a directory which will house your application code.

3.2 Run the application

Execute the following command to run the Spring Boot application

mvn spring-boot:run

Once the application has started, open the browser and go to http://localhost:8080

3.3 Add a REST endpoint

In the application class BookbuddiesApplication, add the @RestController annotation and define a new @RequestMapping

BookbuddiesApplication.java

package com.vishwasgup.bookbuddies;

import com.vishwasgup.bookbuddies.model.Book;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@SpringBootApplication
@RestController
public class BookbuddiesApplication {
    @CrossOrigin(origins = "http://localhost:4200")
    @RequestMapping("/books")
    public Map home() {
        Map model = new HashMap();
        model.put("content", getListOfBooks());
        return model;
    }

    private Book[] getListOfBooks() {
        // Create few books
        Book book1 = new Book("Chinua Achebe", "Nigeria",
                "https://upload.wikimedia.org/wikipedia/en/6/65/ThingsFallApart.jpg", "English",
                "https://en.wikipedia.org/wiki/Things_Fall_Apart", 209, "Things Fall Apart", 1958);

        Book book2 = new Book("Hans Christian Andersen", "Denmark",
                "https://upload.wikimedia.org/wikipedia/commons/5/5b/Hans_Christian_Andersen_%281834_painting%29.jpg", "Danish",
                "https://en.wikipedia.org/wiki/Fairy_Tales_Told_for_Children._First_Collection", 784, "Fairy tales", 1836);

        Book book3 = new Book("Dante Alighieri", "Italy",
                "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e2/Michelino_DanteAndHisPoem.jpg/450px-Michelino_DanteAndHisPoem.jpg",
                "Italian", "https://en.wikipedia.org/wiki/Divine_Comedy", 1928, "The Divine Comedy", 1315);

        return new Book[]{book1, book2, book3};
    }

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

Now, restart the application, open the browser and go to http://localhost:8080/books. You should see the information of all the books in JSON format as seen below

Spring Security Angular 6 Basic Authentication - response on browser
Rest response on browser before enabling security

4. Build front-end application using Angular

Similar to creating the Spring Boot application, there are several ways to build the starter Angular application. We will use Angular CLI.
Execute the following command to spin off a skeleton Angular application with the name bookbuddies-ui

ng new bookbuddies-ui

The command might take few minutes for creating configuration files and bringing in all the dependencies. It will also create a very simple application for you. Once the command completes, execute

ng serve

This command will start the application, open the browser and go to http://localhost:4200 and you will see the default Angular page.

We now need to create some components to provide visualization to our web page. Angular CLI again comes very handy in creating Angular artifacts e.g. components, services etc. For this example, we will create header component for displaying the application name, book component for displaying all the books received from back-end. We will also create a service which will be responsible for invoking our REST endpoint.

For giving our application good look and feel, we will use Bootstrap CSS Framework.

4.1 Installing Bootstrap

Go to the root of the application and install Bootstrap via node package manager npm

npm install --save bootstrap@3

Additionally, when using a project created with Angular CLI 6+ (you may check this using ng -v ), you’ll have an angular.json file and you need to add Bootstrap to the styles[] array as shown

angular.json

"styles": [
  "node_modules/bootstrap/dist/css/bootstrap.min.css",
  "src/styles.css"
]

4.2 Create header and book components

For creating component classes, use the following Angular CLI command

ng generate component header

or, in short

ng g c header

In the header component, add the following code

header.component.html

<nav class="navbar navbar-default">
  <div class="conatiner-fluid">
    <div class="navbar-header">
      <a href="#" class="navbar-brand">Book Buddies</a>
    </div>
  </div>
</nav>

Before building the book component, let’s first create an interface for book class. This is similar to creating domain objects or POJOs in Java. Create a new folder called interfaces and define Book class as below

book.ts

export class Book {
  author: string;
  country: string;
  imageLink: string;
  language: string;
  link: string;
  pages: number;
  title: string;
  year: number;
}

Now create the book component using ng g c book command and inject the service class book.service.ts that we will create shortly

book.component.ts

import { Component, OnInit } from '@angular/core';
import { BookService } from '../book.service';
import { Book } from '../interfaces/book';

@Component({
  selector: 'app-book',
  templateUrl: './book.component.html',
  styleUrls: ['./book.component.css']
})
export class BookComponent implements OnInit {

  books: Book[];

  constructor(private bookService: BookService) { }

  ngOnInit() {
    this.bookService.getBooks()
      .subscribe(
        (books: any[]) => {
          this.books = books['content'];
        },
        (error) => console.log(error)
      );
  }
}

In the book template, add the following code

book.component.html

<div *ngFor="let book of books">
  <div class="row margin-top='1px;'">
    <div class="col-xs-4">
      <h2>{{ book.title }}</h2>
      <h4>Author: {{ book.author }} </h4>
      <h4>Country: {{ book.country }}</h4>
      <h4>Language: {{ book.language }}</h4>
      <h4>Pages: {{ book.pages }}</h4>
      <h4>Year: {{ book.year }}</h4>
      <h4>Wikipedia: <a [href]="book.link">Link</a></h4>
    </div>
    <div class="col-xs-8">
      <img [src]="book.imageLink" alt="" class="img-responsive" style="max-height: 175px;">
    </div>
  </div>
</div>

4.3 Create book service

It’s time to create a service class that we will use for fetching the data from our back-end. We can create a service class using

ng generate service book

or, in short

ng g s book

Add the following code to Book service

book.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class BookService {

  constructor(private http: HttpClient) { }

  getBooks() {
    const url = 'http://localhost:8080/books';
    return this.http.get(url);
  }
}

4.4 A glance at the App module

For using the http service in our service class, we need to import HttpClientModule

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { BookComponent } from './book/book.component';

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent,
    BookComponent
  ],
  imports: [
    BrowserModule, HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

4.5 Access the application

After going through the above steps, you should be able to see the list of books by accessing http://localhost:4200 in the browser.

5. Secure the REST endpoint

To enable security, simply add the following dependency to the pom.xml

pom.xml

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

Restart your Java application by using the command

mvn spring-boot:run

Now, try accessing http://localhost:8080/books. You should see a browser dialog asking for the credentials as shown below

Spring Security Angular 6 Basic Authentication - Basic auth credentials screen
Basic auth credentials screen

The default user name is user and you can get the password from the console in which you started the Spring Boot application as shown below

Spring Security Angular 6 Basic Authentication - Spring boot generated password
Spring boot generated password

If you want to use your own password instead of using the generated one, you need to specify the following in application.properties. As an example, we are using secret123.

application.properties

spring.security.user.name=user
spring.security.user.password=secret123

Try accessing http://localhost:8080/books again, fill in the credentials in the browser dialog and you should see the books JSON again but now secured using basic authentication.

6. Update Angular application for accessing the secure REST endpoint

Try accessing http://localhost:4200 in the browser and you will notice that nothing comes back from the back-end. We need to send the basic authorization headers in our Http request. So, let’s update the code in our book service, specifically the method getBooks()

book.service.ts

...
  getBooks() {
    const url = 'http://localhost:8080/books';
    const headers = new HttpHeaders({ Authorization: 'Basic ' + btoa('user:secret123') });
    return this.http.get(url, { headers });
  }
}

Again, verify http://localhost:4200 in the browser. Does it work?

No, and the reason for its not working is explained in the next section.

7. Cross-origin resource sharing (CORS)

We need to support the CORS protocol for our Angular service to be able to invoke an endpoint on different domain. By different domain, we mean that our front-end application running on http://localhost:4200 is requesting a resource on another domain i.e. http://localhost:8080. Hence, on server, we need to configure the CORS. This is done by providing the support for CORS protocol which involves a “pre-flight” OPTIONS request and some other headers to specify the behavior of the caller that is allowed.

Let’s create a CorsFilter class which specify the above configuration

CorsFilter.java

package com.vishwasgup.bookbuddies;

import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CorsFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token, Cache-Control");
        response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
        if ("OPTIONS".equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            filterChain.doFilter(request, response);
        }
    }
}

There is one additional step here and that is to signal the Spring Security to allow the pre-flight check from the browser. This is done by overriding the configure method of WebSecurityConfigurerAdapter.

WebSecurityConfig.java

package com.vishwasgup.bookbuddies;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;

@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
        http
                .authorizeRequests()
                .antMatchers("/")
                .permitAll()
                .anyRequest()
                .fullyAuthenticated()
                .and()
                .httpBasic()
                .and().csrf().disable();
    }
}

Verify http://localhost:4200 in the browser and you should be able to see the listing of books again.
Congratulations!

8. Spring Security Angular 6 Basic Authentication – Summary

In this example we developed a simple REST API using Spring Boot. We secured this endpoint using Spring Security. And then we used the popular front-end framework Angular for accessing this secure API.

9. Download the Source Code

This was the Spring Security Angular 6 Basic Authentication Example.

Download
You can download the full source code of this example here: SpringSecurityAngular6BasicAuthenticationExample

Vishwas Gupta

Vishwas has 14 years of experience in requirement gathering and analysis, architectural, component and interface design and development of web-based applications in multiple domains. With vast exposure to variety of technologies including Angular, Java, JEE, Spring, Hibernate, Web Services etc., he is currently working as Technical Architect in Location Intelligence domain. He holds a Master degree in Electronics and Communication Engineering from Malaviya National Institute of Technology, Jaipur (India). He is TOGAF 9.1 Certified Enterprise Architect, BEA 8.1 Certified System Administrator and Sun Certified Java Programmer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Anil Kumar Reddy A
Anil Kumar Reddy A
5 years ago

With the above code Get method is working fine. But Post and Put methods are not working

error: {error: SyntaxError: Unexpected token a in JSON at position 0 at JSON.parse () at XMLHttp…, text: “added”}
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: “Http failure during parsing for http://localhost:8080/addBook
name: “HttpErrorResponse”
ok: false
status: 200
statusText: “OK”
url: “http://localhost:8080/addBook”

Rupendra sharma
4 years ago

Hi javacodegeeks,

Thanks for the sharing wonderful info-value:)

We have also written publish an article on the same, check out the below URL and please leave a valuable Comment if you found its good.

https://tecmentor.in/spring-boot/convert-jar-to-war-in-spring-boot/

Regards
tecmentor.in

Back to top button