JWT介绍

jwt介绍

token认证

随着 Restful API、微服务的兴起,基于 Token 的认证现在已经越来越普遍。基于token的用户认证是一种服务端无状态的认证方式,所谓服务端无状态指的token本身包含登录用户所有的相关数据,而客户端在认证后的每次请求都会携带token,因此服务器端无需存放token数据。

当用户认证后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage 等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份

 市面上基于token的认证方式大都采用的是JWT(Json Web Token)。

JWT

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。

JWT令牌结构:

JWT令牌由Header、Payload、Signature三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz

Header

头部包括令牌的类型(即JWT)以及使用的哈希算法(如如HMAC、SHA256或RSA),定义第三部分签名使用的算法。使用Base64Url编码,得到一个字符串就是JWT令牌的第一部分。

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

Payload

第二部分是负载,内容 是一个json对象,它是存放有效信息的地方,可以存放自定义字段信息,一般用户的非隐私信息,此部分可以解码还原原始内容。 。内置字段信息:jti唯一标识 iat创建时间 exp过期时间  sub面向对象。将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分

{
	"sub": "1234567890",
	"name": "456",
	"admin": true
}

Signature

签名:用于防止jwt内容被篡改

这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。

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

base64UrlEncode(header):jwt令牌的第一部分。base64UrlEncode(payload):jwt令牌的第二部分。 secret:签名所使用的密钥。

在idea中使用token

导入依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
</dependency>

单元测试

/**
 * jwt生成与解析介绍
 */
public class JWTTest {


    /**
     * 生成JWT(一般在登录成功后将生成的JWT当做TOKEN来使用)
      */
    @Test
    public void testCreateJwt(){
        //eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjZTAyNzQ5NC1hZjc4LTQ1MTMtODY2Ni0xYjQxMTNiZjc4ZWQiLCJzdWIiOiJhbGwiLCJ1c2VybmFtZSI6InpoYW5nc2FuIiwidXNlcklkIjoxMjMsImlhdCI6MTYxNDE0OTg3NSwiZXhwIjoxNjE0MTUzNDc1fQ.AxKYDz5vHg_EnGxm8Z9zH6oDpSKQZBjDiiEsNt_aec8
        

        String secret = "itcast"; //对称加密密钥
        String token = Jwts.builder().signWith(SignatureAlgorithm.HS256, secret)
                .setId(UUID.randomUUID().toString()) //jwt内置属性jti,jwt的唯一标识
                .setSubject("all")   //jwt内置属性sub,面向用户
                .claim("username", "zhangsan")  //自定义属性username
                .claim("userId",123)            //自定义属性userId
                .setIssuedAt(new Date())//jwt内置属性iat,创建时间
                .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) //1小时过期,过期时间
                .compact();
        System.out.println(token);
    }

    /**
     * 解析JWT(一般登陆后访问系统时需要携带TOKEN字符串,系统会使用JWT工具类校验TOKEN字符串)
* jwt解析失败:
     * 1)jwt被篡改了 抛出异常SignatureException
     * 2)jwt过期了 抛出异常ExpiredJwtException
     */
    @Test
    public void testParseJwt(){
        String secret = "itcast"; //对称加密密钥
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjZTAyNzQ5NC1hZjc4LTQ1MTMtODY2Ni0xYjQxMTNiZjc4ZWQiLCJzdWIiOiJhbGwiLCJ1c2VybmFtZSI6InpoYW5nc2FuIiwidXNlcklkIjoxMjMsImlhdCI6MTYxNDE0OTg3NSwiZXhwIjoxNjE0MTUzNDc1fQ.AxKYDz5vHg_EnGxm8Z9zH6oDpSKQZBjDiiEsNt_aec8";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
        System.out.println("头:" + claimsJws.getHeader());
        System.out.println("载荷:"   +  claimsJws.getBody());
        System.out.println("userId:" +  claimsJws.getBody().get("userId"));
        System.out.println("签名:" + claimsJws.getSignature());
    }

}

工具类

import io.jsonwebtoken.*;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

