Spring Boot Angular Captcha Example

author-imageBy Dhiraj, 09 July,2018   1K

In this article, we will be integrating Google reCaptcha with a spring boot web applications with Angular as a client to protect websites from spam and abuse. The ReCaptcha will appear in an angular login form and once the form is submitted, it will be validated on the server side with the secret key along with the username and password. In the example, once the user credentials and the captcha is validated, post login page will appear.

In my previous articles, we created angular 5 example as well as angular 6 example and integrated JWT token based auth in an angular app. Hence, here we will not be demonstrating JWT integration instead we will be hardcoding the username and password in the backend code as dhiraj@devglan.com and password respectively.

We will be generating a sample angular 6 app using angular CLI and for the APIs we will be using existing Spring Boot REST APIs which we have created earlier with little modifications to integrate Google reCaptcha in the server side.

Generating Angular 6 Project

Both the CLI and generated project have dependencies that require Node 8.9 or higher, together with NPM 5.5.1 or higher for Angular 6.Angular v6 is the first release of Angular that unifies the Framework, Material and CLI. @angular/core now depends on.

  • TypeScript 2.7

  • RxJS 6.0.0

  • tslib 1.9.0

Run following command to generate angular 6 project in any location of your choice.

ng new client

Once the project is generated, you can import it in your favourite directory and get started with it. Following will be the final structure of our project. Also, you can run following commands to see angular 6 app running at localhost:4200

cd client
ng serve

The complete step by step instruction is provided in my angular 6 example here.

angular-captcha-project-strct

Google ReCaptcha SetUp

To set up Google ReCaptcha, head over to https://www.google.com/recaptcha/admin and register the site. Following is my screenshot.

google-recaptcha-register

Now, once you click on the regsiter button, you can see the site key and secret which will be used later for the integration.You can also find all the instructions on this page about integrating Google ReCaptcha with our code.In the screenshot below we can see the sample html and script tag required to place in our code for the client side integration. The secret key will be used in the server side while verifying the captcha with Google API.

google-recaptcha-client-integration

Angular Login with ReCaptcha

Following is a normal login page with an extra div element added for ReCaptcha as per the screenshot above.

login.component.html

<div class="row login">

  <div class="col-md-6 login-form">
    <h1>Login </h1>
    <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
      <div class="form-group">
        <label for="email">Email address:</label>
        <input type="email" class="form-control" formControlName="email" id="email">
        <div *ngIf="submitted && loginForm.controls.email.errors" class="error">
          <div *ngIf="loginForm.controls.email.errors.required">Email is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="pwd">Password:</label>
        <input type="password" class="form-control" formControlName="password" id="pwd">
        <div *ngIf="submitted && loginForm.controls.password.errors" class="error">
          <div *ngIf="loginForm.controls.password.errors.required">Password is required</div>
        </div>
      </div>
      <div class="form-group">
        <div class="g-recaptcha" data-sitekey="6Lff7WIUAAAAAG2UuSYktpVi2Mz7tB6cgXnO1Tez"></div>
          <div *ngIf="captchaError" class="error">Recaptcha not verified.</div>
      </div>
      <button class="btn btn-default">Login</button>
      <div *ngIf="invalidLogin" class="error">
        <div>{{loginResponse}}</div>
      </div>
    </form>
  </div>
</div>

In the .tsfile, we have declared grecaptcha to get the response once the form is submitted. Make sure to include ReCatcha api.js in your index.html before declaring this variable. Following is the script tag that is required to include in the index.html

<script src='https://www.google.com/recaptcha/api.js'></script>
login.component.ts
import { Component, OnInit } from '@angular/core';
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {Router} from "@angular/router";
import {LoginRequest} from "../model/login.request.model";
import {UserService} from "../service/user.service";
declare var grecaptcha: any;

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

  loginForm: FormGroup;
  submitted: boolean = false;
  invalidLogin: boolean = false;
  captchaError: boolean = false;
  loginResponse: string;
  constructor(private formBuilder: FormBuilder, private router: Router, private userService: UserService) { }

  onSubmit() {
    this.submitted = true;
    if (this.loginForm.invalid) {
      return;
    }
    const response = grecaptcha.getResponse();
    if (response.length === 0) {
      this.captchaError = true;
      return;
    }
    let login = new LoginRequest();
    login.email = this.loginForm.controls.email.value;
    login.password = this.loginForm.controls.password.value;
    login.recaptchaResponse = response;
    this.userService.login(login).subscribe(data => {
      if(data.status === 200) {
        //locally store the token
        this.router.navigate(['list-user']);
      } else {
        this.invalidLogin = true;
        this.loginResponse = data.message;
      }
      grecaptcha.reset();
    });
  }

  ngOnInit() {
    this.loginForm = this.formBuilder.group({
      email: ['', Validators.required],
      password: ['', Validators.required]
    });
  }
}

The response string from grecaptcha.getResponse() will be sent to server for further validation.Following is the model class.

login.request.model.ts
export class LoginRequest {

