Spring Security – Recuérdame

Para recordar a los usuarios en la computadora mientras inician sesión en un sitio web, a través de Spring Security podemos mantener eso usando la opción «Recordarme». Spring Security envía una cookie al navegador. Esto tendrá un límite de tiempo específico y hasta ese momento la cookie es válida y en ese período de tiempo, para la URL dada, es posible el inicio de sesión automático. Veamos a través de un código de aplicación de muestra para ver la funcionalidad de trabajo.

Proyecto de ejemplo

Spring Security proporciona dos enfoques para implementar recordar-me

  • Enfoque de token basado en hash
  • Enfoque de token persistente.

En nuestro ejemplo, estamos tomando el enfoque de token persistente en el que se usa una base de datos u otro mecanismo de almacenamiento persistente y es útil almacenar los tokens generados.

Estructura del proyecto:

Project Structure

 

Este es un proyecto impulsado por maven. Veamos el pom.xml

XML

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.gfg.springsecurity</groupId>
  <artifactId>spring-security-remember-me-sample-application</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>spring-security-remember-me-sample-application</name>
  <packaging>war</packaging>
  <properties>
    <!-- It is good to use the later versions
          to get the full supportivity -->
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp.jstl</groupId>
      <artifactId>javax.servlet.jsp.jstl-api</artifactId>
      <version>1.2.1</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-dbcp2</artifactId>
      <version>2.1.1</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>6.0.6</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <!-- Maven jetty plugin for testing war -->
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.4.8.v20171121</version>
      </plugin>
      <!-- To avoid errors, it is mandatory to add this plugin -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.3.1</version>
      </plugin>
    </plugins>
  </build>
</project>

En el lado de la base de datos, estamos usando MySQL. Por lo tanto, esas configuraciones se dan en pom.xml. Para probar la aplicación para recordar, necesitamos tener algunas tablas de muestra en la base de datos. Deje que la base de datos de muestra sea ‘geeksforgeeks’. las tablas de muestra son ‘usuarios’ y ‘autoridades’. Para la autenticación ‘recuérdame’, también necesitamos ‘persistent_logins’ para almacenar los tokens generados

use geeksforgeeks;
-- users
create table users(
    username varchar(50) not null primary key,
    password varchar(100) not null,
    enabled boolean not null
);
-- authorities 
create table authorities(
    username varchar(50) not null,
    authority varchar(50) not null,
    constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on geekAuthorities (username,authority);

create table persistent_logins(
    username varchar(50) not null,
    series varchar(64) primary key,
    token varchar(64) not null,
    last_used timestamp not null
);

Insertemos algunos datos en la tabla de usuarios y autoridades con fines de prueba

-- Let us create a user with admin and password as password@123
-- While storing into the database let us store as encoded password with BCryptPasswordEncoder
-- For password@123, it will be $2a$10$USD5XrNWIpf2sLnGJ62/v.hTtSIY1vdeF7v8Y4YaNJhTftbX1HBwi
insert into users(username,password,enabled)
    values('admin','$2a$10$hbxecwitQQ.dDT4JOFzQAulNySFwEpaFLw38jda6Td.Y/cOiRzDFu',true);
insert into authorities(username,authority) 
    values('admin','ROLE_ADMIN');

 

Para obtener la contraseña codificada, usando un código de muestra, podemos obtenerla

Java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  
//.....
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// Specify the required password here
String password = "password@123"; 
String encodedPassword = passwordEncoder.encode(password);
//---

Ahora vamos a comprobar la información de conectividad de la base de datos. Está disponible en la carpeta src/main/resources

src/main/resources/database.properties

mysql.dirver=com.mysql.jdbc.Driver
mysql.jdbcUrl=jdbc:mysql://localhost:3306/geeksforgeeks?serverTimezone=UTC # using geeksforgeeks
mysql.username=root  # Change the appropriate username here
mysql.password=****  # Change the appropriate password here

Comencemos creando la clase @Configuration y dentro de ella definamos el método @Bean para DataSource 

ApplicationConfiguration.java

Java

import javax.sql.DataSource;
  
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
  
@Configuration
@PropertySource("classpath:database.properties")
public class ApplicationConfiguration {
  
  @Autowired
  private Environment environment;
  
  @Bean
  public DataSource getDataSource() {
    BasicDataSource basicDataSource = new BasicDataSource();
    basicDataSource.setDriverClassName(environment.getProperty("mysql.dirver"));
    basicDataSource.setUrl(environment.getProperty("mysql.jdbcUrl"));
    basicDataSource.setUsername(environment.getProperty("mysql.username"));
    basicDataSource.setPassword(environment.getProperty("mysql.password"));
    return basicDataSource;
  }
}

Configuración de Spring Security

  1. Se necesita la creación de un filtro de servlet springSecurityFilterChain para proteger y validar todas las direcciones URL mediante la creación de una clase @Configuration.
  2. Necesita registrar el filtro springSecurityFilterChain con war. 
  3. Se requiere el método RememberMe() de la clase HttpSecurity para habilitar la autenticación de recordarme. 
  4. Invoque el método tokenRepository() con el argumento PersistentTokenRepository y almacene los tokens generados en la tabla de la base de datos.

Veamos cómo hacer estos

WebSecurityConfiguration.java

Java

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
  
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  
  @Autowired
  private DataSource dataSource;
  
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  
    auth.jdbcAuthentication().dataSource(dataSource)
        .usersByUsernameQuery("select username, password, enabled"
            + " from users where username=?")
        .authoritiesByUsernameQuery("select username,authority  "
            + "from authorities where username=?")
        .passwordEncoder(passwordEncoder());
   
  }
  
  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().anyRequest().hasAnyRole("ADMIN", "USER")
    .and()
    .authorizeRequests().antMatchers("/login**").permitAll()
    .and()
    .formLogin().loginPage("/login").loginProcessingUrl("/loginAction").permitAll()
    .and()
    .logout().logoutSuccessUrl("/login").permitAll()
    .and()
    .rememberMe().rememberMeParameter("remember-me").tokenRepository(tokenRepository())
    .and()
    .csrf().disable();
      
     
  }
  
  @Bean
  public PersistentTokenRepository tokenRepository() {
    JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl=new JdbcTokenRepositoryImpl();
    jdbcTokenRepositoryImpl.setDataSource(dataSource);
    return jdbcTokenRepositoryImpl;
  }
}

