Spring Boot intègre JWT pour réaliser une authentification frontale et dorsale

Prenez l'habitude d'écrire ensemble ! C'est le 10ème jour de ma participation au "Nuggets Daily New Plan·April Update Challenge", cliquez pour voir les détails de l'événement

avant-propos

Le développement rapide de petits programmes et d'applications H5 a fait de la séparation front-end et back-end une tendance. Cependant, l'authentification système est une partie importante du système. Cet article explique comment JWT réalise l'authentification front-end et back-end. .

Présentation de JWT

JWT (nom complet : Json Web Token) est une norme ouverte (RFC 7519) qui définit un moyen compact et autonome de transmettre en toute sécurité des informations entre les parties sous forme d'objets JSON.

Pourquoi utiliser JWT

2.1 Quels sont les inconvénients de l'authentification de session traditionnelle ?

  • Les informations de connexion de chaque utilisateur seront stockées dans la session du serveur. À mesure que le nombre d'utilisateurs augmente, la surcharge du serveur augmente considérablement.

  • Les informations de session sont stockées dans la mémoire du serveur, ce qui entraînera une défaillance des applications distribuées. Bien que les informations de session puissent être stockées dans le cache Redis de manière uniforme, cela peut augmenter la complexité.

  • Étant donné que l'authentification de session est mise en œuvre sur la base de cookies, elle n'est pas applicable aux terminaux mobiles sans navigateur et mobiles.

  • Le système de séparation front-end et back-end, car le front-end et le back-end sont inter-domaines et les informations de cookie ne peuvent pas être croisées, de sorte que l'utilisation de l'authentification de session est également incapable de continuer l'authentification inter-domaine.

2.2 Avantages de l'authentification JWT

  • Concis : JWT Token a une petite quantité de données et une vitesse de transmission rapide.

  • Inter-langue : le jeton JWT est stocké côté client sous forme chiffrée JSON, de sorte que JWT est inter-langue et pris en charge par n'importe quel formulaire Web. Multi-plateforme : il ne dépend pas des cookies et des sessions, et n'a pas besoin de stocker les informations de session côté serveur. Il est très approprié pour les applications distribuées et pour l'expansion.

Structure de données de JWT

image.png

Entête

La première partie du JWT est la partie d'en-tête, qui est un objet Json qui décrit les métadonnées JWT, généralement comme suit.

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

L'attribut alg indique l'algorithme utilisé pour la signature, la valeur par défaut est HMAC SHA256 (écrit comme HS256), l'attribut typ indique le type du jeton et le jeton JWT est uniformément écrit comme JWT.

Charge utile

La deuxième partie de JWT est Payload, qui est également un objet Json. En plus des données qui doivent être transmises, il existe sept champs par défaut à sélectionner. iss : émetteur exp : heure d'expiration sub : sujet aud : utilisateur nbf : indisponible jusque-là iat : heure d'émission jti : ID de JWT utilisé pour identifier ce JWT

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

Il convient de noter que JWT n'est pas chiffré par défaut et que n'importe qui peut interpréter son contenu, donc si certaines informations sensibles ne sont pas stockées ici, cela empêchera les fuites d'informations. Les objets JSON sont également enregistrés sous forme de chaînes à l'aide de l'algorithme d'URL Base64.

Signature

La partie de hachage de signature consiste à signer les deux parties de données ci-dessus. Il est nécessaire d'utiliser l'en-tête codé en base64 et les données de charge utile pour générer le hachage via l'algorithme spécifié afin de garantir que les données ne seront pas falsifiées.

Spring Boot intègre JWT

Importer le package Jwt

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

Écrire la classe d'outils 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";
  }
      
复制代码

Intercepteur d'authentification de jeton

 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;
    }

}
复制代码

Configurer l'intercepteur

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

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

Processus de vérification de connexion

image.png

exemple de code

@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;
}
复制代码

Résumer

Les temps avancent et la technologie est constamment mise à jour. Dans le passé, Session+Redis était utilisé pour implémenter l'authentification unique, mais maintenant il peut être remplacé par l'implémentation jwt+Redis. Ce n'est qu'en mettant constamment à jour notre pile technologique que nous pouvons éviter d'être En ce qui concerne l'utilisation des méthodes d'annotation, la mise en œuvre et le rafraîchissement des jetons seront expliqués dans les articles suivants.

Je suppose que tu aimes

Origine juejin.im/post/7085364569916309535
conseillé
Classement