jwt实现登录验证

一、什么是jwt

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。
简要说明:前段传验证信息到后端,后端验证通过,返回一个对象,只不过这个对象是加密的,每次请求的时候,请求头带上token,里面封装了对象的信息,我们只需要用拦截器进行拦截,解析token,后端就可以知道是谁登录。

二、为什么需要jwt

cookie和session的缺点:

  • session都存储在服务端内存
  • 集群环境中需要额外处理。ip_hash,分布式,session......
    user -> u1,u2,u3
  • csrf:Cross-site request forgery, cookie被截获后可能发生跨站点请求伪造
  • cookie的跨域(前后端分离)读写不方便
jwt的优点:解决cookie和session的缺点

三、jwt的组成

一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名

1.头部header

{
	"alg": "HS256",
	"typ": "JWT"
}

alg: 签名算法,常用HS256,具体支持可参考jwt官网: https://jwt.io/
typ: token令牌的类型,统一写成JWT

2.payload 负载

也是一个 json 的对象,下面是官方字段:

iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号  

当然也可以定义一些其他字段:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

由于jwt默认不是加密的,所以不要保存敏感信息

3.Signature 签名

Signature 部分是对前两个部分的签名,防止数据篡改。
首先需要制定一个密匙(secret),这个密匙只有服务器才知道,不能泄露给用户。
然后使用Header里面指定的签名算法,按照下面的公式产生签名。

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret 
)

算出签名后,把header,payload,signature之间用 ( . )隔开,就可以返给用户

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

四、jwt的实现方式

常用方式:

java-jwt

引入Maven依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.1</version>
</dependency>

获取token:

final String key = "123abc";
/**
 * @description: 测试java-jwt生成token字符串
 * @author: gsz
 * @date: 2022/3/12 10:36
 */
@Test
public void test1() {
    
    
	// 设置过期时长
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.SECOND,120);
    JWTCreator.Builder builder = JWT.create()
            //payload内容,由多个Claim组成
            .withClaim("userId", 1L)
            .withClaim("userName", "公孙瓒")
            .withExpiresAt(calendar.getTime());
    String token = builder.sign(Algorithm.HMAC256(key));
    System.out.println(token);
}

运行结果:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IuWFrOWtmeeTkiIsImV4cCI6MTY0NzA1NDMwNSwidXNlcklkIjoxfQ.AZ0REjUE5ecDtfY9_876bDqcE07zwrodLhjPyvX1d9I

校验:

@Test
public void textVerify(){
    
    
    String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6IuWFrOWtmeeTkiIsImV4cCI6MTY0NzA1NDMwNSwidXNlcklkIjoxfQ.AZ0REjUE5ecDtfY9_876bDqcE07zwrodLhjPyvX1d9I";
    DecodedJWT verify = null;
    try {
    
    
        verify = JWT.require(Algorithm.HMAC256(key)).build().verify(token);
    } catch (SignatureVerificationException e){
    
    
        e.printStackTrace();
        log.info("--> 签名不一致 <--");
    } catch (TokenExpiredException e){
    
    
        e.printStackTrace();
        log.info("--> 令牌过期 <--");
    } catch (AlgorithmMismatchException e){
    
    
        e.printStackTrace();
        log.info("--> 签名算法不匹配 <--");
    } catch (InvalidClaimException e){
    
    
        e.printStackTrace();
        log.info("--> payload不可用 <--");
    } catch (Exception e){
    
    
        e.printStackTrace();
        log.info("--> 校验失败 <--");
    }

    if (verify != null){
    
    
        // 获取payload信息,类型不匹配获取不到
        Long userId = verify.getClaim("userId").asLong();
        String userName = verify.getClaim("userName").asString();
        log.info("{},{}",userId,userName);
    }
}

控制台打印结果:

1,公孙瓒

jjwt

引入Maven依赖

<!-- jwt实现方式之jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

获取token

final String key = "123abc";

@Test
public void testGenerateToken(){
    
    
    //过期时间
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.SECOND,120);
    //创建payload的私有声明
    Map<String, Object> claims = new HashMap<>();
    claims.put("userId", 2L);
    claims.put("userName", "测试");
    JwtBuilder jwtBuilder = Jwts.builder()
            .setClaims(claims)
            .setExpiration(calendar.getTime())
            .signWith(SignatureAlgorithm.HS256, key);
    String compact = jwtBuilder.compact();
    System.out.println(compact);
}

运行结果:

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Iua1i-ivlSIsImV4cCI6MTY0NzA1NzU3NCwidXNlcklkIjoyfQ.YMXW80A8MK7MCVGSmzTY0kpTsxe3kCSOw6CwuTPrXuM

校验token

@Test
public void textVerify(){
    
    
	//刚刚获得的token
    String token = "ey...";
    Claims claims = Jwts.parser()
            .setSigningKey(key)
            .parseClaimsJws(token).getBody();
    Long userId = claims.get("userId", Long.class);
    String userName = claims.get("userName", String.class);
    log.info("{},{}",userId,userName);
}

控制台打印结果:

2,测试

后端工具类

根据 jjwt 进行封装

package common.util;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import project.entity.LoginUser;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

/**
 * Token工具类
 * @author gsz
 * @date 2022/03/11 15:27
 */
public class TokenUtil {
    
    
    public static final Logger log = LoggerFactory.getLogger(TokenUtil.class);

    // 私钥设置
    public static final String TOKEN_SECRET = "6gahgadg80asd";

    /**
     * @description: 生成token
     * @author: gsz
     * @date: 2022/3/12 13:31
     * @param loginUser
     * @return
     */
    public static String getToken(LoginUser loginUser){
    
    
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.SECOND,120);
        //创建payload的私有声明
        Map<String, Object> claims = new HashMap<>();
        claims.put("userInfo", JSON.toJSONString(loginUser));
        JwtBuilder jwtBuilder = Jwts.builder()
                .setClaims(claims)
                .setExpiration(calendar.getTime())
                .signWith(SignatureAlgorithm.HS256, TOKEN_SECRET);
        String token = jwtBuilder.compact();
        return token;
    }

    /**
     * @description: 校验token
     * @author: gsz
     * @date: 2022/3/12 13:36
     * @param tokenToBeVerify
     * @return
     */
    public static Claims verify(String tokenToBeVerify){
    
    
        Claims claims = null;
        try {
    
    
            claims = Jwts.parser()
                    .setSigningKey(TOKEN_SECRET)
                    .parseClaimsJws(tokenToBeVerify).getBody();
        } catch (SignatureVerificationException e){
    
    
            e.printStackTrace();
            log.info("--> 签名不一致 <--");
        } catch (TokenExpiredException e){
    
    
            e.printStackTrace();
            log.info("--> 令牌过期 <--");
        } catch (AlgorithmMismatchException e){
    
    
            e.printStackTrace();
            log.info("--> 签名算法不匹配 <--");
        } catch (Exception e){
    
    
            e.printStackTrace();
            log.info("--> 校验失败 <--");
        }
        return claims;
    }

    /**
     * @description: 解析
     * @author: gsz
     * @date: 2022/3/12 13:39
     * @param claims
     * @return
     */
    public static LoginUser parser(Claims claims){
    
    
        return JSON.parseObject(claims.get("userInfo", String.class), LoginUser.class);
    }
}

简单测试一下

@Test
public void test(){
    
    
    LoginUser loginUser = new LoginUser(1L, "公孙瓒", "123456");
    String token = TokenUtil.getToken(loginUser);
    Claims verify = TokenUtil.verify(token);
    LoginUser parser = TokenUtil.parser(verify);
    System.out.println(token);
    System.out.println(parser.toString());
}

控制台打印结果:

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySW5mbyI6IntcInBhc3N3b3JkXCI6XCIxMjM0NTZcIixcInVzZXJJZFwiOjEsXCJ1c2VyTmFtZVwiOlwi5YWs5a2Z55OSXCJ9IiwiZXhwIjoxNjQ3MDY1NzM1fQ.Y9lLQhJlzqRkFNLDXZh0_VSePVd9YLrwsyv7k67364U
LoginUser{
    
    userId=1, userName='公孙瓒', password='123456'}

猜你喜欢

转载自blog.csdn.net/lyc000412/article/details/123416908