1. Introducción

En este POST veremos como securizar de forma simplificada una aplicación SpringBoot utilizando gradle como gestor de paquetes. Definiremos dos usuarios en memoria con sus respectivos roles que serán cargados por el módulo SpringSecurity y mediante los cuáles podremos realizar el proceso de autenticación y autorización correspondiente.
Os podéis descargar el código de mi GitHub que se encuentra aquí.

Tecnologías empleadas:

  • Java 8
  • Gradle 3.1
  • SpringBoot 1.5.2.RELEASE
  • Spring 4.3.7.RELEASE
  • SpringSecurity 1.5.2.RELEASE

2. ¿Qué es Spring Security?

SpringSecurity es una parte del proyecto de Spring que permite securizar aplicaciones. Básicamente resuelve dos problemas:

  • Autenticación: Proceso por el cual un usuario valida unas credenciales contra el sistema y adquiere unos roles.
  • Autorización: Proceso por el cual se le da permiso a un usuario para acceder a un recurso. Esto dependerá de los roles asignados.

3. Estructura del proyecto

3. Requisitos previos

El proyecto ha sido montado sobre gradle, así que si no lo tienes instalado deberás.

  1. Descargar gradle de la web oficial
  2. Descomprimir los binarios en un directorio dentro de tu filesystem
  3. Importar el directorio /bin del gradle a la variable de entorno PATH

Para verificar que hemos configurado gradle correctamente deberemos hacer

gradle -v

4. Ficheros

Añadimos las dependencias spring-boot-starter-thymeleaf y spring-security. La dependencia spring-boot-starter-thymeleaf es igual que la spring-boot-starter pero además incorpora soporte para sistemas de plantillas thymeleaf.

group 'com.jorgehernandezramirez.spring.springboot'
version '1.0-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

sourceCompatibility = 1.8

springBoot {
    mainClass = "com.jorgehernandezramirez.spring.springboot.security.Application"
}

repositories {
    mavenCentral()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    compile("org.springframework.boot:spring-boot-starter-security")
}

Definimos nuestra clase main.

package com.jorgehernandezramirez.spring.springboot.security;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public Application(){
        //For Spring
    }

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

Definimos un servicio rest de pruebas, en donde se especificarán 4 controladores.

  • /public/resource
  • /private/resource
  • /private/admin/resource1
  • /private/admin/resource2
package com.jorgehernandezramirez.spring.springboot.security.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ResourceController {

    @RequestMapping("/public/resource")
    public String getPublicResource() {
        return "Public resource";
    }

    @RequestMapping("/private/resource")
    public String getPrivateResouce() {
        return "Private resource";
    }

    @RequestMapping("/private/admin/resource1")
    public String getAdminResouce1() {
        return "Admin resource1";
    }

    @PreAuthorize(value = "hasRole('ROLE_ADMIN')")
    @RequestMapping("/private/admin/resource2")
    public String getAdminResouce2() {
        return "Admin resource2";
    }

}

A continuación viene la magia. Queremos securizar nuestra aplicación para que:

  • /public/resource. Sea un recurso público y se pueda acceder sin realizar el proceso de autenticación
  • /private/resource. Sea un recurso privado y sólo se puede acceder una vez realizado el proceso de autenticación
  • /private/admin/resource1. Sea un recurso privado y sólo pueda acceder un usuario con rol ADMIN
  • /private/admin/resource2. Sea un recurso privado y sólo pueda acceder un usuario con rol ADMIN
package com.jorgehernandezramirez.spring.springboot.security.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .antMatchers("/private/admin/**").access("hasRole('ROLE_ADMIN')")
                .antMatchers("/private/**").authenticated()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .and()
                .logout()
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin").password("admin").roles("ADMIN").and()
                .withUser("jorge").password("jorge").roles("USER");
    }
}

– En la función configure es donde establecemos la autorización para cada recurso. Además configuramos el formulario de login que se debe cargar en caso de requerir autenticación por parte de un usuario que accede a un recurso privado. En el ejemplo de arriba, como no se especifica ningún formulario spring-security cargará uno por defecto.

Para realizar la securización sobre los recursos podemos utilizar los siguientes métodos / anotaciones

  • permitAll(). Define que el recurso el público.
  • authenticated(). Define que debemos estar logados para acceder al recurso.
  • access(“hasRole(‘ROLE_ADMIN’)”). Define que debemos tener el rol ADMIN para acceder al recurso
  • @Preauthorize . Si te has fijado, en el fichero ResourceController hemos puesto esta anotación sobre la función getAdminResouce2. De esta forma podemos definir el tipo de autorización necesaria para acceder a este controlador.

Para más información sobre las expresiones que podemos utilizar para llevar a cabo la autenticación sobre recursos ir a la web de spring.

– Por otra parte en la función configureGlobal definimos cómo va a ser el proceso de autenticación. En este caso definimos en memoria los usuarios sus contraseñas y roles.

5. Probando la aplicación

Accedemos a /private/admin/resource1

Como el recurso es privado y no estamos logados, spring-security nos redirige al formulario de login. Después de autenticarnos con nuestras credenciales accedemos al recurso.