Aprovecha el precio especial

Antes:$249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Comienza ahora

Termina en:

03d

02h

55m

03s

30

Spring Security y JSON Web Token

Queriendo complementar un poco el curso de Java EE dictado por Anahi, quiero compartir como agregarle seguridad a nuestra api.

Hay un post de @SergioDxa que explica lo que es un JSON Web Token:
https://platzi.com/blog/introduccion-json-web-tokens/

Pero para resumirlo, cuando hagamos login a nuestra api, esta nos devolverá una cadena larga da caracteres codificada, la cual contiene el nombre del usuario, el tipo y algoritmo con el que fue cifrado, y una firma que se genera usando estas dos partes mas una clave secreta que definiremos en la api.

Hay diversas maneras de lograr esto, y estoy convencido al 100% de que existen mejores maneras y con mucho mejores prácticas que las que aplico aquí, pero para quien quiera entrar a entender un poco de esto, puede llegar a ser una base, la cual pueden mejorar todo lo que imaginen.

<ins>Primero que nada deberán crear 2 tablas. Una será para los usuarios, y otra para los roles de los usuarios.</ins>

  • La tabla de usuarios esta muy simple, solo id, un campo único llamado usuario, contraseña y un campo enabled que determinara si el usuario esta habilitado o no. Aquí eh de resaltar que la contraseña esta “simple” , es decir, no me tome el trabajo de encriptarla, pero si gustan pueden hacerlo.

  • La tabla roles, contiene un campo indice llamado username que es con el que se relaciona a la tabla usuarios, y un campo role, que es el que identifica el nombre del rol. (nuevamente, este campo rol debería salir de otra tercer tabla de roles, y estar relacionada a ella, para evitar que un usuario tenga el rol “administrador” y otro tenga el rol “administrator”, es decir, evitar el libre tipeo).

CREATETABLEIFNOTEXISTS`users` (
  `username`varchar(45) NOTNULL,
  `password`varchar(45) NOTNULL,
  `enabled` tinyint(4) NOTNULLDEFAULT'1',
  `id`int(11) NOTNULL AUTO_INCREMENT,
  PRIMARY KEY (`id`),
  UNIQUEKEY`username` (`username`),
  KEY`username_2` (`username`)
) ENGINE=InnoDBDEFAULTCHARSET=utf8 AUTO_INCREMENT=2;

--
-- Volcado de datos para la tabla `users`
--

INSERTINTO`users` (`username`, `password`, `enabled`, `id`) VALUES
('matias', '12345', 1, 1);

-- --------------------------------------------------------

--
-- Estructura de tabla para la tabla `user_roles`
--

CREATETABLEIFNOTEXISTS`user_roles` (
  `user_role_id`int(11) NOTNULL AUTO_INCREMENT,
  `username`varchar(45) NOTNULL,
  `role`varchar(45) NOTNULL,
  PRIMARY KEY (`user_role_id`),
  KEY`username` (`username`),
  KEY`username_2` (`username`),
  KEY`id_user` (`username`)
) ENGINE=InnoDBDEFAULTCHARSET=utf8 AUTO_INCREMENT=6;

--
-- Volcado de datos para la tabla `user_roles`
--

INSERTINTO`user_roles` (`user_role_id`, `username`, `role`) VALUES
(4, 'matias', 'ROLE_ADMIN'),
(5, 'matias', 'ROLE_USER');

<ins>Antes de seguir, deberemos importar unas dependencias extras:</ins>

<dependency><groupId>org.springframework.securitygroupId>
		    <artifactId>spring-security-coreartifactId>
		    <version>4.2.0.RELEASEversion>
		dependency>

		<dependency><groupId>org.springframework.securitygroupId>
		    <artifactId>spring-security-webartifactId>
		    <version>4.2.2.RELEASEversion>
		dependency>

		<dependency><groupId>org.springframework.securitygroupId>
		    <artifactId>spring-security-configartifactId>
		    <version>3.2.0.RELEASEversion>
		dependency>

		<dependency><groupId>io.jsonwebtokengroupId>
		    <artifactId>jjwtartifactId>
		    <version>0.7.0version>
		dependency>

        <dependency><groupId>org.springframeworkgroupId>
         <artifactId>spring-jdbcartifactId>
        dependency>

        <dependency><groupId>eu.bitwalkergroupId>
		    <artifactId>UserAgentUtilsartifactId>
		    <version>1.14version>
		dependency>

La última no hace falta para lograr el cometido, pero es de utilidad para conocer el origen del cual los usuarios están consumiendo la api. Mostraré un ejemplo de esto en una de las clases, estará como código comentado, pero si lo “descomentan” pueden probar lo que imprime en consola.

<ins>Ahora debemos crear un nuevo package, el cual lo denominaremos security</ins>

<ins>Sí, también contedra esas 4 clases dentro:</ins>

  • SecurityConfig
  • LoginFilter
  • JwtFilter
  • JwtUtil

Es todo lo que necesitaremos y no tendremos que tocar nada de lo que hicimos con Anahi.

Empecemos con SecurityConfig

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;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.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebSecurity
@EnableWebMvc
public classSecurityConfigextendsWebSecurityConfigurerAdapter{
    @Override
    protected void configure(HttpSecurity http) throwsException {
        http.csrf().disable().authorizeRequests()
            .antMatchers("/login").permitAll() //permitimos el acceso a /login a cualquiera
            .antMatchers("/v1/socialMedias").access("hasRole('ROLE_ADMIN')")// requiere el rol de administrador 
            .anyRequest().authenticated() //cualquier otra peticion requiere autenticacion
            .and()
            // Las peticiones /login pasaran previamente por este filtro
            .addFilterBefore(newLoginFilter("/login", authenticationManager()),
                    UsernamePasswordAuthenticationFilter.class)                
            // Las demás peticiones pasarán por este filtro para validar el token
            .addFilterBefore(newJwtFilter(),
                    UsernamePasswordAuthenticationFilter.class); 

    }

    @Autowired
	DataSource dataSource;

    @Autowired
	public void configAuthentication(AuthenticationManagerBuilder auth) throwsException {

	  auth.jdbcAuthentication().dataSource(dataSource)
		.usersByUsernameQuery(
			"select username,password, enabled from users where username=?")
		.authoritiesByUsernameQuery(
			"select username, role from user_roles where username=?");	  	 

	}	    

}

Esta es una clase que hereda de la clase WebSecurityConfigurerAdapter. Es la mas corta pero es la principal. El @EnableWebMvc podría no ir, quedo allí por unas pruebas que hice.
Los comentarios en el código explican casi todo.

  • En primer lugar sobre escribimos el método configure, aquí es donde se determinan los accesos a las distintas rutas

Esta línea implica que todo mundo podrá ingresar a la ruta /login la cual verán que no tiene un método GET, sino que habrá que hacer un POST a esa ruta con los datos de usuario y password.

.antMatchers("/login").permitAll()

Esta siguiente línea dice que, si el usuario no tiene asignado el rol de administrador, en este caso se llama “ROLE_ADMIN”, no podrá acceder a la ruta indicada. La prueba que pueden hacer es, crear un usuario que no tenga rol de “ROLE_ADMIN”, y ver que si pueden ver los datos de Teacher por ejemplo, pero que les lanza un acceso denegado al querer ver las social medias. Luego asignelen el rol de administrador y verán que ahora si, van a ver los datos de social medias…

.antMatchers("/v1/socialMedias").access("hasRole('ROLE_ADMIN')")

Luego, la siguiente linea indica que para el resto de las rutas solo basta con estar logueado, sin importar que roles tenga ese usuario.

.anyRequest().authenticated()

Y por ultimo las siguientes dos lineas son filtros, y nos dice que cuando hagamos post a /login, pasara por el primer filtro, y luego por el segundo. Pero si ya estamos logueados y hacemos por ejemplo un GET a /socialMedias salteara el primer filtro pero de todas formas pasara por el segundo, ya que en el segundo filtro se encargara de verificar que el token sea valido.

.and()
            // Las peticiones /login pasaran previamente por este filtro
            .addFilterBefore(new LoginFilter("/login", authenticationManager()),
                    UsernamePasswordAuthenticationFilter.class)                
            // Las demás peticiones pasarán por este filtro para validar el token
            .addFilterBefore(new JwtFilter(),
                    UsernamePasswordAuthenticationFilter.class); 

Como ven se hace uso de dos clases entro de dichos filtros, que son los que vamos a ver en un ratito…

Para lo que sigue en el resto de la clase, hay algo que si debemos agregar a lo creado con Anahi, y es que en la clase DataBaseConfiguration que esta dentro del package configuration abra que agregarle un nombre a la anotacion Bean, quedando de esta manera:

@Bean(name = "dataSource")
	public DataSource dataSource(){
		DriverManagerDataSource dataSource = new DriverManagerDataSource();
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		dataSource.setUrl("jdbc:mysql://localhost:3306/platziprofesores");
		dataSource.setUsername("platziprofesores");
		dataSource.setPassword("platziprofesores");return dataSource;
	}

Ahora si, el motivo era para el siguiente: debemos poder acceder a ese datasource, para que la clase AuthenticationManagerBuilder tenga acceso y pueda buscar y comparar el usuario y contraseña enviado por el usuario.
En estas lineas hacemos eso:

@Autowired
	DataSource dataSource;

    @Autowired
	publicvoidconfigAuthentication(AuthenticationManagerBuilder auth)throws Exception {

	  auth.jdbcAuthentication().dataSource(dataSource)
		.usersByUsernameQuery(
			"select username,password, enabled from users where username=?")
		.authoritiesByUsernameQuery(
			"select username, role from user_roles where username=?");	  	 

	}	

Y con esto termina la primer clase, pasamos a la siguiente: LoginFilter
Esta es clase implementada en el primer filtro, es decir, el filtro de login.

import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.InputStream;import java.util.Collections;

public class LoginFilter extends AbstractAuthenticationProcessingFilter {

    public LoginFilter(Stringurl, AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(url));
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(
            HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException, IOException, ServletException {

        // obtenemos el body de la peticion que asumimos viene en formato JSON
        InputStream body = req.getInputStream();

        // Asumimos que el body tendra el siguiente JSON  {"username":"xxxx", "password":"xxxx"}// Realizamos un mapeo a nuestra clase User para tener ahi los datos
        User user = new ObjectMapper().readValue(body, User.class);

        // Finalmente autenticamos// Spring comparar el user/password recibidos// contra el que definimos en la clase SecurityConfig// el Collections.emptyList() es el lugar para los roles o authorities, pero no es necesario en este primer filtroreturn getAuthenticationManager().authenticate(
                new UsernamePasswordAuthenticationToken(
                        user.getUsername(),
                        user.getPassword(),
                        Collections.emptyList()
                )
        );

    }

    @Override
    protected void successfulAuthentication(
            HttpServletRequest req,
            HttpServletResponse res, FilterChain chain,
            Authentication auth) throws IOException, ServletException {

    	 HttpServletRequest request = (HttpServletRequest) req;
         HttpServletResponse response = (HttpServletResponse) res;
         String clientOrigin = request.getHeader("origin");

         /*
         String audience = request.getHeader("User-Agent");         

         UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
         OperatingSystem agent = userAgent.getOperatingSystem();

         System.out.print("You're a ");
         System.out.print(agent.getName() + " about a: ");
         System.out.println(agent.getDeviceType().getName());

         System.out.println(audience);
         */

         response.addHeader("Access-Control-Allow-Origin", clientOrigin);
         response.setHeader("Access-Control-Allow-Methods", "POST, GET,  DELETE, PUT");
         response.setHeader("Access-Control-Allow-Credentials", "true");
         response.setHeader("Access-Control-Max-Age", "3600");
         response.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Origin, Authorization, X-Auth-Token");

        // Si la autenticacion fue exitosa, agregamos el token a la respuesta
        JwtUtil.addAuthentication(response, auth.getName());
    }
}

class User {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Bueno esta clase esta casi toda comentada, excepto dos partes.
Primero empiezo por esta:

  /*
         String audience = request.getHeader("User-Agent");         

         UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
         OperatingSystem agent = userAgent.getOperatingSystem();

         System.out.print("You're a ");
         System.out.print(agent.getName() + " about a: ");
         System.out.println(agent.getDeviceType().getName());

         System.out.println(audience);
         */

Esa es la parte que utiliza la dependencia que comente al principio, que es para conocer desde que dispositivo y con que software están accediendo a la api., si lo descomentan pueden probarlo.

En esta segunda parte, la utilidad es para poder acceder desde otros dominios a nuestra api, deberemos pasar estos parámetros por headers para que puedan establecer una comunicación segura con nuestra api.
En otras palabras, si creamos una web en el mismo proyecto, es decir, que corra en el mismo puerto, no hace falta. Pero si queremos acceder desde otra web, si, es mas, incluso si queremos acceder utilizando una web que esta corriendo para nuestro caso en “localhost”, pero con otro puerto, es también considerado otro dominio, y no van a poder acceder.
Este es el código al que hago referencia:

response.addHeader("Access-Control-Allow-Origin", clientOrigin);
         response.setHeader("Access-Control-Allow-Methods", "POST, GET,  DELETE, PUT");
         response.setHeader("Access-Control-Allow-Credentials", "true");
         response.setHeader("Access-Control-Max-Age", "3600");
         response.setHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Origin, Authorization, X-Auth-Token");

Ahora pasamos al segundo filtro, la clase llamada : JwtFilter

import java.io.IOException;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Collection;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.stereotype.Controller;import org.springframework.web.filter.GenericFilterBean;import com.platzi.profesoresplatzi.configuration.DataBaseConfiguration;import com.platzi.profesoresplatzi.model.Rol;import io.jsonwebtoken.Jwts;

@Controller
public class JwtFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain filterChain)
            throws IOException, ServletException {

    	// Obtenemos el token que viene en el encabezado de la peticionString token = ((HttpServletRequest) request).getHeader("Authorization");
        String[] list_roles = null;

        if (token != null) {
            String username = Jwts.parser()
                    .setSigningKey(JwtUtil.KEYSECRET) //la clave secreta esta declara en JwtUtil
                    .parseClaimsJws(token) //este metodo es el que valida
                    .getBody()
                    .getSubject();        //extraigo el nombre de usuario del token/*De manera "sucia" vamos a obtener los roles del usuario*/
	        DataBaseConfiguration db = new DataBaseConfiguration();       
	        JdbcTemplate jdbcTemplate = new JdbcTemplate(db.dataSource());

	        Collection roles = jdbcTemplate.query(
	                "select user_role_id,username,role from user_roles where username='" + username + "'", new RowMapper() {
						@Override
						public Object mapRow(ResultSet rs, int arg1) throws SQLException {
							// TODO Auto-generated method stub
							Rol rol = new Rol();
	                        rol.setId(rs.getLong("user_role_id"));
	                        rol.setUsername(rs.getString("username"));
	                        rol.setRol(rs.getString("role"));
	                        return rol;
						}
	                });

	        list_roles = newString[roles.size()];
	        int i = 0;
	        for (Objectrol : roles) {
	        	list_roles[i] = rol.toString();
	        	i++;
	        }
        }
        Authentication authentication = JwtUtil.getAuthentication((HttpServletRequest)request, list_roles);

        SecurityContextHolder.getContext().setAuthentication(authentication);

        filterChain.doFilter(request, response);

    }
}

En este filtro verificamos que el token que debe venir en la cabecera “Authorization”, no sea null y que ademas sea valido, extraemos el nombre de usuario de dicho token, y buscamos los roles que tiene asignado en la tabla de roles de usuarios. Como ven hago uso de una pequeña clase llamada Rol, que no es mas que un POJO, bueno, en realidad el código que voy a mostrar verán que es un BEANS, pero para el ejemplo con un POJO bastaría. La idea de que sea un BEANS es que veo necesario el hecho de crear un RolDao, y un RolService, con sus respectivas implementaciones, para poder usarlas en un controlador de la ruta v1/roles, porque?? porque pienso en que las personas encargadas del frontend necesitaran conocer los roles del usuario para saber si mostrar o no un botón de “eliminar”, por ejemplo. Más allá de que si no tiene autorización en la ruta de poder eliminar, no va a poder hacerlo, pero si el diseñador del frontend puede saber esto de ante manos podría evitar mostrar botones que no tienen sentido.
Bueno aquí la clase Rol:

import java.io.Serializable;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;

@Entity
@Table(name="user_roles")
publicclassRolimplementsSerializable{
	@Id
	@Column(name="user_role_id")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;

	@Column(name="username")
	private String username;

	@Column(name="role")
	private String rol;

	publicRol(String username, String rol){
		super();
		this.username = username;
		this.rol = rol;
	}

	publicRol(){
		super();
		// TODO Auto-generated constructor stub
	}

	public Long getId(){
		return id;
	}
	publicvoidsetId(Long id){
		this.id = id;
	}
	public String getUsername(){
		return username;
	}
	publicvoidsetUsername(String username){
		this.username = username;
	}
	public String getRol(){
		return rol;
	}
	publicvoidsetRol(String rol){
		this.rol = rol;
	}

	 public String toString(){
	        return rol;
	    }

}

Ese último método, retorna unicamente el rol, ya que es el método utilizado en la iteración de la lista Collection que utilice en la clase del filtro.

Habrán visto que hago uso de la clase JwtUtil, sobre todo porque allí eh de definir una key secreta para mi api

<ins>Vamos a la última clase: JwtUtil</ins>

import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.authority.AuthorityUtils;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.Date;

public class JwtUtil {

	public static String KEYSECRET = "laclavesecreta";

    // Metodo para crear el JWT y enviarlo al cliente en el header de la respuesta
    static void addAuthentication(HttpServletResponse res, String username) {

        String token = Jwts.builder()
            .setSubject(username)
            // Vamos a asignar un tiempo de expiracion de 12 horas
            .setExpiration(newDate(System.currentTimeMillis() + 43200000))
            // Hash con el que firmaremos la clave
            .signWith(SignatureAlgorithm.HS512, KEYSECRET)
            .compact();
        //agregamos al encabezado el token
        res.addHeader("Authorization", token);    
    }

    // Metodo para validar el token enviado por el cliente
    static Authentication getAuthentication(HttpServletRequest request, String[] roles) throws IOException {

        // Obtenemos el token que viene en el encabezado de la peticionString token = request.getHeader("Authorization");

        // si hay un token presente, entonces lo validamosif (token != null) {
            String user = Jwts.parser()
                    .setSigningKey(KEYSECRET)
                    .parseClaimsJws(token) //este metodo es el que valida
                    .getBody()
                    .getSubject();

            // Recordamos que para las demas peticiones que no sean /login// no requerimos una autenticacion por username/password// por este motivo podemos devolver un UsernamePasswordAuthenticationToken sin password, pero debemos consultar nuevamente los roles// seguramente hay una mejor forma de implementar roles, pero no esta del todo mal volver a consultarlos, puesto que si hubo un cambio// en la asignación de roles a un usuario, se le otorgara o se le denegara el acceso de forma automatica, y no hay que esperar al vencimiento del tokenreturn user != null ?
                    new UsernamePasswordAuthenticationToken(user, null, AuthorityUtils.createAuthorityList(roles)) :
                    null;

        }

        returnnull;
    }

}

Como ven esta todo comentado y no hay mucho que explicar. Solo que del primer método hace uso el primer filtro, es decir el del login, y el segundo método lo utiliza el segundo filtro, que es el que le asigna los roles.

Y eso es todo, les queda de tarea hacer la clase RolDao, RolService y sus implementaciones, y la clase controller para la ruta v1/roles. Aclaro que esto último no es necesario para poder aplicar lo expuesto.

De cierre les comparto esta pequeña clase, que pueden aplicarla tanto en una aplicación escritorio, como web o móvil.

import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;/**
 *
 * @author Matias
 */
public class JavaApplication5 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

	  try {

		URL url = new URL("http://localhost:8080/login");
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setDoOutput(true);
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Content-Type", "application/json");

		String input = "{\"username\":\"matias\",\"password\":\"12345\"}";

		OutputStream os = conn.getOutputStream();
		os.write(input.getBytes());
		os.flush();

                String token = conn.getHeaderField("Authorization");

                System.out.println(token.trim());

		conn.disconnect();

                url = new URL("http://localhost:8080/v1/socialMedias");
		conn = (HttpURLConnection) url.openConnection();
		conn.setRequestMethod("GET");
                conn.setRequestProperty("Authorization", token);
		conn.setRequestProperty("Accept", "application/json");

		if (conn.getResponseCode() != 200) {
			thrownew RuntimeException("Failed : HTTP error code : "
					+ conn.getResponseCode());
		}

		BufferedReader br = new BufferedReader(new InputStreamReader(
			(conn.getInputStream())));

		String output;
		System.out.println("Output from Server .... \n");
		while ((output = br.readLine()) != null) {
			System.out.println(output);
		}

		conn.disconnect();                

	  } catch (MalformedURLException e) {

		e.printStackTrace();

	  } catch (IOException e) {

		e.printStackTrace();

	 }

    }

}

