Introducción y uso de JWT

1. JWT implementa servicios web sin estado

1. ¿Qué tiene estado?

Servicios con estado, es decir, el servidor necesita registrar la información del cliente de cada sesión para identificar la identidad del cliente y procesar las solicitudes en función de la identidad del usuario. Un diseño típico es el de sesión en Tomcat.

Por ejemplo, iniciar sesión: después de que el usuario inicia sesión, guardamos la información de inicio de sesión en la sesión del servidor y le damos al usuario un valor de cookie para registrar la sesión correspondiente. Luego, en la siguiente solicitud, si el usuario aporta el valor de la cookie, podemos identificar la sesión correspondiente y encontrar la información del usuario.

¿Cuales son las desventajas?

  • El servidor guarda una gran cantidad de datos, lo que aumenta la presión sobre el servidor.
  • El servidor guarda el estado del usuario y no puede expandirse horizontalmente.
  • Las solicitudes de los clientes dependen del servidor y varias solicitudes deben acceder al mismo servidor.

2. ¿Qué es apátrida?

El servidor no necesita registrar la información de estado del cliente, es decir:

  • El servidor no guarda ninguna información del solicitante del cliente.
  • Cada solicitud del cliente debe tener información autodescriptiva a través de la cual se pueda identificar la identidad del cliente.

¿Cuales son los beneficios?

  • Las solicitudes de los clientes no dependen de la información del servidor y no es necesario que varias solicitudes accedan al mismo servicio.
  • El clúster y el estado del servidor son transparentes para el cliente.
  • El servidor se puede migrar y escalar arbitrariamente.
  • Reducir la presión de almacenamiento del servidor

3. Cómo lograr la apatridia

Proceso de inicio de sesión sin estado:

  • Cuando el cliente solicita el servicio por primera vez, el servidor autentica la información del usuario (login)
  • Una vez pasada la autenticación, la información del usuario se cifra para formar un token y se devuelve al cliente como credencial de inicio de sesión.
  • Para cada solicitud posterior, el cliente llevará el token de autenticación.
  • El servicio descifra el token y determina si es válido.

diagrama de flujo:

​El cliente solicita iniciar sesión y las credenciales se emiten después de iniciar sesión.

Por favor agregue la descripción de la imagen.

¿Cuál es el punto más crítico en todo el proceso de inicio de sesión?

Seguridad de tokens

El token es el único identificador para identificar al cliente, si el cifrado no es lo suficientemente estricto y se falsifica, se acabará.

¿Qué método de cifrado es seguro y confiable?

Usaremos: Cifrado asimétrico JWT + RSA

4. Introducción a JWT

El nombre completo de JWT es Json Web Token, que es una especificación de autenticación de identidad y autorización liviana de estilo JSON que puede realizar la autorización de aplicaciones web distribuidas y sin estado; sitio web oficial: https://jwt.io

JWT contiene tres partes de datos:

  • Encabezado: Encabezado, normalmente el encabezado tiene dos partes de información:

    • Tipo de declaración, aquí está la información de autodescripción de JWT

    Codificaremos en base64 el encabezado para codificar y decodificar la primera parte de los datos en base64.

  • Carga útil: la carga útil son datos válidos y generalmente contienen la siguiente información:

    • Información de identidad del usuario (tenga en cuenta que debido a que aquí se utiliza la codificación base64, la decodificación es reversible, por lo tanto, no almacene información confidencial)
    • Declaración de registro: como la hora de emisión del token, la hora de vencimiento, el emisor, etc. Esta parte del contenido es como la información de la tarjeta de identificación.

    Esta parte también se codificará usando base64 para obtener la segunda parte de los datos.

  • Firma: La firma es la información de autenticación de todos los datos. Generalmente, a partir de los datos de los dos primeros pasos, más el secreto (no lo filtres, lo mejor es cambiarlo periódicamente), se genera una firma mediante un algoritmo de cifrado (irreversible). Se utiliza para verificar la integridad y confiabilidad de todos los datos.

Formato de datos generados:

Por favor agregue la descripción de la imagen.

Puedes ver que está dividido en 3 segmentos, cada segmento es parte de los datos anteriores.

5. Proceso de interacción JWT

Traducción de pasos:

  • 1. Inicio de sesión de usuario
  • 2. Autenticación del servicio, jwt se genera después de pasar
  • 3. Devuelva el jwt generado al navegador.
  • 4. Los usuarios llevan jwt cada vez que lo solicitan.
  • 5. El servidor utiliza la clave pública para interpretar la firma JWT y, después de juzgar que la firma es válida, obtiene la información del usuario de la carga útil.
  • 6. Procese la solicitud y devuelva el resultado de la respuesta.

2. biblioteca nimbus-jose-jwt

1. Ingrese las dependencias

nimbus-jose-jwt, jose4j y java-jwt son varias bibliotecas comunes para operar JWT en Java.

Sitio web oficial de nimbus-jose-jwt: https://connect2id.com/products/nimbus-jose-jwt

Coordenadas requeridas

    <dependency>
        <groupId>com.nimbusds</groupId>
        <artifactId>nimbus-jose-jwt</artifactId>
        <version>9.11.1</version>
    </dependency>

2. API principal

