Spring Boot - OAuth2 y JWT

Saludo, amigos! Mañana comenzarán las primeras clases en el nuevo hilo del curso "Desarrollador en Spring Framework" . En este sentido, tradicionalmente compartimos material útil sobre el tema.



En este artículo, exploraremos el uso de OAuth2 y JWT junto con Spring Boot y Spring Security.

Servidor de autorizaciones


Un servidor de autorización es el componente más importante en la arquitectura de seguridad de la API web. El servidor de autorización actúa como un único punto de autorización y permite que sus aplicaciones y puntos finales HTTP definan las funciones de su aplicación.

Servidor de recursos


El servidor de autorización proporciona a los clientes un token de acceso para acceder a los puntos finales HTTP del servidor de recursos. Un servidor de recursos es una colección de bibliotecas que contiene puntos finales HTTP, recursos estáticos y páginas web dinámicas.

OAuth2


OAuth2 es un protocolo de autorización que permite que un cliente (tercero) acceda a los recursos de su aplicación. Para construir una aplicación OAuth2, necesitamos conocer el Tipo de concesión (código de autorización), el ID del cliente y el secreto del Cliente.

Token JWT


Un token JWT es un token web JSON. Se utiliza para representar información de identificación segura (reclamos) entre dos partes. Puede encontrar más información sobre los tokens JWT en www.jwt.io.

Vamos a hacer una aplicación OAuth2 utilizando tokens JWT, que incluirá un servidor de autorización y un servidor de recursos.

Primero, necesitamos agregar las dependencias a nuestro archivo de compilación.

Los usuarios de Maven pueden agregar las siguientes dependencias a pom.xml .

 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> 

Nota del traductor: para Java anterior a 9, también debe agregar las siguientes dependencias:

 <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.2.11</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.2.11</version> </dependency> 

Los usuarios de Gradle pueden agregar las siguientes dependencias al archivo build.gradle .

 compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-web') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('org.springframework.security:spring-security-test') compile("org.springframework.security.oauth:spring-security-oauth2") compile('org.springframework.security:spring-security-jwt') compile("org.springframework.boot:spring-boot-starter-jdbc") compile("com.h2database:h2:1.4.191") 

donde

  • Spring Boot Starter Security - implementa Spring Security
  • Spring Security OAuth2 : implementa estructuras OAUTH2 para el funcionamiento del servidor de autorización y el servidor de recursos.
  • Spring Security JWT - Genera tokens JWT
  • Spring Boot Starter JDBC : acceso a la base de datos para la verificación del usuario.
  • Spring Boot Starter Web : proporciona puntos finales HTTP.
  • Base de datos H2 : almacena información del usuario para autenticación y autorización.

El archivo completo pom.xml muestra a continuación.

 <?xml version = "1.0" encoding = "UTF-8"?> <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.tutorialspoint</groupId> <artifactId>websecurityapp</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>websecurityapp</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 

Gradle — build.gradle

 buildscript { ext { springBootVersion = '1.5.9.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' group = 'com.tutorialspoint' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-web') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('org.springframework.security:spring-security-test') compile("org.springframework.security.oauth:spring-security-oauth2") compile('org.springframework.security:spring-security-jwt') compile("org.springframework.boot:spring-boot-starter-jdbc") compile("com.h2database:h2:1.4.191") } 

Ahora, agregue las anotaciones @EnableAuthorizationServer y @EnableResourceServer al archivo principal de la aplicación Spring Boot para que la aplicación funcione como un servidor de autorización y un servidor de recursos.

Agregue también un punto final HTTP simple (/ productos) para acceder a la API protegida por Spring Security utilizando un token JWT.

 package com.tutorialspoint.websecurityapp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableAuthorizationServer @EnableResourceServer @RestController public class WebsecurityappApplication { public static void main(String[] args) { SpringApplication.run(WebsecurityappApplication.class, args); } @RequestMapping(value = "/products") public String getProductName() { return "Honey"; } } 

Defina una clase POJO para almacenar información de autenticación de usuario.

 package com.tutorialspoint.websecurityapp; import java.util.ArrayList; import java.util.Collection; import org.springframework.security.core.GrantedAuthority; public class UserEntity { private String username; private String password; private Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>(); public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Collection<GrantedAuthority> getGrantedAuthoritiesList() { return grantedAuthoritiesList; } public void setGrantedAuthoritiesList(Collection<GrantedAuthority> grantedAuthoritiesList) { this.grantedAuthoritiesList = grantedAuthoritiesList; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } 

A continuación, para la autenticación, agregue la clase CustomUser, que extiende org.springframework.security.core.userdetails.User .

 package com.tutorialspoint.websecurityapp; import org.springframework.security.core.userdetails.User; public class CustomUser extends User { private static final long serialVersionUID = 1L; public CustomUser(UserEntity user) { super(user.getUsername(), user.getPassword(), user.getGrantedAuthoritiesList()); } } 

Cree una @Repository- para recuperar información del usuario de la base de datos y agregue los privilegios ROLE_SYSTEMADMIN. Esta clase también se usará en CustomDetailsService .

 package com.tutorialspoint.websecurityapp; import java.sql.ResultSet; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Repository; @Repository public class OAuthDao { @Autowired private JdbcTemplate jdbcTemplate; public UserEntity getUserDetails(String username) { Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>(); String userSQLQuery = "SELECT * FROM USERS WHERE USERNAME=?"; List<UserEntity> list = jdbcTemplate.query(userSQLQuery, new String[] { username }, (ResultSet rs, int rowNum) -> { UserEntity user = new UserEntity(); user.setUsername(username); user.setPassword(rs.getString("PASSWORD")); return user; }); if (list.size() > 0) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_SYSTEMADMIN"); grantedAuthoritiesList.add(grantedAuthority); list.get(0).setGrantedAuthoritiesList(grantedAuthoritiesList); return list.get(0); } return null; } } 

Para llamar al repositorio DAO, puede crear su UserDetailsService , heredando de org.springframework.security.core.userdetails.UserDetailsService , como se muestra a continuación.

 package com.tutorialspoint.websecurityapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class CustomDetailsService implements UserDetailsService { @Autowired OAuthDao oauthDao; @Override public CustomUser loadUserByUsername(final String username) throws UsernameNotFoundException { UserEntity userEntity = null; try { userEntity = oauthDao.getUserDetails(username); CustomUser customUser = new CustomUser(userEntity); return customUser; } catch (Exception e) { e.printStackTrace(); throw new UsernameNotFoundException("User " + username + " was not found in the database"); } } } 

A continuación, cree una @onfiguration- para habilitar la Web Security . Defina los parámetros de cifrado de contraseña ( BCryptPasswordEncoder ) y el bean AuthenticationManager en él.
Esta clase de SecurityConfiguration debe heredar de la clase WebSecurityConfigurerAdapter .

 package com.tutorialspoint.websecurityapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private CustomDetailsService customDetailsService; @Bean public PasswordEncoder encoder() { return new BCryptPasswordEncoder(); } @Override @Autowired protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customDetailsService).passwordEncoder(encoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated().and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.NEVER); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } 

Ahora agregue una clase para configurar OAuth2. En él, defina el ID de cliente, el secreto de cliente, JwtAccessTokenConverter, las claves privadas y públicas para firmar y verificar el token, y configure ClientDetailsServiceConfigurer para ámbitos de token válidos.

 package com.tutorialspoint.websecurityapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; @Configuration public class OAuth2Config extends AuthorizationServerConfigurerAdapter { private String clientid = "tutorialspoint"; private String clientSecret = "my-secret-key"; private String privateKey = "private key"; private String publicKey = "public key"; @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Bean public JwtAccessTokenConverter tokenEnhancer() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(privateKey); converter.setVerifierKey(publicKey); return converter; } @Bean public JwtTokenStore tokenStore() { return new JwtTokenStore(tokenEnhancer()); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore()) .accessTokenConverter(tokenEnhancer()); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory().withClient(clientid).secret(clientSecret).scopes("read", "write") .authorizedGrantTypes("password", "refresh_token").accessTokenValiditySeconds(20000) .refreshTokenValiditySeconds(20000); } } 

Ahora cree las claves privadas y públicas usando openssl .
Para generar una clave privada, puede usar los siguientes comandos:

 openssl genrsa -out jwt.pem 2048 openssl rsa -in jwt.pem 

Para clave pública:

 openssl rsa -in jwt.pem -pubout 

Para Spring Boot anterior a la versión 1.5, agregue la siguiente propiedad al archivo application.properties (para determinar el orden de filtrado de los recursos OAuth2).

 security.oauth2.resource.filter-order=3 

Si está utilizando un archivo YAML, agregue lo siguiente.

 security: oauth2: resource: filter-order: 3 

Ahora cree los data.sql schema.sql y data.sql en el classpath en el src/main/resources/directory para conectar la aplicación a la base de datos H2.

El archivo schema.sql ve así:

 CREATE TABLE USERS (ID INT PRIMARY KEY, USERNAME VARCHAR(45), PASSWORD VARCHAR(60));  data.sql: INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES ( 1, 'tutorialspoint@gmail.com','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG'); INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES ( 2, 'myemail@gmail.com','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG'); 

Nota - La contraseña en la tabla de la base de datos debe almacenarse en el formato Bcrypt Encoder.
Puede crear un archivo JAR ejecutable y ejecutar la aplicación Spring Boot utilizando los siguientes comandos Maven o Gradle.


Para Maven, puede usar el siguiente comando:

 mvn clean install 

Después de "CONSTRUIR ÉXITO" puede encontrar los archivos JAR en el directorio de target .

Para Gradle, puede usar el comando -

 gradle clean build 

Después de "BUILD SUCCESSFUL" puede encontrar los archivos JAR en el directorio build/libs .
Ahora ejecute el archivo JAR con el comando -

java –jar < JARFILE >

La aplicación se lanzó en Tomcat en el puerto 8080.



Ahora envíe una solicitud POST a través de POSTMAN para recibir un token OAUTH2.

http://localhost:8080/oauth/token
Ahora agregue los encabezados de solicitud:

  • Autorización: básica con su ID de cliente y secreto de cliente.
  • Content-Type - application / x-www-form-urlencoded



Y solicitar parámetros -

  • grant_type = contraseña
  • nombre de usuario = tu nombre
  • contraseña = tu contraseña



Ahora ejecute y obtenga access_token como se muestra.



Ahora cree una solicitud para la API del servidor de recursos con un token Bearer en el encabezado.



Obtenemos el resultado como se muestra a continuación.



Esperamos sus comentarios, y también le informamos que hasta el 31 de mayo puede unirse al curso a un precio especial.

Source: https://habr.com/ru/post/453664/


All Articles