  id: number;
  firstName: string;
  lastName: string;
  email: string;
}

login.response.model.ts
export class LoginResponse {
  status : number;
  message : string;
  username : string;
  token : string;
}


Following is the implementation in our service class at Angular side.

user.service.ts
@Injectable()
export class UserService {
  constructor(private http: HttpClient) { }
  baseUrl: string = 'http://localhost:8080/users';

  login(loginRequest: LoginRequest): Observable {
    return this.http.post(this.baseUrl + '/login', loginRequest);
  }

}

Spring Boot SetUp

Spring boot team has really made spring boot environment setup easy by providing default initializers.Open the url https://start.spring.io/ and generate the project as follow.

spring-boot-project-initializers

Now unzip user-portal.zip and import into java IDE. Following will be the final structure.

google-recaptcha-server-project-strct

Spring Boot Rest APIs

Now, let us start by creating our APIs first.We have UserController where all the APIs for CRUD operation is exposed.The @CrossOrigin is used to allow Cross-Origin Resource Sharing (CORS) so that our angular application running on different server can consume these APIs from a browser. For simplicity I am only including the login API here.

In real time application, we will have an AuthController for performing login. There are many articles posted on Devglan for login auth here.

UserController.java
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping({"/users"})
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public LoginResponse login(@RequestBody LoginRequest loginRequest){
        return userService.login(loginRequest);
    }
	
	...
	...
	
}
LoginRequest
public class LoginRequest {

    private String email;
    private String password;
    private String recaptchaResponse;
	
	Getters and setters
	
}

Following is the service class implementation that will actually make DB calls and validate the user credentials along with the captcha. To make the example simple, I have hardcoded the username and password.

UserServiceImpl.java
    @Override
    public LoginResponse login(LoginRequest loginRequest) {
        LoginResponse loginResponse = new LoginResponse();
        boolean captchaVerified = captchaService.verify(loginRequest.getRecaptchaResponse());
        if(!captchaVerified) {
            loginResponse.setMessage("Invalid captcha");
            loginResponse.setStatus(400);
        }
        if(captchaVerified && loginRequest.getEmail().equals("dhiraj@devglan.com") && loginRequest.getPassword().equals("password")) {
            loginResponse.setMessage("Success");
            loginResponse.setStatus(200);
            loginResponse.setUsername("dhiraj");
            loginResponse.setToken("token");
        }else {
            loginResponse.setMessage("Invalid credentials.");
            loginResponse.setStatus(400);
        }
        return loginResponse;
    }

Here, captchaService is injected as a dependency which will validate the captcha response coming from the client and return either true or false after validation. Following is the screenshot of the Google API response.

google-recaptcha-api-response LoginResponse.java
public class LoginResponse {

    private int status;
    private String message;
    private String username;
    private String token;
	
	Getters and setters
	
}

Google Recaptcha Server Side Integration

Below is the implementation of CaptchaService.java that will make a POST request to https://www.google.com/recaptcha/api/siteverify in order to validate the captcha response coming from the client side.

CaptchaService.java
package com.devglan.userportal.service;

import com.devglan.userportal.RecaptchaResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

@PropertySource("classpath:application.properties")
@Service
public class CaptchaService {

    private final RestTemplate restTemplate;

    public CaptchaService(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    @Value("${google.recaptcha.secret.key}")
    public String recaptchaSecret;
    @Value("${google.recaptcha.verify.url}")
    public String recaptchaVerifyUrl;

    public boolean verify(String response) {
        MultiValueMap param= new LinkedMultiValueMap<>();
        param.add("secret", recaptchaSecret);
        param.add("response", response);

        RecaptchaResponse recaptchaResponse = null;
        try {
            recaptchaResponse = this.restTemplate.postForObject(recaptchaVerifyUrl, param, RecaptchaResponse.class);
        }catch(RestClientException e){
            System.out.print(e.getMessage());
        }
       if(recaptchaResponse.isSuccess()){
            return true;
        }else {
            return false;
        }
    }
}

Following entries are required in application.properties file that we get from Google ReCatcha during registration.

google.recaptcha.secret.key=6Lff7WIUAAAAAP_MkVOaQlxvrUGNtEC_OLIA6s5L
google.recaptcha.verify.url=https://www.google.com/recaptcha/api/siteverify

Testing Google ReCaptcha App

Now, we are all set to test our captcha implementation. For this let us first run our angular app with ng serve and hit localhost:4200

google-recaptcha-user-login

Once the login is successfull,you can see the list page.

google-recaptcha-post-login

Conclusion

In this article we discussed about integrating Google ReCaptcha with a spring boot angular app. The complete source code can be found on my Github page here. If you have any suggestions, please let me know in the comments below:

Further Reading on Angular JS

1. Material Sidenav Example

2. Rxjs Tutorial

3. Angular Data Table Example

4. Spring Boot Jwt Auth

5. Angular Material App

If You Appreciate What We Do Here On Devglan, You Should Consider: