Spring Boot integra JWT para lograr la autenticación de front-end y back-end

¡Acostúmbrate a escribir juntos! Este es el décimo día de mi participación en el "Nuggets Daily New Plan·Desafío de actualización de abril", haz clic para ver los detalles del evento

prefacio

El rápido desarrollo de pequeños programas y aplicaciones H5 ha hecho que la separación de front-end y back-end sea una tendencia. Sin embargo, la autenticación del sistema es una parte importante del sistema. Este artículo explicará cómo JWT realiza la autenticación de front-end y back-end. .

Introducción a JWT

JWT (nombre completo: Json Web Token) es un estándar abierto (RFC 7519) que define una forma compacta e independiente de transmitir información de forma segura entre las partes como objetos JSON.

Por qué usar JWT

2.1 ¿Cuáles son las desventajas de la autenticación de sesión tradicional?

  • La información de inicio de sesión de cada usuario se almacenará en la sesión del servidor. A medida que aumenta el número de usuarios, la sobrecarga del servidor aumentará significativamente.

  • La información de la sesión se almacena en la memoria del servidor, lo que provocará fallas en las aplicaciones distribuidas.Aunque la información de la sesión se puede almacenar en el caché de Redis de manera uniforme, esto puede aumentar la complejidad.

  • Dado que la autenticación de sesión se implementa en base a cookies, no es aplicable a terminales móviles sin navegador y móviles.

  • El sistema de separación de front-end y back-end, porque el front-end y el back-end son dominios cruzados, y la información de las cookies no se puede cruzar, por lo que el uso de la autenticación de sesión tampoco puede continuar con la autenticación entre dominios.

2.2 Ventajas de la autenticación JWT

  • Conciso: el token JWT tiene una pequeña cantidad de datos y una velocidad de transmisión rápida.

  • Idioma cruzado: el token JWT se almacena en el lado del cliente en forma cifrada JSON, por lo que JWT es multilingüe y compatible con cualquier formulario web. Multiplataforma: No depende de cookies y sesiones, y no necesita almacenar información de sesión en el lado del servidor.Es muy adecuado para aplicaciones distribuidas y para expansión.

Estructura de datos de JWT

foto.png

Encabezamiento

La primera parte del JWT es la parte del encabezado, que es un objeto Json que describe los metadatos del JWT, generalmente de la siguiente manera.

{
    "alg": "HS256",
    "typ": "JWT"
}
复制代码

El atributo alg indica el algoritmo utilizado para la firma, el valor predeterminado es HMAC SHA256 (escrito como HS256), el atributo typ indica el tipo de token y el token JWT se escribe uniformemente como JWT.

Carga útil

La segunda parte de JWT es Payload, que también es un objeto Json Además de los datos que deben pasarse, hay siete campos predeterminados para la selección. iss: emisor exp: tiempo de caducidad sub: asunto aud: usuario nbf: no disponible hasta entonces iat: tiempo de emisión jti: ID de JWT utilizado para identificar este JWT

{
    //默认字段
    "sub":"主题123",
    //自定义字段
    "name":"java",
    "isAdmin":"true",
    "loginTime":"2021-12-05 12:00:03"
}
复制代码

Cabe señalar que JWT no está cifrado de forma predeterminada y cualquiera puede interpretar su contenido, por lo que si no se almacena aquí alguna información confidencial, se evitará la fuga de información. Los objetos JSON también se guardan como cadenas mediante el algoritmo de URL Base64.

Firma

La parte del hash de la firma es para firmar las dos partes de datos anteriores. Es necesario utilizar el encabezado codificado en base 64 y los datos de la carga útil para generar el hash a través del algoritmo especificado para garantizar que los datos no se alteren.

Spring Boot integra JWT

Importar el paquete Jwt

<dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
  </dependency>
复制代码

Escribir clase de herramienta jwt


public class JwtUtil
{
//创建jwt
public static String createJWT(String subject, String issue, Object claim,
            long ttlMillis)
    {
       //当前时间
        long nowMillis = System.currentTimeMillis();
        //过期时间
        long expireMillis = nowMillis + ttlMillis;
        String result = Jwts.builder()
                .setSubject(subject) //设置主题
                .setIssuer(issue) //发行者
                .setId(issue)//jwtID
                .setExpiration(new Date(expireMillis)) //设置过期日期
                .claim("user", claim)//主题,可以包含用户信息
                .signWith(getSignatureAlgorithm(), getSignedKey())//加密算法
                .compressWith(CompressionCodecs.DEFLATE).compact();//对载荷进行压缩

        return result;
    }
    
    // 解析jwt
    public static Jws<Claims> pareseJWT(String jwt)
    {
        Jws<Claims> claims;
        try
        {
            claims = Jwts.parser().setSigningKey(getSignedKey())
                    .parseClaimsJws(jwt);
        }
        catch (Exception ex)
        {
            claims = null;
        }
        return claims;
    }