2.1 Proceso de cifrado
  • En nimbus-jose-jwt, la clase Header se usa para representar el encabezado del JWT, sin embargo, la clase Header es una clase abstracta y usamos su subclase JWSHeader .

    Crea el objeto de encabezado:

     @Test
     public void createToken(){
          
          
         //创建头部对象
         JWSHeader jwsHeader =
                 new JWSHeader.Builder(JWSAlgorithm.HS256) // 加密算法
                         .type(JOSEObjectType.JWT) // 静态常量
                         .build();
         System.out.println(jwsHeader);
     }
    

    Puede .toBase64URL()obtener el formato Base64 de la información del encabezado (que también es la información del encabezado real en el JWT) mediante el método:

  • Utilice la clase Payload para representar la parte de carga útil del JWT

    Crear objeto de sección de carga:

        @Test
        public void createToken(){
          
          
            //创建头部对象
            JWSHeader jwsHeader =
                    new JWSHeader.Builder(JWSAlgorithm.HS256)       // 加密算法
                            .type(JOSEObjectType.JWT) // 静态常量
                            .build();
            System.out.println(jwsHeader);
    
            //创建载荷
            Payload payload = new Payload("hello world");
            System.out.println(payload);
        }
    

    Puede .toBase64URL()obtener la forma Base64 de la información de carga útil a través del método (esta también es la información de carga útil real en el JWT):

  • Parte de la firma

    No existe una representación de clase especial para la parte de la firma. La parte de la firma no la crea usted mismo, sino que la 头部 + 荷载部 + 加密算法calcula

    ​ nimbus-jose-jwt proporciona especialmente un firmante JWSSigner para participar en el proceso de firma. La clave se especifica al crear el firmante:

JWSSigner jwsSigner = new MACSigner("Key"); //Se debe especificar una clave en MACSigner()


最终,整个 JWT 由一个 **JWSObject** 对象表示:

```java
JWSObject jwsObject = new JWSObject(jwsHeader, payload);
// 进行签名(根据前两部分生成第三部分)
jwsObject.sign(jwsSigner);

Lo que finalmente queremos es una cadena JWT, no un objeto. Aquí podemos .serialize()llamar al método en el objeto JWSObject que representa el JWT:

String token = jwsObject.serialize();

Ejemplo completo:

  @Test
    public void createToken() throws JOSEException {
    
    

        //创建头部对象
        JWSHeader jwsHeader =
                new JWSHeader.Builder(JWSAlgorithm.HS256)       // 加密算法
                        .type(JOSEObjectType.JWT) // 静态常量
                        .build();
        //创建载荷
        Payload payload = new Payload("hello world");

        //创建签名器
        JWSSigner jwsSigner = new MACSigner("woniu");//woniu为密钥
        //创建签名
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);// 头部+载荷
        jwsObject.sign(jwsSigner);//再+签名部分

        //生成token字符串
        String token = jwsObject.serialize();
        System.out.println(token);
    }

Si ocurre una excepción: com.nimbusds.jose.KeyLengthException: La longitud del secreto debe ser de al menos 256 bits, es porque la longitud de la clave no es lo suficientemente larga, solo aumente la longitud de la clave.

2.2 Descifrado

Solo hay dos API principales para el proceso de verificación y descifrado inverso: el método de análisis del método estático de JWSObject y la verificación de su objeto JWSVerifier.

Si desea verificar directamente la validez de un objeto JWSObject, debe crear un objeto JWSVerifier .

//创建验证器
JWSVerifier jwsVerifier = new MACVerifier("密钥");//密钥要和加密时的相同

Luego llame directamente al método de verificación del objeto jwsObject:

if (!jwsObject.verify(jwsVerifier)) {
    
    
    throw new RuntimeException("token 签名不合法!");
}

3. Renovación de tokens

En el desarrollo real, el token no siempre puede ser válido. Por ejemplo, si no se realiza ninguna operación dentro de 30 minutos, la autenticación caducará y deberá iniciar sesión nuevamente. Si ha estado solicitando acceso, el token será válido hasta el último visita faltan más de 30 segundos para la siguiente visita, si el tiempo excede los 30 minutos la certificación caduca.

springsecurity integra JWT:


@Component
public class JWTfilter extends OncePerRequestFilter {
    
    

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired(required = false)
    private SecurityLoginService securityLoginService;

    @SneakyThrows
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain)
            throws ServletException, IOException {
    
    

        //功能点1:在请求头拿到jwt
        String jwt = httpServletRequest.getHeader("jwt");
        if (jwt == null) {
    
    
            //放给security 其他过滤器,该方法不做处理
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        // 功能点2:jwt不合法
        if (!JWTUtil.decode(jwt)) {
    
    
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        //功能点3 获取jwt的用户信息
        Map payLoad = JWTUtil.getPayload(jwt);
        String username = (String) payLoad.get("username");

        //拿到redis的jwt
        String redisJWT = redisTemplate.opsForValue().get("jwt:" + username);

        //判断redis是否有该jwt
        if (redisJWT == null) {
    
    
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        if (!jwt.equals(redisJWT)) {
    
    
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        //给redis 的jwt续期
        redisTemplate.opsForValue().set("jwt:" + username, jwt, 30,
                TimeUnit.MINUTES);

        //获取用户名,密码,权限
        UserDetails userDetails = securityLoginService.loadUserByUsername(username);

        // 获取用户信息 生成security容器凭证
        UsernamePasswordAuthenticationToken upa =
                new UsernamePasswordAuthenticationToken(userDetails.getUsername()
                        , userDetails.getPassword(), userDetails.getAuthorities());

        //放入凭证
        SecurityContextHolder.getContext().setAuthentication(upa);

        // 本方法共功能执行完了,交给下一个过滤器
        filterChain.doFilter(httpServletRequest, httpServletResponse);

    }
}
   //前后端项目中要禁用掉session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
在securityConfig 类注入
http.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class);

Supongo que te gusta

Origin blog.csdn.net/lanlan112233/article/details/129762966
Recomendado
Clasificación