Por cabecera les paso mi usuario y contraseña a la api, como ven mi usuario por defecto es “matias” y mi contraseña es “12345”, cambienlos por los datos del usuario que ustedes hayan creado. Lo mismo con el puerto que les corra el tomcat, a mi por defecto me corre en el 8080, pero si no es su caso modifiquenlo por el que sea necesario.

Este es mi primer pPOST, espero poder haberme dado a entender.

(Artículo original de Ascari Romo)

Escribe tu comentario
+ 2
Ordenar por:
2
5109Puntos
6 años

Está muy bien compartir el conocimiento De forma libre, solo te falto al menos hacer una referencia al blog de donde tomaste el código fuente, hasta los comentarios del código le dejaste igual.

El código fuente de este artículo está basado en este https://windoctor7.github.io/spring-jwt.html

1
17707Puntos
6 años

Ja… es chistoso pero este post tiene mas de 2 años y tu post que tomaste de aqui es de poco más de 1 año… algo no coincide?

1
31518Puntos
3 años

Intente implementarlo en mi proyecto, pero me sale este error.

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
	at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:254) ~[spring-security-crypto-5.6.0.jar:5.6.0]
	at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:202) ~[spring-security-crypto-5.6.0.jar:5.6.0]
	at org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$LazyPasswordEncoder.matches(AuthenticationConfiguration.java:313) ~[spring-security-config-5.6.0.jar:5.6.0]
	at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:76) ~[spring-security-core-5.6.0.jar:5.6.0]
	at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:147) ~[spring-security-core-5.6.0.jar:5.6.0]
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-5.6.0.jar:5.6.0]
	at com.platzi.profesoresplatzi.security.LoginFilter.attemptAuthentication(LoginFilter.java:43) ~[classes/:na]
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:223) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.13.jar:5.3.13]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.13.jar:5.3.13]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) ~[spring-security-web-5.6.0.jar:5.6.0]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.3.13.jar:5.3.13]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.3.13.jar:5.3.13]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.13.jar:5.3.13]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.13.jar:5.3.13]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.13.jar:5.3.13]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.13.jar:5.3.13]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.13.jar:5.3.13]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.13.jar:5.3.13]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.55.jar:9.0.55]
	at java.base/java.lang.Thread.run(Thread.java:831) ~[na:na]

Alguna idea de que pueda ser?

1
7305Puntos
7 años

Me surgieron estas dudas, si me pudieras ayudar 😃

Es un login como tal para que cualquier usuario que se autentifique?
Lo puedo usar en frontend como un modulo de autentificación?

2
17707Puntos
6 años

Hola, wow, tiene tiempo este post ja… Y al parecer no te llegan notificaciones cuando alguien comenta.

Esto es para implementar seguridad en el backend con spring security implementando para ellos jwt. Cuando implementes el frontend al iniciar sesion, en el frontend recibes un json web token, y luego a cada solicitud que hagas a un recurso de la api, deberas adjuntar en el header de la solicitud el parámetro Authorization donde el valor es justamente ese token. De no hacerlo la api va a rechazar la solicitud.

Saludos!

1
8297Puntos
5 años

tiene mucho tiempo esta publicación pero a ver si me pueden resolver la duda… porque se le asigna tanto tiempo de vida al token?, no debería morir antes?, o muere al mismo tiempo que la sesión?

1
5 años

Alguien puede pasarme la configuracion de CORS para JwtFilter, tengo problemas para conectar con front end

0

tu codigo esta medio chafa, y no explica el porque, solo explicaciones a lo wey , se puede mejorar demasiado chava