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.