SecurityWebApplicationInitializer.java

Java

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
  
public class SecurityWebApplicationInitializer 
  extends AbstractSecurityWebApplicationInitializer {
  
}

La configuración de Spring MVC se realiza mediante el uso de JSP Views para la parte de la vista

WebConfiguration.java

Java

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.gfg.spring.controller" })
public class WebConfiguration implements WebMvcConfigurer {
  
  @Override
  public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.jsp().prefix("/WEB-INF/views/").suffix(".jsp");
  }
  
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/login").setViewName("login");
  }
}

La inicialización y la configuración del contenedor de servlet se pueden ver usando

MvcWebApplicationInitializer.java

Java

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
  
public class MvcWebApplicationInitializer 
      extends AbstractAnnotationConfigDispatcherServletInitializer {
  
  // Load database and spring security configuration
  @Override
  protected Class<?>[] getRootConfigClasses() {
    return new Class[] { ApplicationConfiguration.class, WebSecurityConfiguration.class };
  }
  
  // Load spring web configuration
  @Override
  protected Class<?>[] getServletConfigClasses() {
    return new Class[] { WebConfiguration.class };
  }
  
  @Override
  protected String[] getServletMappings() {
    return new String[] { "/" };
  }
  
}

Veamos ahora la clase de controlador.

SampleController.java

Java

import java.security.Principal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
  
@Controller
public class SampleContoller {
  @GetMapping("/")
  public String index(Model model, Principal principal) {
    model.addAttribute("message", "Welcome! You are logged by using " + principal.getName() + " username");
    return "index";
  }
}

Veamos ahora la parte de la vista JSP

iniciar sesión.jsp

HTML

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
   pageEncoding="ISO-8859-1"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<!DOCTYPE html>
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
      <title>Spring Security</title>
      <style>
         table#sample tr:nth-child(odd) {
         background-color: #0074ab ;
         color: yellow ;
         }
         table#sample tr:nth-child(even) {
         background-color: #e6f7ff ;
         }
         table#sample {
         border-collapse: collapse ;
         }
         table#sample td {
         padding: 5px ;
         }
         table#sample caption {
         font-style: italic ;
         background-color: black ;
         color: white ;
         }
      </style>
   </head>
   <body background="#FFFFFF">
      <center>
         <h1>Spring Security - Remember Me Example</h1>
         <h4>Sample Login Form</h4>
         <form action='<spring:url value="/loginAction"/>' method="post">
            <table id = "sample">
               <tr>
                  <td>Username</td>
                  <td><input type="text" name="username"></td>
               </tr>
               <tr>
                  <td>Password</td>
                  <td><input type="password" name="password"></td>
               </tr>
               <tr>
                  <td><input type="checkbox" name="remember-me"></td>
                  <td>Remember me on this Computer</td>
               </tr>
               <tr>
                  <td align="center" colspan="2"><button type="submit">Login</button></td>
               </tr>
            </table>
         </form>
         <br/>
      </center>
   </body>
</html>

índice.jsp

HTML

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
   pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
      <title>Spring Security</title>
   </head>
   <body>
      <h1>Spring Security - Remember Me Example</h1>
      <h2>${message}</h2>
      <form action="/logout" method="post">
         <input value="Logout" type="submit">
      </form>
   </body>
</html>

Como este es el proyecto maven, primero construyamos la aplicación desde el símbolo del sistema de la siguiente manera

mvn clean install

 

Ejecute la aplicación utilizando

mvn jetty:run

 

Probemos ahora presionando http://localhost:8080/

 

admin/password@123 debe proporcionarse como credenciales. Como es el usuario disponible en la tabla de usuarios y esa contraseña se guarda de forma codificada. Como se selecciona la opción recordarme, en la base de datos, podemos ver una entrada en ‘persistent_logins’

 

Al mismo tiempo, también podemos verificar lo mismo en las cookies. A medida que se usa el navegador Chrome, verifiquemos eso a través de las opciones de configuración del navegador Chrome

 

 

A partir de ahora, las credenciales se almacenan en cookies y también en DB debido al mecanismo de persistencia, después de que cerramos el navegador y lo abrimos nuevamente, podemos ver la página de índice cuando presionamos http://localhost:8080 . Esta es la ventaja de la opción Recuérdame. Esto será posible hasta el momento de la expiración. Además, los datos de las cookies no deben borrarse. Aquí para nuestro ejemplo, hasta el 24 de junio, las cookies no se borran, podemos ver la funcionalidad ‘Recordarme’ habilitada.

Publicación traducida automáticamente

Artículo escrito por priyarajtt y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *