Versiones comparadas

Clave

  • Se ha añadido esta línea.
  • Se ha eliminado esta línea.
  • El formato se ha cambiado.

...

Dependiendo de la situación, se distinguen dos tipos de configuraciones:
- Global: Todos los endpoints de nuestra API requerirán el mismo nivel LoA establecido en su configuración.
- Por endpointmétodo: Configurar el nivel LoA de forma independiente para cada endpoint. 

...

  1. Si no existe en nuestro proyecto, crear una clase SecurityConfig.java nueva a partir del código de FundeWebJSSecurityConfig(ubicada en la librería fundewebjs-security).

    Bloque de código
    languagejava
    titleFundeWebJSSecurityConfig
    collapsetrue
    @Log4j2
    @Configuration
    public class FundeWebJSSecurityConfig {
    
    	@Value( "${server.scopes}" )
    	private String[] serverScopes;
    
    	@Value( "${app.server.path}" )
    	private String apiPath;
    
    
    	/**
    	 * Proveedor de gestion de acceoss
    	 */
    	@Bean( name = "fundeWebJsDefaultSecurityFilterChain" )
    	@ConditionalOnExpression( "${fdwjs.starter.security.enable:true}" )
    	public SecurityFilterChain filterChain( HttpSecurity http ) throws Exception {
    
    		log.debug( "Carga SecurityFilterChain desde FundewebJs-Starter" );
    
    		http.requestMatchers().antMatchers( "/public/**" ).and().requestMatchers().antMatchers( apiPath + "/**" ).and()
    		.sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS )
    		// configuro politica de sesion sin estado
    		.and().cors() // Aniado configuracion CORS por defecto
    		.and().csrf().disable().authorizeRequests().mvcMatchers( apiPath + "/public/**" ).permitAll()
    		.mvcMatchers( apiPath + "/**" ).hasAnyAuthority( serverScopes ).anyRequest().authenticated().and()
    		.addFilterAfter( new FundeWebJSLoggingAuthorizationFilter(), BearerTokenAuthenticationFilter.class )
    		.oauth2ResourceServer()
    		.jwt();
    
    		return http.build();
    	}
    
    }



  2. En el application.properties (de local y de los entornos en Helm Chart), añadir la siguiente property:

...

  1. En la clase con la configuración de seguridad (ver pasos 1 y 2 de la configuración común), añadir el siguiente Bean para configurar el filtro para LoA, estableciendo el nivel mínimo que se desea establecer en toda nuestra API:

    Bloque de código
        @Bean
    	public UmuJwtLoaAuthenticationFilter loaFilterBean() {
    		return new UmuJwtLoaAuthenticationFilter( UmuJwtLoaLevelEnum.SUBSTANTIAL);
    	}



  2. En la misma clase, en el método configure, añadir el filtro configurado en el paso anterior de la siguiente forma:

    Bloque de código
    languagejava
    .addFilterAfter( loaFilterBean(), BearerTokenAuthenticationFilter.class )

    Hay que añadirlo justo antes de  .oauth2ResourceServer().jwt().
    Por ejemplo:

    Bloque de código
    languagejava
       @Override    
       public SecurityFilterChain filterChain( HttpSecurity http ) throws Exception {         
            http.authorizeRequests()
                .mvcMatchers("/actuator/**").permitAll()
                .mvcMatchers("/api-docs").permitAll()
                .mvcMatchers(apiPath+"/public/**").permitAll()
                .mvcMatchers(apiPath+"/private/**").hasAnyAuthority(serverScopes)
    				.anyRequest().authenticated().and()
    				.addFilterAfter( loggingFilterBean(), BearerTokenAuthenticationFilter.class )
    				.addFilterAfter( loaFilterBean(), BearerTokenAuthenticationFilter.class )
                .oauth2ResourceServer().jwt();        
           return http.build();
        }


    Advertencia

    Repetimos: ESTA CONFIGURACIÓN GLOBAL VALIDARÁ EL NIVEL DEL CLAIM LOA EN TODOS LOS ENDPOINTS DEL API.



...

Configuración para establecer niveles LoA mínimos (o diferentes) en un/os endpoints concretos de nuestra API:

Clase Configuration (SecurityConfig)

  1. En la clase con la configuración de seguridad (ver pasos 1 y 2 de la configuración común), añadir la siguiente anotación en la declaración de la clase:

    Bloque de código
    languagejava
    @EnableGlobalMethodSecurity( prePostEnabled = true )


  2. Añadir el siguiente Bean:

    Bloque de código
    languagejava
    @Bean
    UmuJwtLoaAccessDeniedHandler accessDeniedHandler() {
       return new UmuJwtLoaAccessDeniedHandler();
    }


  3. En la misma clase, en el método configure, añadir el accessDeniedHandler configurado en el paso anterior de la siguiente forma:

    Bloque de código
    languagejava
    .and().exceptionHandling().accessDeniedHandler( accessDeniedHandler() )

    Ejemplo de configuración completa:

    Bloque de código
    languagejava
    public SecurityFilterChain filterChain( HttpSecurity http ) throws Exception {
            http
                .authorizeRequests()
                .mvcMatchers("/actuator/**").permitAll()
                .mvcMatchers("/api-docs").permitAll()
                .mvcMatchers(apiPath+"/public/**").permitAll()
                .mvcMatchers(apiPath+"/private/**").hasAnyAuthority(serverScopes)
    			.anyRequest().authenticated()
    			.and().exceptionHandling().accessDeniedHandler( accessDeniedHandler() )
    			.and().addFilterAfter( loggingFilterBean(), BearerTokenAuthenticationFilter.class )
                .oauth2ResourceServer().jwt();        
    	return http.build();     
    }


    RestController
    Ahora, en nuestro RestController, anotar el método con nuestro endpoint a securizar con la siguiente anotación:

    Bloque de código
    languagejava
    @PreAuthorize( "@umuJwtLoaAuthenticator.isSubstantialLoA(authentication)" )


    Advertenciainfo

    El método isSubstantialLoA del componente umuJwtLoaAuthenticator establece el nivel mínimo de LoA para acceder al endpoint a medio(Substantial del eIDAS).

    Para establecer el nivel alto (High), es necesario utilizar el método isHighLoA(authentication).


    Por ejemplo:

    Bloque de código
    languagejava
        @GetMapping( "/private/afiliacion" )
    	@Operation( summary = "Endpoint test /afiliacion",
    				description = "Obtiene el dto de afiliación del token llamando a serviciosgente internamente",
    				tags = {"Serviciosgente"},
    				security = {@SecurityRequirement( name = "OIDC", scopes = "openid" )},
    				responses = {
    						@ApiResponse( responseCode = "401", description = "Token inválido o LoA mínimo no alcanzado", content = @Content ),
    						@ApiResponse( responseCode = "500", description = "Error de comunicación con serviciosgente", content = @Content ),
    						@ApiResponse( responseCode = "200", description = "Datos de Afiliación del propietario del token obtenidos correctamente",	content = @Content( schema = @Schema( implementation = AfiliacionDTO.class ) ) )
    				} )
    	@PreAuthorize( "@umuJwtLoaAuthenticator.isSubstantialLoA(authentication)" )
    	public ResponseEntity<AfiliacionDTO> getAfiliacionGente( @AuthenticationPrincipal Jwt jwt ) {
    
    


    Advertencia

    Repetimos: ESTA CONFIGURACIÓN POR MÉTODO VALIDARÁ EL NIVEL DEL CLAIM LOA EN EL ENDPOINT CON LA ANOTACIÓN PREAUTHORIZE.


...

RESPUESTA DE ERROR

Cuando se intenta acceder a un endpoint securizado con un token sin el claim LoA mínimo (independientemente de la configuración elegida), se devolverá la siguiente información:

- Código HTTP: 401
- Cabecera en la respuesta: umu-authenticat: invalid_loa_claim 
- Cabecera en la respuesta: www-authenticate: Bearer error="invalid_loa_claim",error_description="Invalid LoA Claim. Excepted level 'SUBSTANTIAL' : Found level: 'LOW' ",error_uri="https://tools.ietf.org/html/rfc6750#section-3.1

...

Combinación de AutenticaciónLoA
LDAP (cuenta UM)LOW
LDAP (cuenta UM) + MFA (OTP)SUBSTANTIAL
CMNLOW
CMN + MFASUBSTANTIAL
Cl@ve certificadoSUBSTANTIAL
Cl@ve PIN (lleva implícito el OTP)SUBSTANTIAL
Cl@ve permanenteLOWSUBSTANTIAL
Cl@ve certificado + MFASUBSTANTIAL
Cl@ve PIN + MFASUBSTANTIAL
Cl@ve permanente + MFASUBSTANTIAL
*Cl@ve + DNIeHIGH

...