背景:传统的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": "briup@briup.com", "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-jwt3.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,Mapinfo) { 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 ListsecurityContexts() { 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值
这时,无“锁”图标的接口,在访问的时候默认不会携带设置的请求头信息
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)