Skip to content

Commit 5f37dcf

Browse files
committed
Insertado proyecto Spring Boot
1 parent 656391e commit 5f37dcf

27 files changed

+1058
-0
lines changed

Llamadas a mi primer proyecto Spring Boot.postman_collection.json

+485
Large diffs are not rendered by default.

pom.xml

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-starter-parent</artifactId>
8+
<version>3.2.2</version>
9+
<relativePath/> <!-- lookup parent from repository -->
10+
</parent>
11+
<groupId>dev.acobano</groupId>
12+
<artifactId>mi-primer-proyecto-con-keycloak</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<name>mi-primer-proyecto-con-keycloak</name>
15+
<description>Proyecto de iniciación para practicar de forma didáctica la integración de Keycloak en un proyecto Spring Boot.</description>
16+
<properties>
17+
<java.version>17</java.version>
18+
</properties>
19+
<dependencies>
20+
<dependency>
21+
<groupId>org.springframework.boot</groupId>
22+
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.springframework.boot</groupId>
26+
<artifactId>spring-boot-starter-security</artifactId>
27+
</dependency>
28+
<dependency>
29+
<groupId>org.springframework.boot</groupId>
30+
<artifactId>spring-boot-starter-web</artifactId>
31+
</dependency>
32+
33+
<dependency>
34+
<groupId>org.projectlombok</groupId>
35+
<artifactId>lombok</artifactId>
36+
<optional>true</optional>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.springframework.boot</groupId>
40+
<artifactId>spring-boot-starter-test</artifactId>
41+
<scope>test</scope>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.springframework.security</groupId>
45+
<artifactId>spring-security-test</artifactId>
46+
<scope>test</scope>
47+
</dependency>
48+
49+
<!-- https://mvnrepository.com/artifact/org.keycloak/keycloak-admin-client -->
50+
<dependency>
51+
<groupId>org.keycloak</groupId>
52+
<artifactId>keycloak-admin-client</artifactId>
53+
<version>22.0.5</version>
54+
</dependency>
55+
56+
<!-- https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-jaxrs -->
57+
<dependency>
58+
<groupId>org.jboss.resteasy</groupId>
59+
<artifactId>resteasy-jaxrs</artifactId>
60+
<version>3.15.6.Final</version>
61+
</dependency>
62+
63+
</dependencies>
64+
65+
<build>
66+
<plugins>
67+
<plugin>
68+
<groupId>org.springframework.boot</groupId>
69+
<artifactId>spring-boot-maven-plugin</artifactId>
70+
<configuration>
71+
<excludes>
72+
<exclude>
73+
<groupId>org.projectlombok</groupId>
74+
<artifactId>lombok</artifactId>
75+
</exclude>
76+
</excludes>
77+
</configuration>
78+
</plugin>
79+
</plugins>
80+
</build>
81+
82+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package dev.acobano.miprimerproyectoconkeycloak;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class MiPrimerProyectoConKeycloakApplication {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(MiPrimerProyectoConKeycloakApplication.class, args);
11+
}
12+
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dev.acobano.miprimerproyectoconkeycloak.configuracion;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
8+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10+
import org.springframework.security.config.http.SessionCreationPolicy;
11+
import org.springframework.security.web.SecurityFilterChain;
12+
13+
@Configuration
14+
@EnableWebSecurity
15+
@EnableMethodSecurity
16+
@RequiredArgsConstructor
17+
public class ConfiguracionSeguridad
18+
{
19+
@Autowired
20+
private JwtAutenticacionConversor jwtConversor;
21+
22+
@Bean
23+
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception
24+
{
25+
return httpSecurity
26+
.csrf(csrfConfigurer -> csrfConfigurer.disable())
27+
.authorizeHttpRequests(http -> http.anyRequest().authenticated())
28+
.oauth2ResourceServer(oAuth2Configurer -> {
29+
oAuth2Configurer.jwt( jwtConfigurer -> jwtConfigurer.jwtAuthenticationConverter(jwtConversor));
30+
})
31+
.sessionManagement(sessionConfigurer ->
32+
sessionConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
33+
.build();
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package dev.acobano.miprimerproyectoconkeycloak.configuracion;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.core.convert.converter.Converter;
5+
import org.springframework.security.authentication.AbstractAuthenticationToken;
6+
import org.springframework.security.core.GrantedAuthority;
7+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
8+
import org.springframework.security.oauth2.jwt.Jwt;
9+
import org.springframework.security.oauth2.jwt.JwtClaimNames;
10+
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
11+
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
12+
import org.springframework.stereotype.Component;
13+
14+
import java.util.Collection;
15+
import java.util.Map;
16+
import java.util.Objects;
17+
import java.util.Set;
18+
import java.util.stream.Stream;
19+
20+
@Component
21+
public class JwtAutenticacionConversor implements Converter<Jwt, AbstractAuthenticationToken>
22+
{
23+
private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter =
24+
new JwtGrantedAuthoritiesConverter();
25+
26+
@Value("${jwt.auth.converter.principle-attribute}")
27+
private String atributoNombreUsuario;
28+
29+
@Value("${jwt.auth.converter.resource-id}")
30+
private String clienteId;
31+
32+
@Override
33+
public AbstractAuthenticationToken convert(Jwt jwt)
34+
{
35+
Collection<GrantedAuthority> permisos = Stream.concat(
36+
jwtGrantedAuthoritiesConverter.convert(jwt).stream(),
37+
extraerRoles(jwt).stream()
38+
).toList();
39+
return new JwtAuthenticationToken(jwt, permisos, getNombreSolicitud(jwt));
40+
}
41+
42+
private Collection<? extends GrantedAuthority> extraerRoles(Jwt jwt)
43+
{
44+
Map<String, Object> recursosAcceso;
45+
Map<String, Object> recurso;
46+
Collection<String> roles;
47+
48+
if(Objects.isNull(jwt.getClaim("resource_access")))
49+
return Set.of();
50+
else
51+
{
52+
recursosAcceso = jwt.getClaim("resource_access");
53+
54+
if (Objects.isNull(recursosAcceso.get(clienteId)))
55+
return Set.of();
56+
else
57+
{
58+
recurso = (Map<String, Object>) recursosAcceso.get(clienteId);
59+
60+
if (Objects.isNull(recurso.get("roles")))
61+
return Set.of();
62+
else
63+
{
64+
roles = (Collection<String>) recurso.get("roles");
65+
return roles.stream()
66+
.map(rol -> new SimpleGrantedAuthority("ROLE_".concat(rol)))
67+
.toList();
68+
}
69+
}
70+
}
71+
}
72+
73+
private String getNombreSolicitud(Jwt jwt)
74+
{
75+
String nombreSolicitud = JwtClaimNames.SUB;
76+
77+
if (!Objects.isNull(atributoNombreUsuario))
78+
nombreSolicitud = atributoNombreUsuario;
79+
80+
return jwt.getClaim(nombreSolicitud);
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package dev.acobano.miprimerproyectoconkeycloak.controladores;
2+
3+
import dev.acobano.miprimerproyectoconkeycloak.dto.UsuarioDTO;
4+
import dev.acobano.miprimerproyectoconkeycloak.servicios.interfaces.IServicioUsuarios;
5+
import org.keycloak.representations.idm.UserRepresentation;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.http.ResponseEntity;
8+
import org.springframework.security.access.prepost.PreAuthorize;
9+
import org.springframework.web.bind.annotation.*;
10+
11+
import java.net.URI;
12+
import java.net.URISyntaxException;
13+
import java.util.List;
14+
15+
//<>
16+
@RestController
17+
@RequestMapping("/api/keycloak/usuarios")
18+
@PreAuthorize("hasRole('ADMINISTRADOR-rol-cliente')")
19+
public class ControladorUsuarios
20+
{
21+
@Autowired
22+
private IServicioUsuarios servicioUsuarios;
23+
24+
@GetMapping
25+
public ResponseEntity<List<UserRepresentation>> listarUsuarios()
26+
{
27+
return ResponseEntity.ok(this.servicioUsuarios.listarUsuarios());
28+
}
29+
30+
@GetMapping("/{username}")
31+
public ResponseEntity<List<UserRepresentation>> buscarUsuariosPorUsername(@PathVariable String username)
32+
{
33+
return ResponseEntity.ok(this.servicioUsuarios.buscarUsuarioPorUsername(username));
34+
}
35+
36+
@PostMapping()
37+
public ResponseEntity<String> crearNuevoUsuario(@RequestBody UsuarioDTO usuarioDTO)
38+
throws URISyntaxException
39+
{
40+
String respuesta = this.servicioUsuarios.crearUsuario(usuarioDTO);
41+
return ResponseEntity.created(new URI("/api/keycloak/usuarios")).body(respuesta);
42+
}
43+
44+
@PutMapping("/{usuarioId}")
45+
public ResponseEntity<String> modificarDatosUsuario(@PathVariable String usuarioId, @RequestBody UsuarioDTO usuarioDTO)
46+
{
47+
this.servicioUsuarios.modificarUsuario(usuarioId, usuarioDTO);
48+
return ResponseEntity.ok("Usuario actualizado correctamente en el sistema.");
49+
}
50+
51+
@DeleteMapping("/{usuarioId}")
52+
public ResponseEntity<Void> eliminarUsuario(@PathVariable String usuarioId)
53+
{
54+
this.servicioUsuarios.eliminarUsuario(usuarioId);
55+
return ResponseEntity.noContent().build();
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package dev.acobano.miprimerproyectoconkeycloak.controladores;
2+
3+
import org.springframework.security.access.prepost.PreAuthorize;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.RequestMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
@RestController
9+
@RequestMapping("/api/v1")
10+
public class MiControlador
11+
{
12+
@GetMapping("/encargado")
13+
@PreAuthorize("hasRole('ENCARGADO-rol-cliente')")
14+
public String endpointEncargado()
15+
{
16+
return "Si estás leyendo esto, es porque has accedido a este endpoint con un usuario con rol de ENCARGADO.";
17+
}
18+
19+
@GetMapping("/admin")
20+
@PreAuthorize("hasRole('ADMINISTRADOR-rol-cliente')")
21+
public String endpointAdmin()
22+
{
23+
return "Si estás leyendo esto, es porque has accedido a este endpoint con un usuario con rol de ADMINISTRADOR.";
24+
}
25+
26+
@GetMapping("/combinado")
27+
@PreAuthorize("hasRole('ENCARGADO-rol-cliente') or hasRole('ADMINISTRADOR-rol-cliente')")
28+
public String endpointCombo()
29+
{
30+
return "Si estás leyendo esto, es porque has accedido a este endpoint con un usuario con rol de ENCARGADO" +
31+
"o con rol de ADMINISTRADOR.";
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dev.acobano.miprimerproyectoconkeycloak.dto;
2+
3+
import lombok.Builder;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.Value;
6+
7+
import java.util.Set;
8+
9+
@Value
10+
@RequiredArgsConstructor
11+
@Builder
12+
public class UsuarioDTO
13+
{
14+
private String username;
15+
private String email;
16+
private String firstName;
17+
private String lastName;
18+
private String password;
19+
private Set<String> roles;
20+
}

0 commit comments

Comments
 (0)