   //获取主题信息
    public static Claims getClaims(String jwt)
    {
        Claims claims;
        try
        {
            claims = Jwts.parser().setSigningKey(getSignedKey())
                    .parseClaimsJws(jwt).getBody();
        }
        catch (Exception ex)
        {
            claims = null;
        }
        return claims;
    }
  }
  
   /**
     * 获取密钥
     * 
     * @return Key
     */
    private static Key getSignedKey()
    {
        byte[] apiKeySecretBytes = DatatypeConverter
                .parseBase64Binary(getAuthKey());
        Key signingKey = new SecretKeySpec(apiKeySecretBytes,
                getSignatureAlgorithm().getJcaName());
        return signingKey;
    }
    
    private static SignatureAlgorithm getSignatureAlgorithm()
    {
        return SignatureAlgorithm.HS256;
    }
  
  //获取密钥,可以动态配置
  public static String getAuthKey()
  {
        String auth = "123@#1234";
  }
      
复制代码

Interceptor de autenticación de tokens

 Component
public class TokenInterceptor extends HandlerInterceptorAdapter
{
    public static Log logger = LogManager.getLogger(TokenInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception
    {
        String uri = request.getRequestURI();
        logger.info("start TokenInterceptor preHandle.." + uri);
         
		//需要过滤特殊请求
        if (SystemUtil.isFree(uri) || SystemUtil.isProtected(uri))
        {
            return true;
        }
        
		
        String metohd=request.getMethod().toString();
        logger.info("TokenInterceptor request method:"+metohd);
        //options 方法需要过滤
        if("OPTIONS".equals(metohd))
        {
            return true;   
        }
        
		//是否开启token认证
        boolean flag = SystemUtil.getVerifyToken();
        ResponseResult result = new ResponseResult();
		//从请求的head信息中获取token
        String token = request.getHeader("X-Token");

        if (flag)
        {
            if(StringUtils.isEmpty(token))
            {
                token=request.getParameter("X-Token");    
            }
            // token不存在
            if (StringUtils.isEmpty(token))
            {
                result.setCode(ResultCode.NEED_AUTH.getCode());
                result.setMsg(ResultCode.NEED_AUTH.getMsg());
                WebUtil.writeJson(result, response);
                return false;
            }
            else
            {
                Claims claims = JwtUtil.getClaims(token);
                String subject = "";
                if (claims != null)
                {
                    subject = claims.getSubject();
                    // 验证主题
                    if (StringUtils.isEmpty(subject))
                    {
                        result.setCode(ResultCode.INVALID_TOKEN.getCode());
                        result.setMsg(ResultCode.INVALID_TOKEN.getMsg());
                        WebUtil.writeJson(result, response);
                        return false;
                    }								
                }
                else
                {
                    result.setCode(ResultCode.INVALID_TOKEN.getCode());
                    result.setMsg(ResultCode.INVALID_TOKEN.getMsg());
                    WebUtil.writeJson(result, response);
                    return false;
                }
            }
        }
        return true;
    }

}
复制代码

Configurar el interceptor

@Configuration
public class WebConfig implements WebMvcConfigurer
{
    @Resource
    private TokenInterceptor tokenInterceptor;

    public void addInterceptors(InterceptorRegistry registry)
    {
        registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
    }
 }
复制代码

Proceso de verificación de inicio de sesión

foto.png

Código de muestra

@RequestMapping("login")
public Result login(HttpServletResponse response)
{
    Map<String, Object> map = new HashMap<>();
    //
    Result result = loginAuth(user);
    int code = result.getCode();
            //登录认证成功
    if (code ==ResultCode.SUCCESS)
    {
        //默认为7天
        Long ttlMillis = 7*1000 * 60 * 60 * 24;
        //过期时间
        long expreTime = System.currentTimeMillis() + ttlMillis;
        String tokenKey = UUID.randomUUID().toString();
        String tokenId = JwtUtil.createJWT(user.getUserId(), tokenKey,
                user.getPassword(), expreTime);                   
        map.put("expreTime", expreTime);				
        map.put("tokenId", tokenId);           
    }
    else
    {
        logger.error("login error:" +FastJsonUtil.toJSONString(result));
    }
    return result;
}
复制代码

Resumir

Los tiempos avanzan y la tecnología se actualiza constantemente. En el pasado, Session+Redis se usaba para implementar el inicio de sesión único, pero ahora puede ser reemplazado por la implementación de jwt+Redis. Solo actualizando constantemente nuestra pila de tecnología podemos evitar ser eliminado sin piedad En cuanto al uso de métodos de anotación, la implementación y la actualización del token se explicarán en artículos posteriores.

Supongo que te gusta

Origin juejin.im/post/7085364569916309535
Recomendado
Clasificación