Google reCaptcha security validation using Angular, Spring boot

What you’ll build

Project

in this article we'll build an application that uses google captcha using JAVA, Spring boot, Angular.

Keywords

Google Captcha

CAPTCHA or Completely Automated Public Turing test to Tell Computers and Humans Apart is a technique to distinguish between humans and computers. CAPTCHA is mainly used as a security (spam, brute force, etc) check to ensure only human users can pass through. Generally, computers or bots are not capable of solving a captcha.

Google reCaptcha

In order to use google recaptcha you need to sign up for an api key pair for your websites (sign up for an API key pair).

Site key: is the public key that will be used in our frontend (Angular 6) app.

Secret key: is the private key that will be used in our backend app (Spring boot).

Spring boot

Requirements

Maven dependencies

<dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20180813</version>
</dependency>

The json library is used to parse google captcha validation http response.

Java Code

package com.yamicode.captcha.rest;

import org.json.JSONObject;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;

@RestController
@CrossOrigin("*")
public class CaptchaRestController {
    @RequestMapping(value="verify/captcha", method = RequestMethod.POST)
    public ResponseEntity<?> validateReCaptha(@RequestBody Map<String, String> captchaMap){
        if(!isCaptchaValid("private-key", captchaMap.get("captcha"))){
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }
        return ResponseEntity.noContent().build();
    }

    public static boolean isCaptchaValid(String secretKey, String response) {
        try {
            String url = "https://www.google.com/recaptcha/api/siteverify?"
                    + "secret=" + secretKey
                    + "&response=" + response;
            InputStream res = new URL(url).openStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(res, Charset.forName("UTF-8")));

            StringBuilder sb = new StringBuilder();
            int cp;
            while ((cp = rd.read()) != -1) {
                sb.append((char) cp);
            }
            String jsonText = sb.toString();
            res.close();

            JSONObject json = new JSONObject(jsonText);
            return json.getBoolean("success");
        } catch (Exception e) {
            return false;
        }
    }

}

Replace private-key text with your private key.

Angular

Generate the project

ng new captcha-angular --routing
npm install ng-recaptcha --save
npm install ngx-toastr --save
ng g c components/yami-code-captcha --spec false
ng g s services/captcha --spec false

ng-recaptcha is the angular library that we will use.[ng-recaptcha]

ngx-toastr: Used just for notifications (not mandatory).

Angular code

<div>
  <re-captcha (resolved)="resolved($event)" siteKey="public-key"></re-captcha>
</div>

<button style="color: #fff;background-color: #1D71B8;border-color: #1D71B8;" [disabled]="token=='' || !token" (click)="onSubmit()" type="button">
  Send
</button>
import { Component, OnInit } from '@angular/core';
import { CaptchaService } from 'src/app/services/captcha.service';
import { ToastrService } from 'ngx-toastr';

@Component({
  selector: 'app-yami-code-captcha',
  templateUrl: './yami-code-captcha.component.html',
  styleUrls: ['./yami-code-captcha.component.css']
})
export class YamiCodeCaptchaComponent implements OnInit {
  token: String = '';
  constructor(private captchaService: CaptchaService, private toastr: ToastrService) { }

  ngOnInit() {
  }

  onSubmit() {
    this.captchaService.post(this.token)
      .subscribe(
        () => { this.toastr.success('form sent correctly'); },
        (error) => {
          this.toastr.error('invalid token');
        }
      );
  }


  resolved(captchaResponse: string) {
    this.token = captchaResponse;
    console.log(`Resolved captcha with response ${captchaResponse}:`);
  }

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

@Injectable({
  providedIn: 'root'
})
export class CaptchaService {
  url: string = environment.url + 'verify/captcha';

  constructor(private http: HttpClient) { }

  post(captcha) {
    return this.http.post(this.url, {captcha: captcha});
  }
}
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { YamiCodeCaptchaComponent } from './components/yami-code-captcha/yami-code-captcha.component';
import { HttpClientModule } from '@angular/common/http';
import { ToastrModule } from 'ngx-toastr';
import { RecaptchaModule } from 'ng-recaptcha';

@NgModule({
  declarations: [
    AppComponent,
    YamiCodeCaptchaComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    HttpClientModule,
    RecaptchaModule,
    ToastrModule.forRoot({ timeOut: 3000 }),
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
export const environment = {
  production: false,
  url: 'http://localhost:8080/'
};
export class AppModule { }

Demo

Source code:

The full implementation of this artcile can be found in the GitHub project. Download and unzip the source repository for this guide GIT, or clone it using Git: git clone https://github.com/YamiCode2016/recaptcha-spring-angular.git