public class AppJwtUtil {

    // TOKEN的有效期一天(S)
    private static final int TOKEN_TIME_OUT = 3_600;
    // 加密KEY
    private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
    // 最小刷新间隔(S)
    private static final int REFRESH_TIME = 300;

    // 生产ID
    public static String getToken(Long id){
        Map<String, Object> claimMaps = new HashMap<>();
        claimMaps.put("id",id);
        long currentTime = System.currentTimeMillis();
        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date(currentTime))  //签发时间
                .setSubject("system")  //说明
                .setIssuer("heima") //签发者信息
                .setAudience("app")  //接收用户
                .compressWith(CompressionCodecs.GZIP)  //数据压缩方式
                .signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
                .setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳
                .addClaims(claimMaps) //cla信息
                .compact();
    }

    /**
     * 获取token中的claims信息
     *
     * @param token
     * @return
     */
    private static Jws<Claims> getJws(String token) {
            return Jwts.parser()
                    .setSigningKey(generalKey())
                    .parseClaimsJws(token);
    }

    /**
     * 获取payload body信息
     *
     * @param token
     * @return
     */
    public static Claims getClaimsBody(String token) {
        try {
            return getJws(token).getBody();
        }catch (ExpiredJwtException e){
            return null;
        }
    }

    /**
     * 获取hearder body信息
     *
     * @param token
     * @return
     */
    public static JwsHeader getHeaderBody(String token) {
        return getJws(token).getHeader();
    }

    /**
     * 是否过期
     *
     * @param claims
     * @return -1:有效,0:有效,1:过期,2:过期
     */
    public static int verifyToken(Claims claims) {
        if(claims==null){
            return 1;
        }
        try {
            claims.getExpiration()
                    .before(new Date());
            // 需要自动刷新TOKEN
            if((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){
                return -1;
            }else {
                return 0;
            }
        } catch (ExpiredJwtException ex) {
            return 1;
        }catch (Exception e){
            return 2;
        }
    }

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    public static void main(String[] args) {
       /* Map map = new HashMap();
        map.put("id","11");*/
        System.out.println(AppJwtUtil.getToken(1102L));
        Jws<Claims> jws = AppJwtUtil.getJws("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAADWLQQqEMAwA_5KzhURNt_qb1KZYQSi0wi6Lf9942NsMw3zh6AVW2DYmDGl2WabkZgreCaM6VXzhFBfJMcMARTqsxIG9Z888QLui3e3Tup5Pb81013KKmVzJTGo11nf9n8v4nMUaEY73DzTabjmDAAAA.4SuqQ42IGqCgBai6qd4RaVpVxTlZIWC826QA9kLvt9d-yVUw82gU47HDaSfOzgAcloZedYNNpUcd18Ne8vvjQA");
        Claims claims = jws.getBody();
        System.out.println(claims.get("id"));

    }
}

全局过滤器中实现jwt校验

统一认证Token处理逻辑

1.获取请求和响应实例。2.如果路径是登录,则放行。3.如果路径非登录获取请求头token。4.如果token为空,响应401。5.如果token解析过期,响应401。6.成功放行。7.如果解析Token异常,响应401

 

import com.heima.utils.common.AppJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //2.判断是否是登录接口,是直接放行
        String path = request.getURI().getPath();
        if(path.equals("/user/api/v1/login/login_auth/")){
            return chain.filter(exchange);
        }


        //3.非登录接口,获取token
        String token = request.getHeaders().getFirst("token");


        //4.判断token是否存在,不存在返回401(无权访问)
        if(StringUtils.isBlank(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();     //请求被拒绝
        }


        //5.判断token是否有效,无效返回401(无权访问)
        try {
            Claims claimsBody = AppJwtUtil.getClaimsBody(token);
            int result = AppJwtUtil.verifyToken(claimsBody);
            if(result==0){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
        } catch (Exception e) {
            e.printStackTrace();
            //篡改token,无效返回401
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }


        //6.放行
        return chain.filter(exchange);
    }

    /**
     * 优先级设置  值越小  优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值