| Tabla de contenidos |
|---|
¿Qué login debo incluir?
...
Con Actualmente, en el Portal de Servicios se utiliza el token POSE, tenemos dos vías para implementar el login, utilizar el propio login del Portal o implementar un cliente CAS para nuestra aplicación, y dependerá de dónde se sitúe esta.
- Se utilizará el login del portal si la aplicación está totalmente integrada, es decir, si va a ser una aplicación que esté bajo micampus.
- Si es una aplicación independiente, habrá que implementar el cliente CAS en nuestro backend.
En ambos casos, ya que se usa siempre el CAS como sistema de autenticación (o bien la propia aplicación o bien el Portal de Servicios) deberemos asegurarnos que nuestra aplicación está dada de alta siguiendo la guía Alta de aplicaciones en CAS.
En principio, todas las aplicaciones desarrolladas con FundeWebJS irán al Portal de Servicios, así que vamos a ver cómo hacerlo en ese caso. La implementación del cliente CAS está al final de esta documentación, en un desplegable. Cómo comprobar el token es común a ambos métodos.
Login con el Portal de Servicios
En nuestro backend, cuando usemos el login a través del Portal de Servicios, sólo necesitaremos poder comprobar el token. Para ello, necesitaremos incluir el secret en nuestro proyecto. En local, debemos incluir el secret como una variable de entorno en Eclipse. Esto lo haremos yendo a Run As → Run Configurations..., tendremos que seleccionar el proyecto correspondiente en la barra de la izquierda, e irnos a la pestaña Environment. Ahí le damos a Add.., e introducimos la variable ES_UM_JWT_SECRET, y el correspondiente valor del secret:
Pulsamos en Apply, y podemos cerrar la ventana.
Después, incluiremos la variable es.um.jwt.secret en el archivo application.properties de nuestro proyecto, así:
| Bloque de código | ||
|---|---|---|
| ||
es.um.jwt.secret=${ES_UM_JWT_SECRET} |
El valor del secret variará en cada entorno, pero para poder probarlo en local, tendremos que incluir el secret del entorno correspondiente con el que estemos haciendo las pruebas. Al hacerlo de esta manera, evitamos que el secret quede expuesto en GitLab.
Para desarrollar en local, utilizaremos el token de micampusdesa, así que hará falta incluir el secret de desarrollo. Para obtener este secret, preguntad a MNCS, pues por seguridad no lo compartimos aquí.
Una vez hecho esto, ya solo nos queda comprobar el token en los métodos privados de nuestro servicio REST, que está explicado en el apartado Cómo comprobar el token.
Cómo comprobar el token
Recibiremos el token por parte del front-end como un String, en el header Authorization, que tendrá la siguiente forma:
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
# Bearer token
"Bearer tokenCifrado" |
Para realizar la comprobaciones en cada petición que llegue, podemos utilizar filtros que añadiremos a la configuración de seguridad de nuestra aplicación.
Filtro de autorización
Para comprobar el token en cada petición que se haga, tendremos un filtro de autorización. Un ejemplo, podría ser este:
pero se está migrando para utilizar token OAuth. Por lo tanto, de momento tenemos que incluir ambos; nuestra aplicación tendrá login dual, aceptará tanto token POSE como token OAuth.
Login con token POSE
En nuestro backend, cuando usemos el login a través del Portal de Servicios, sólo necesitaremos poder comprobar el token. Para ello, necesitaremos incluir el secret en nuestro proyecto. En local, debemos incluir el secret como una variable de entorno en Eclipse. Esto lo haremos yendo a Run As → Run Configurations..., tendremos que seleccionar el proyecto correspondiente en la barra de la izquierda, e irnos a la pestaña Environment. Ahí le damos a Add.., e introducimos la variable ES_UM_JWT_SECRET, y el correspondiente valor del secret:
Pulsamos en Apply, y podemos cerrar la ventana.
Después, incluiremos la variable es.um.jwt.secret en el archivo application.properties de nuestro proyecto, así:
| Bloque de código | ||
|---|---|---|
| ||
es.um.jwt.secret=${ES_UM_JWT_SECRET} |
El valor del secret variará en cada entorno, pero para poder probarlo en local, tendremos que incluir el secret del entorno correspondiente con el que estemos haciendo las pruebas. Al hacerlo de esta manera, evitamos que el secret quede expuesto en GitLab.
Para desarrollar en local, utilizaremos el token de micampusdesa, así que hará falta incluir el secret de desarrollo. Para obtener este secret, preguntad a MNCS, pues por seguridad no lo compartimos aquí.
Una vez hecho esto, ya solo nos queda comprobar el token en los métodos privados de nuestro servicio REST, que está explicado en el apartado Cómo comprobar el token.
Cómo comprobar el token
Recibiremos el token por parte del front-end como un String, en el header Authorization, que tendrá la siguiente forma:
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
# Bearer token
"Bearer tokenCifrado" |
Para realizar la comprobaciones en cada petición que llegue, podemos utilizar filtros que añadiremos a la configuración de seguridad de nuestra aplicación.
Filtro de autorización
Para comprobar el token en cada petición que se haga, tendremos un filtro de autorización. Un ejemplo, podría ser este:
| Bloque de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
public class JwtAuthorizationFilter extends OncePerRequestFilter {
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager
.getLogger( JwtAuthorizationFilter.class | ||||||||
| Bloque de código | ||||||||
| ||||||||
public class JwtAuthorizationFilter extends OncePerRequestFilter {
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager
.getLogger( JwtAuthorizationFilter.class );
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain ) throws ServletException, IOException {
if ( isPrivate( httpServletRequest ) ) {
final String token = httpServletRequest.getHeader( AUTHORIZATION_HEADER );
// INCLUIR AQUÍ LAS COMPROBACIONES NECESARIAS DE LA APLICACIÓN
// Con getClaim sólo se comprueba si el token es correcto
jwtTokenUtil.getClaim( token );
}
filterChain.doFilter( httpServletRequest, httpServletResponse );
}
private boolean isPrivate( HttpServletRequest httpServletRequest ) {
final RequestMatcher privateUrl = new AntPathRequestMatcher( PRIVATE_PREFIX + "/**" );
return privateUrl.matches( httpServletRequest );
}
} |
...
| Bloque de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
/**
* ServicioNotFoundException
*/
public class UnauthorizedException extends ServicioException {
private static final long serialVersionUID = 1L;
private static final HttpStatus status = HttpStatus.UNAUTHORIZED;
public UnauthorizedException() {
super( "No tiene acceso para solicitar este servicio " );
}
@Override
public HttpStatus getStatus() {
return UnauthorizedException.status;
}
} |
Login con
...
Como se comenta al inicio, todas las aplicaciones FundeWebJS irán al Portal de Servicios, por lo que no será necesario implementar un cliente CAS. Es decir, esta explicación queda obsoleta, pero se mantiene en esta documentación por si en algún caso excepcional fuese necesaria.
...
| title | Haz click aquí para mostrar explicación... |
|---|
Esto es una versión inicial, todavía no tiene suplantación, se irá añadiendo conforme vayamos teniendo tiempo para hacerlo.
Tablas de base de datos
Para cada aplicación, necesitaremos crearnos varias tablas de base de datos. En primer lugar, necesitaremos una tabla para mantener la sesión, es decir, para tener un registro de los tokens que se han dado, y poder comprobar si han expirado o no. Esta tabla podría llamarse CAS_TOKEN_SESSION, y podemos crearla con este script, cambiando el esquema (que en este caso es PORTALFUNDEWEB):
| Bloque de código | ||||||
|---|---|---|---|---|---|---|
| ||||||
CREATE TABLE "PORTALFUNDEWEB"."CAS_TOKEN_SESSION"
( "UUID" VARCHAR2(40 BYTE) NOT NULL ENABLE,
"LOGIN" VARCHAR2(60 BYTE) NOT NULL ENABLE,
"START_TIME" TIMESTAMP (6) NOT NULL ENABLE,
"LAST_REFRESH" TIMESTAMP (6) NOT NULL ENABLE,
"USER_AGENT" VARCHAR2(256 BYTE),
"PLACE" VARCHAR2(256 BYTE),
"LDAP_GROUPS" VARCHAR2(256 BYTE),
CONSTRAINT "CAS_TOKEN_SESSION_PK" PRIMARY KEY ("UUID")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "PORTALFUNDEWEB" ENABLE
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "PORTALFUNDEWEB" ; |
Después, seguimos el diagrama que se muestra en esta página de la wiki de FundeWeb 2.0. Se muestra tanto el esquema como el script para crear las tablas. Se tienen tres tablas, Usuarios, Roles y Objetivos, con tablas intermedias entre ellas. Lo más habitual es que se utilicen los Usuarios y los Roles, los Objetivos son para dar permisos más detallados, para ciertas partes de la aplicación. Vamos a ver las entidades correspondientes a estas tablas.
En nuestro proyecto tendremos que incluir las entidades correspondientes e implementar UserDetails (en nuestro ejemplo son las clases de es.um.atica.proyecto.entities y la clase ServiceUserDetails de es.um.atica.logincas.model, las podemos descargar en el apartado Resto de clases). Además, si nuestro frontend necesita consultar datos de usuario, debemos implementar los métodos correspondientes en la clase UserEndpoints (en nuestro ejemplo sólo hay un /whoami básico).
Cosas a añadir en el proyecto
Dependencias en el pom.xml
Tendremos que añadir estas dependencias al pom.xml:
| Bloque de código | ||||||||
|---|---|---|---|---|---|---|---|---|
| ||||||||
<!-- CAS -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency> |
Parámetros en application.properties
En nuestro application.properties, además de tener definido el datasource de la aplicación, tenemos que añadir las siguientes propiedades:
| Bloque de código | ||||
|---|---|---|---|---|
| ||||
login.config.serviceurl=https://atica-67-165.atica.um.es:8080/api/entrada
login.config.sso.loginurl=https://sso.um.es/cas/login
login.config.sso.logouturl=https://sso.um.es/cas/logout
login.config.sso.serverurl=https://sso.um.es/cas/
es.um.jwt.secret=---
security.enable-csrf=false
es.um.token.session.expiration=1000
# Tiempo de vida del token (en minutos)
es.um.jwt.expires=15
server.servlet.session.persistent=false
# Entorno - Se comprobara si esta variable es "local" o "desarrollo"
es.um.p15s.environment=local |
La variable login.config.serviceurl es a la que tendremos que acceder para redirigir al cas. Se utiliza un secret para codificar el token, en la variable es.um.jwt.secret. En cada entorno se sustituirá por el secret correspondiente. Si queremos probar el login con el portal, como será este el que nos devuelva el token cifrado, tendremos que poner el mismo secret que se utilice en el entorno correspondiente con el que hagamos las pruebas.
Configuración en la clase inicial
En la calse que inicia nuestra aplicación, que se llamará NombreProyectoApplication.java, tendremos que añadir varias cosas, dejándola como en este ejemplo, pero con el método main que nos venía:
| Bloque de código | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
package es.um.atica.----;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSessionEvent;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.jasig.cas.client.validation.Cas30ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.event.EventListener;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.client.RestTemplate;
import es.um.atica.----.cas.services.UsuarioService;
@SpringBootApplication
public class LogincasApplication {
@Value( "${login.config.serviceurl}" )
private String serviceurl;
@Value( "${login.config.sso.loginurl}" )
private String ssoLoginUrl;
@Value( "${login.config.sso.logouturl}" )
private String ssoLogoutUrl;
@Value( "${login.config.sso.serverurl}" )
private String ssoServerUrl;
public static void main( String[] args ) {
SpringApplication.run( LogincasApplication.class, args );
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public ServiceProperties serviceProperties() {
final ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setSendRenew( false );
serviceProperties.setService( serviceurl );
serviceProperties.setAuthenticateAllArtifacts( true );
return serviceProperties;
}
@Bean
@Primary
public AuthenticationEntryPoint authenticationEntryPoint( ServiceProperties sP ) {
final CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint() {
@Override
protected String createServiceUrl( final HttpServletRequest request, final HttpServletResponse response ) {
String serviceUrl = serviceProperties().getService();
final String callback = request.getParameter( "callback" );
if ( callback != null ) {
serviceUrl += "/" + callback;
}
return serviceUrl;
}
};
entryPoint.setLoginUrl( ssoLoginUrl );
entryPoint.setServiceProperties( sP );
return entryPoint;
}
@Bean
public TicketValidator ticketValidator() {
return new Cas30ServiceTicketValidator( ssoServerUrl );
}
@Bean
public UserDetailsService generateUserDetailsService() {
return new UsuarioService();
}
@Bean
public CasAuthenticationProvider casAuthenticationProvider() {
final CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setServiceProperties( serviceProperties() );
provider.setTicketValidator( ticketValidator() );
provider.setUserDetailsService( generateUserDetailsService() );
provider.setKey( "CAS_PROVIDER_P15S" );
return provider;
}
@Bean
public SecurityContextLogoutHandler securityContextLogoutHandler() {
return new SecurityContextLogoutHandler();
}
@Bean
public LogoutFilter logoutFilter() {
final LogoutFilter logoutFilter = new LogoutFilter( ssoLogoutUrl, securityContextLogoutHandler() );
logoutFilter.setFilterProcessesUrl( "/logout/cas" );
return logoutFilter;
}
@Bean
public SingleSignOutFilter singleSignOutFilter() {
final SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
// singleSignOutFilter.setCasServerUrlPrefix( ssoServerUrl );
singleSignOutFilter.setIgnoreInitConfiguration( true );
return singleSignOutFilter;
}
@EventListener
public SingleSignOutHttpSessionListener singleSignOutHttpSessionListener( HttpSessionEvent event ) {
return new SingleSignOutHttpSessionListener();
}
} |
Configuración de seguridad
Nuestra clase de configuración de seguridad (la que tenemos con la anotación @EnableWebSecurity, se llame ConfiguraciónSeguridad, SecurityConfig, o como queramos) quedará así (no hay que duplicar esta clase, debemos tener una en el proyecto, si tenemos otras cosas metidas en nuestro SecurityConfig, como ModelMapper por ejemplo, se debe añadir lo que contiene esta):
| Bloque de código | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
package es.um.atica.----.cas.config;
import static es.um.atica.----.Constants.SESSION_TOKEN_HEADER;
import static es.um.atica.----.Constants.LOCAL_ENV;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.web.CasAuthenticationFilter;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import es.um.atica.----.cas.handlers.CustomizedLogoutHandler;
import es.um.atica.----.cas.handlers.CustomizedUrlAuthenticationSuccessHandler;
@EnableWebSecurity
@EnableGlobalMethodSecurity( securedEnabled = true )
public class ConfiguracionSeguridad extends WebSecurityConfigurerAdapter {
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager
.getLogger( ConfiguracionSeguridad.class );
private final AuthenticationProvider authenticationProvider;
private final LogoutFilter logoutFilter;
private final SingleSignOutFilter singleSignOutFilter;
private final AuthenticationEntryPoint authenticationEntryPoint;
private final ServiceProperties serviceProperties;
@Value( "${es.um.p15s.environment}" )
private String environment;
@Autowired
public ConfiguracionSeguridad ( AuthenticationEntryPoint aep, LogoutFilter lF, SingleSignOutFilter ssF,
CasAuthenticationProvider casAuthenticationProvider, ServiceProperties sp ) {
authenticationProvider = casAuthenticationProvider;
logoutFilter = lF;
singleSignOutFilter = ssF;
authenticationEntryPoint = aep;
serviceProperties = sp;
}
// Autenticacion básica con usuario autogenerado
@Override
protected void configure( HttpSecurity http ) throws Exception {
http.cors().and().csrf().disable().addFilter( casAuthenticationFilter( serviceProperties ) ).authorizeRequests()
.regexMatchers( "^/entrada(\\/)?(\\?.+)?$" ).authenticated().and().authorizeRequests()
.regexMatchers( "/public/*", "/private/*" ).permitAll().and().httpBasic()
.authenticationEntryPoint( authenticationEntryPoint ).and().logout()
.logoutSuccessHandler( customizedLogoutHandler() ).deleteCookies( SESSION_TOKEN_HEADER ).and()
.addFilterBefore( singleSignOutFilter, CasAuthenticationFilter.class )
.addFilterBefore( logoutFilter, LogoutFilter.class );
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns( Arrays.asList( "*" ) );
configuration.setAllowedMethods( Arrays.asList( "*" ) );
configuration.setAllowedHeaders( Arrays.asList( "*" ) );
configuration.setAllowCredentials( true );
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration( "/**", configuration );
return source;
}
@Bean
public CustomizedLogoutHandler customizedLogoutHandler() {
return new CustomizedLogoutHandler();
}
@Override
protected void configure( AuthenticationManagerBuilder auth ) throws Exception {
auth.authenticationProvider( authenticationProvider );
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return new ProviderManager( Arrays.asList( authenticationProvider ) );
}
@Bean
AuthenticationDetailsSource<HttpServletRequest, ServiceAuthenticationDetails> dynamicServiceResolver() {
return ( HttpServletRequest context ) -> {
String urlProtocol = "http:";
if ( !LOCAL_ENV.equals( environment ) ) {
urlProtocol = "https:";
}
final String url = context.getRequestURL().toString().replace( "http:", urlProtocol );
log.error( "-- ORIGINAL Dynamic URL: " + context.getRequestURL().toString() + " ---" );
return new ServiceAuthenticationDetails() {
private static final long serialVersionUID = 1L;
@Override
public String getServiceUrl() {
log.error( "-- Dynamic URL: " + url + " ---" );
return url;
}
};
};
}
@Bean
public CasAuthenticationFilter casAuthenticationFilter( ServiceProperties sP ) throws Exception {
final CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setServiceProperties( sP );
filter.setAuthenticationManager( authenticationManager() );
filter.setFilterProcessesUrl( "/entrada/*" );
filter.setAuthenticationDetailsSource( dynamicServiceResolver() );
filter.setAuthenticationSuccessHandler( new CustomizedUrlAuthenticationSuccessHandler() );
return filter;
}
} |
Descargar clases necesarias
Podemos descargar estas clases, y el resto de clases necesarias aquí: Clases cliente CAS.rar.
Se incluyen las entidades correspondientes a base de datos, y sus correspondientes repositorios. La estructura de paquetes del proyecto, incluyendo el cliente cas, quedaría así:
...
CustomizedLogoutHandler.java, CustomizedUrlAuthenticationSuccessHandler.java
...
token OAuth
En primer lugar, debemos tener configurada nuestra aplicación para que soporte OAuth, lo haremos siguiendo esta guía: Migración del backend a soporte oAuth.
Una vez hecho esto, para obtener el token en nuestros servicios REST, utilizamos @AuthenticationPrincipal:
| Bloque de código | ||
|---|---|---|
| ||
@GetMapping( PRIVATE_PREFIX + API_VERSION + "/resguardos" )
public ResponseEntity getResguardos( @AuthenticationPrincipal Jwt jwt,
@RequestHeader( value = "Authorization", required = true ) String token ) { ... } |
Como vemos, tendremos tanto esto como el header. Cuando entremos con token POSE, jwt será null. Podemos obtener el identificador del token simplemente con:
| Bloque de código | ||
|---|---|---|
| ||
jwt.getSubject() |
Con la configuración que habremos añadido, se tienen dos endpoints:
| Bloque de código | ||
|---|---|---|
| ||
@RequestMapping( "/", "${app.server.path}" ) |
Esto es, la ruta base del proyecto para token POSE, y la ruta base + "/grupo/proyecto" para OAuth.
Aunque tengamos estos endpoints diferentes, los métodos no están duplicados, pero no será necesario distinguir qué tipo de token recibimos. Cada tipo de token puede llevar un identificador diferente, pero hemos hecho una librería que hace de cliente del servicio de recursos humanos para obtener el identificador de cada usuario, lo vemos en el siguiente apartado.
Librería de cliente RRHH para obtener datos de usuario
En la mayoría de aplicaciones querremos obtener el identificador del usuario logeado para pedir datos a base de datos. Para obtener este identificador, rrhh dispone de un servicio, y tenemos una librería que hace de cliente de éste, facilitando la llamada sin tener que diferenciar entre el tipo de token que tenemos y permitiendo cachear las respuestas.
Para incluir la librería, en el pom.xml, en el bloque de dependencies, añadiremos esta:
| Bloque de código | ||
|---|---|---|
| ||
<!-- Servicios RRHH Client -->
<dependency>
<groupId>es.um.atica.fundewebjs.fundewebjs-api</groupId>
<artifactId>fundewebjs-serviciosrrhh-client</artifactId>
<version>1.0.2-SNAPSHOT</version>
</dependency> |
Y en el bloque de repositories, si no lo tenemos, este:
| Bloque de código | ||
|---|---|---|
| ||
<repository>
<id>fundewebjs.archiva.atica.umu.es</id>
<name>ATICA - UMU Repository - FundeWebJS</name>
<url>https://archiva.um.es/archiva/repository/FundeWebJS/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository> |
Después, para utilizarla, simplemente la incluimos con @Autowired:
| Bloque de código |
|---|
@Autowired
private ServiciosRrhhClientService serviciosRrhhClient; |
Y llamamos al método getAfiliacion pasándole el subject del token, el token como String (cogido con @RequestHeader en nuestro método REST) y el Jwt (cogido con @AuthenticationPrincipal). Esto nos devuelve un objeto de tipo AfiliacionDTO, que contiene información del usuario. En concreto, se compone de estos campos:
| Bloque de código |
|---|
private String identificador;
private String letra;
private String tipoIdentificador;
private String nombre;
private String apellido1;
private String apellido2;
private Date fechaNacimiento;
private String sexo;
private String nacionalidad;
private DireccionDTO direccion;
private List<String> telefonos;
private List<String> emails;
private List<CentroAlumnoDTO> centros; |
Todos tienen sus correspondientes getters. Lo más normal es que utilicemos el identificador, pero los demás también pueden ser útiles. En el caso de los emails, si un usuario tiene varios, el primero de la lista es el principal.
