springboot——jwt(java web token)一个开放标准(RFC 7519)

springboot——jwt(java web token)一个开放标准(RFC 7519),第1张

springboot——jwt(java web token)一个开放标准(RFC 7519) 概述

背景:传统的Web应用中,使用session来存在用户的信息,每次用户认证通过以后,服务器需要创建一条记录
保存用户信息,通常是在内存中。

随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大,由于Session是在内存中的,这就带来一些扩展性的问题

servlet依赖于web容器

描述:JSON Web Token (JWT,token的一种),是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

JWT存放在客户端(前端),每次请求的请求头中,携带此JWT发送给服务器,服务器端负责接收
和验证
服务器端可以不用存储JWT,这样可以降低服务器的内存的开销
JWT和语言无关,扩展起来非常方便,无论是PC端还是移动端,都可以很容易的使用
不受cookie的限制

session和JWT的主要区别就是保存的位置,session是保存在服务端的,而JWT是保存在客户端的

JWT就是一个固定格式的字符串,jwt的官网是:https://jwt.io/

结构

JWT固定各种的字符串,由三部分组成:
Header,头部
Payload,载荷
Signature,签名
把这三部分使用点(.)连接起来,就是一个JWT字符串

头部

header一般的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。

JWT里验证和签名使用的算法列表如下:

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

载荷

payload主要用来包含声明(claims ),这个声明一般是关于实体(通常是用户)和其他数据的声明。

声明有三种类型:

registered
public
private

具体如下:

Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

Public claims : 可以随意定义

自定义数据:存放在token中存放的key-value值

Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明

{
    "iss": "briup",
    "iat": 1446593502,
    "exp": 1446594722,
    "aud": "www.briup.com",
    "sub": "[email protected]",
    "username": "tom"
}

注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的

把头部和载荷分别进行base64编码之后得到两个字符串,然后再将这两个编码后的字符串用英文句号.连接在一起(头部在前),形成新的字符串:aaa.bbb

签名

最后,将上面拼接完的字符串用HS256算法进行加密,在加密的时候,还需要提供一个密钥(secret)。加密后的内容也是一个字符串,这个字符串就是签名。

把这个签名拼接在刚才的字符串后面就能得到完整的JWT字符串。
header部分和payload部分如果被篡改,由于篡改者不知道密钥是什么,也无法生成新的signature部分,
服务端也就无法通过。

在JWT中,消息体是透明的,使用签名可以保证消息不被篡改。

确保密钥不会泄露,否则会被篡改

例如,使用HMACSHA256加密算法,配合秘钥,将前俩部进行加密,生成签名

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

例如,将Header、Payload、Signature三部分使用点(.)连接起来

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI2MmI2OWNlZC02YWNlLTRmYzAtOTk5MS00Y WUwMjIxODQ0OTciLCJleHAiOjE2MDYwNTQzNjl9.DNVhr36j66JpQBfcYoo64IRp84dKiQeaq7axHTBcP9 E

例如,使用官网提供的工具,可以对该JWT进行验证和解析

我们使用JWT封装的工具类,也可以完成此 *** 作

整合

依赖


		    com.auth0
		    java-jwt
		    3.11.0
		

工具类

public class JwtUtil {
	
    private static final long EXPIRE_TIME = 5 * 60 * 1000;
    
    private static final String SECRET = "jwt_secret";

    
    public static String sign(String userId,Map info) {
        try {
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            return JWT.create()
                    // 将 user id 保存到 token 里面
                    .withAudience(userId)
                    // 存放自定义数据
                    .withClaim("info", info)
                    // 五分钟后token过期
                    .withExpiresAt(date)
                    // token 的密钥
                    .sign(algorithm);
        } catch (Exception e) {
        	e.printStackTrace();
            return null;
        }
    }

    
    public static String getUserId(String token) {
        try {
            String userId = JWT.decode(token).getAudience().get(0);
            return userId;
        } catch (JWTDecodeException e) {
            return null;
        }
    }
    
    
    public static Map getInfo(String token) {
        try {
            return JWT.decode(token).getClaim("info").asMap();
        } catch (JWTDecodeException e) {
            return null;
        }
    }
    

    
    public static boolean checkSign(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    // .withClaim("username", username)
                    .build();
            verifier.verify(token);
            return true;
        } catch (JWTVerificationException exception) {
            throw new RuntimeException("token 无效,请重新获取");
        }
    }
}

拦截器

// 拦截认证资源
public class JwtInteceptors implements HandlerInterceptor{

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// 判断是不是认证资源(根据拦截器配置 auth
	private List security() {
		return Collections.singletonList(new ApiKey("Authorization", "token", "header"));
	}

	
	private List securityContexts() {

		List antPaths = new ArrayList();
		antPaths.add("/auth
	private Predicate antPathsCondition(List antPaths) {

		List> list = new ArrayList<>();

		antPaths.forEach(path -> list.add(PathSelectors.ant(path)));

		return Predicates.or(list);

	}

	
	private List defaultAuth() {
		AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
		AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
		authorizationScopes[0] = authorizationScope;
		return Collections.singletonList(new SecurityReference("Authorization", authorizationScopes));
	}

}


这里比之前新增的代码如下:

新增代码如图所示,以及下面新增的几个方法

这里会显示一个“锁”的图标,表示这里有些接口是需要认证的
展开模块后,访问路径符合要求接口,也会显示“锁”的图标

点击锁的图标,添加请求头中的统一认证信息(token):


点击认证按钮后,进行访问测试:

这时候,有“锁”图标的接口,在访问的时候,都会携带刚刚设置的请求头中的认证信息token值

这时,无“锁”图标的接口,在访问的时候默认不会携带设置的请求头信息

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5596732.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存