WebSecurityConfig.java

/**
 * TFG 75.678 - TFG Desarrollo web 2020 e-Learning for Schools
 * Copyright (C) 2020  Eduardo Rodriguez Carro
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.uoc.tfg.sel.config;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
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.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.bcrypt.BCryptPasswordEncoder.BCryptVersion;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.uoc.tfg.sel.security.JwtAuthenticationEntryPoint;
import org.uoc.tfg.sel.security.JwtRequestFilter;
import org.uoc.tfg.sel.security.JwtUserDetailsService;

/**
 * Configuracion de seguridad
 * 
 * Se usa NoOpPasswordEncoder como metodo alternativo bajo el
 * DelegatingPasswordEncoder para poder establecer las claves iniciales .
 *
 * @author Eduardo Rodriguez Carro
 */
@Profile("!test")
@SuppressWarnings("deprecation")
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	/** The cors origin. */
	@Value("${adpcus.cors.origin:*}")
	private List<String> corsOrigin;
	
	/** The cors allowed methods. */
	@Value("${adpcus.cors.allowed.methods: GET,POST,DELETE,PUT}")
	private List<String> corsAllowedMethods;
	
	/** The cors allowed headers. */
	@Value("${adpcus.cors.allowed.headers:Authorization}")
	private List<String> corsAllowedHeaders;
	
	/** The jwt authentication entry point. */
	@Autowired
	private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

	/** The user details service. */
	@Autowired
	private JwtUserDetailsService userDetailsService;

	/** The jwt request filter. */
	@Autowired
	private JwtRequestFilter jwtRequestFilter;

	/**
	 * Configuracion del autenticador.
	 *
	 * @param auth the auth
	 * @throws Exception the exception
	 */
	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		// anadimos nuestro servicio para obtener los datos del usuario asi como el servicio de cifrado de la password
		auth.eraseCredentials(true);
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}
	
	/**
	 * Objeto para el cifrado de las passwords.
	 *
	 * @return the password encoder
	 */
	@Bean
	public PasswordEncoder passwordEncoder() {
		Map<String,PasswordEncoder> allowedEncoders = new HashMap<>();
		allowedEncoders.put("np", NoOpPasswordEncoder.getInstance());
		allowedEncoders.put("bc", new BCryptPasswordEncoder(BCryptVersion.$2Y,10));
		return new DelegatingPasswordEncoder("bc",allowedEncoders);
	}

	/**
	 * Autenticador.
	 *
	 * @return the authentication manager
	 * @throws Exception the exception
	 */
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}
	   
   	/**
	 * Cors configurer.
	 *
	 * @return the web mvc configurer
	 */
   	@Bean
	    public WebMvcConfigurer corsConfigurer() {
	        return new WebMvcConfigurerAdapter() {
	            @Override
	            public void addCorsMappings(CorsRegistry registry) {
	                registry.addMapping("/**")
	                	.allowedOrigins("*")
	                	.allowedMethods("PUT", "DELETE","GET","POST");
	            }
	        };
	    }
	   
	/**
	 * Configurador de la seguridad HTTP.
	 *
	 * @param httpSecurity the http security
	 * @throws Exception the exception
	 */
	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {

		// No es necesario CSRF
		httpSecurity.cors().and().csrf().disable()
		
				// el API De login no requiere estar autenticado
				.authorizeRequests().antMatchers("/auth/login").permitAll().
				
				// El resto de peticiones requieren estar autenticado y se validaran con la seguridad por anotaciones
				anyRequest().authenticated().and().
				
				// Establecemos el punto de error cuando no se ha autenticado a una pagina de error
				// No implementamos en este lado un formularo de login
				exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
				
				// Establecemos el servidor como stateless, para no guardar datos de sesion en memoria ni cookies
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS);

		// Incroporamos el fitro de peticiones que se encargara de validar el usuario con el token JWT
		httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
	}
}