JWT了解与实战
最近在使用JWT做一个单点登录与接口鉴权的功能,正好可以对JWT有深一步的了解。
一、JWT使用场景:
1. 授权:用户登录后,每个请求都包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT地一个特性,因为它开销小,并且可以轻松地跨域使用。
2. 信息交换: 对于安全的在各方之间传输信息而言,JWT无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,还可以验证内容有没有被篡改。
使用依赖:
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>7.1</version>
</dependency>
核心代码:
/**
* 采用HS256算法生成token
*
* @param payLoadMap 荷载
* @return
* @throws JOSEException
*/
public static String createTokenHS256(Map<String, Object> payLoadMap) throws JOSEException {
JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);
Payload payload = new Payload(new JSONObject(payLoadMap));
JWSObject jwsObject = new JWSObject(jwsHeader, payload);
log.info("header : " + jwsHeader);
log.info("payload : " + payload);
JWSSigner jwsSigner = new MACSigner(TokenConstants.SECRET);
jwsObject.sign(jwsSigner);
return jwsObject.serialize();
} /**
* 解析token
*
* @param token
* @return
* @throws ParseException
* @throws JOSEException
*/
public static Map<String, Object> parseTokenHS256(String token) throws ParseException, JOSEException {
JWSObject jwsObject = JWSObject.parse(token);
JWSVerifier jwsVerifier = new MACVerifier(TokenConstants.SECRET);
return verify(jwsObject, jwsVerifier);
} /**
* 验证token
*
* @param jwsObject
* @param jwsVerifier
* @return
* @throws JOSEException
*/
private static Map<String, Object> verify(JWSObject jwsObject, JWSVerifier jwsVerifier) throws JOSEException {
Map<String, Object> resultMap = new HashMap<>();
Payload payload = jwsObject.getPayload();
if (jwsObject.verify(jwsVerifier)) {
resultMap.put(TokenConstants.RESULT, TokenConstants.TOKEN_PARSE_SUCCESS);
JSONObject jsonObject = payload.toJSONObject();
resultMap.put(TokenConstants.DATA, jsonObject);
if (jsonObject.containsKey(TokenConstants.EXPIRE_TIME)) {
Long expireTime = Long.valueOf(jsonObject.get(TokenConstants.EXPIRE_TIME).toString());
Long nowTime = System.currentTimeMillis();
log.info("nowTime : " + nowTime);
if (nowTime > expireTime) {
resultMap.clear();
resultMap.put(TokenConstants.RESULT, TokenConstants.TOKEN_EXPIRED);
}
}
} else {
resultMap.put(TokenConstants.RESULT, TokenConstants.TOKEN_PARSE_FAILED);
}
return resultMap;
}
单元测试:
@Test
public void createTokenTest() {
String appId = "appId";
String appSecret = "appSecret";
Map<String, Object> map = new HashMap<>();
map.put("appId", appId);
map.put("appSecret", appSecret);
Long createTime = System.currentTimeMillis();
map.put("createTime", createTime);
map.put("expireTime", createTime + 60 * 60 * 2);
try {
String token = TokenHS256Util.createTokenHS256(map);
System.out.println(token);
log.info("token : " + token);
} catch (JOSEException e) {
log.error("create token failed. ");
e.printStackTrace();
}
} @Test
public void ValidTokenTest() {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJhcHBTZWNyZXQiOiJhcHBTZWNyZXQiLCJleHBpcmVUaW1lIjoxNTU3NDczMDI2OTg3LCJjcmVhdGVUaW1lIjoxNTU3NDczMDE5Nzg3LCJhcHBJZCI6ImFwcElkIn0.mNwTFBLOtc3hD90SI7gKV1YlahulOOartZFaLFbqK0Q";
if (token != null) {
try {
Map<String, Object> validMap = TokenHS256Util.parseTokenHS256(token);
Integer result = (Integer) validMap.getOrDefault(TokenConstants.RESULT, TokenConstants.TOKEN_PARSE_FAILED);
switch (result) {
case TokenConstants.TOKEN_PARSE_SUCCESS:
log.info("token parse success.");
JSONObject jsonObject = (JSONObject) validMap.getOrDefault(TokenConstants.DATA, StringUtils.EMPTY);
log.info("appId = " + jsonObject.getOrDefault("appId", StringUtils.EMPTY));
log.info("appSecret = " + jsonObject.getOrDefault("appSecret", StringUtils.EMPTY));
log.info("sta = " + jsonObject.getOrDefault("sta", StringUtils.EMPTY));
log.info("expire = " + jsonObject.getOrDefault("expire", StringUtils.EMPTY));
break;
case TokenConstants.TOKEN_EXPIRED:
log.error("token has expired. ");
break;
default:
log.error("token parse failed. ");
break;
}
} catch (ParseException | JOSEException e) {
e.printStackTrace();
}
}
}
二、JWT组成
JWT由三部分组成,它们之间用圆点(.)连接

我们项目里的header内容固定是:
{"alg":"HS256"}
表示使用的是HS256算法,然后用base64多对这个json编码就得到JWT的第一部分,即Header。
payload包含声明(要求),声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型:registered,public,private
registered claims: 这里有一组预定义的声明,它们不是强制的,但是推荐。比如iss(issure), exp(expiration time), sub(subject), aud(audience)等
public claims: 可以随意定义
private claims: 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。
例如:
{"appSecret":"appSecret","expireTime":1558059574173,"createTime":1558059566973,"appId":"appId"}
对payload进行base64编码就得到JWT的第二部分
注意:不要在JWT的payload或header中放置敏感信息,除非它们是加密的。
Signature:为了得到签名部分,你必须有编码过的header、编码过的payload、一个密钥,签名算法是header中指定的那个,然后对他们签名即可。
签名是用于验证消息在传递过程中有没有被更改,并且对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。
三、官网的debugger (https://jwt.io/)
(每次修改verify signature里面的内容,生成的token是会改动的,所以可以验证消息在传递过程中是否有被修改)

四、JWT的工作原理
在认证时,当用户用他们的凭证成功登陆以后,一个JSON Web Token将会被返回。此后,token就是用户凭证了,需要非常小心以防出现安全问题。一般而言,保存令牌时不应该超过你所需要它的时间。
无论何时用户想要访问受保护的路由或者资源的时候,用户代理(通常是浏览器)都应该带上JWT,典型的,通常放在Authorization header中,用Bearer schema。
header 应该看起来是这样的:
Authorization:Bearer <token>
服务器上的受保护的路由将会检查Authorization
header中的JWT是否有效,如果有效,则用户可以访问受保护的资源。
如果JWT包含足够多的必须的数据,那么就可以减少对某些操作的数据库查询的需要,尽管可能并不总是如此。
如果token是在授权头(Authorization header)中发送的,那么跨域资源共享(CORS)将不会成为问题,因为它不使用cookie。
第一步:请求授权接口,获取授权码token
第二步:使用授权码token访问受保护的资源(比如:API)
五、基于服务器端session的身份认证
HTTP协议是无状态的,即如果我们已经认证了一个用户,那么下一次请求时,服务器不知道来者何人,需要再次进行认证。
传统的做法是将已经认证过的用户信息存储在服务器上,比如session,用户下次请求时带着session ID,然后服务器以此检查用户是否认证过。
缺陷: 1. session: 每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器在此处的开销就越大。
2. scalability: 由于session是在内存中,扩展不灵活。
3. CORS: 当想要扩展我们的应用,让我们的数据被多个移动设备使用时,我们必须考虑跨资源共享问题。当使用Ajax调用从另一个域名下获取资源时,可能会遇到禁止请求的问题。
4. CSRF: 用户很容易受到CSRF攻击。
六、基于token的身份认证
基于token的身份认证是无状态的,服务器或者session中不会存储任何用户信息,没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。
主要流程:
1. 用户携带用户名、密码请求访问
2. 服务器校验用户凭证
3. 应用提供一个token给客户端
4. 客户端存储token,并且在随后的每一次请求中都带着它
5. 服务器校验token并返回数据
注意:每一次请求都要token;token应该放在请求header中,我们还需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin:*
使用token的优势: 1. 无状态和可扩展性
2. 安全,token不是cookie,每次请求的时候token都会被发送,而且由于没有cookie被发送,还有助于防止CSRF攻击。
即使在你的实现中将token存储在客户端的cookie中,这个cookie也只是一种存储机制,而非身份认证机制,没有基于会话的操作。
token在一段时间以后会过期,这个时候用户需要重新登录,这有助于我们保持安全。还有一个概念叫token撤销,它允许我们根据相同的授权许可使特定的token甚至一组token无效。
内容参考:https://www.cnblogs.com/cjsblog/p/9277677.html
JWT了解与实战的更多相关文章
- 实战模拟│JWT 登录认证
目录 Token 认证流程 Token 认证优点 JWT 结构 JWT 基本使用 实战:使用 JWT 登录认证 Token 认证流程 作为目前最流行的跨域认证解决方案,JWT(JSON Web Tok ...
- JWT—JSON Web Token - 理解JWT网络间应用用户安全认证交互设计
原文地址:http://blog.leapoahead.com/2015/09/06/understanding-jwt/ 官网地址:https://jwt.io/ JSON Web Token(JW ...
- JSON Web Token (JWT),服务端信息传输安全解决方案。
JWT介绍 JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑独立的基于JSON对象在各方之间安全地传输信息的方式.这些信息可以被验证和信任,因为它是数字签名的 ...
- Django REST framework(DRF)
Django REST framework(DRF) FBV与CBV CBV源码分析 Restful接口规范 DRF简单配置使用和源码解析 DRF序列化器 DRF10大接口 DRF视图集 三大组件 j ...
- JWT实战:使用axios+PHP实现登录认证
上一篇文中,我们学习了什么是JWT(Json Web Token),今天我们来结合实例给大家讲述JWT的实战应用,就是如何使用前端Axios与后端PHP实现用户登录鉴权认证的过程. 查看演示 下载源码 ...
- ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露
一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...
- 【从零开始搭建自己的.NET Core Api框架】(四)实战!带你半个小时实现接口的JWT授权验证
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
- 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程
[.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本 ...
- OAuth2简易实战(三)-JWT
1. OAuth2简易实战(三)-JWT 1.1. 与OAuth2授权码模式差别 授权服务器代码修改 @Configuration @EnableAuthorizationServer public ...
随机推荐
- TinyXPath 对于xpath标准的支持测试
xpath是一种基于xml的查询标准,一般的xml解析工具都具有,有的因为卓越的xpath性能而出名,其匹配查询算法牛逼而又高效,和正则有的一拼.虽然我现在大部分从事前端工作了,但是对于原理性的东西还 ...
- Java 字符串(二)字符串常用操作
一.连接字符串 1.连接多个字符串 使用“+”运算符可以实现连接多个字符串的功能.“+” 运算符可以连接多个运算符并产生一个 String 对象. 2.连接其他数据类型 字符串与其他基本数据类型进行连 ...
- SpringCloud之监控数据聚合Turbine
前言 SpringCloud 是微服务中的翘楚,最佳的落地方案. 使用 SpringCloud 的 Hystrix Dashboard 组件可以监控单个应用服务的调用情况,但如果是集群环境,可能就 不 ...
- vim巧妙用法
1. 块复制 按ctrl+v键,编辑框最下方将出现"可视 块"字样 使用方向键移动光标,选择矩形区域内的文字 y 键复制文本: d 键剪切文本:p 键粘贴文本 按shift+v键, ...
- byte[],File和InputStream的相互转换
File.FileInputStream 转换为byte[] File file = new File("test.txt"); InputStream input = new F ...
- Linux相关目录
Linux 启动流程 Linux--基本目录 Linux--selinux Linux--网卡配置 Linux--系统运行级别 Linux--重要文件
- 微信小程序获取用户手机号 记录 (PHP)
1. 用户登录时需要获取 openid ,同时可以获取 session_key, 二者同时返回, 此时我们要将二者存储在服务端. 2. 小程序端 button 按钮拉起授权, 向api 传递 iv 和 ...
- SpringCloud2.0 Feign 服务发现 基础教程(五)
1.启动[服务中心]集群,即 Eureka Server 参考 SpringCloud2.0 Eureka Server 服务中心 基础教程(二) 2.启动[服务提供者]集群,即 Eureka Cli ...
- 团队——Beta版本发布
目录 最棒团队成员信息 一.7次冲刺博客链接 二.解决的Alpha版本问题 / Beta计划任务(与Alpha版本不同点) 二.项目本次α版本的发布地址.下载安装说明 四.PM最终报告(详细报告在各冲 ...
- 转,关于TCP粘包问题的一些思路
TCP粘包分析 一 .两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通讯连接,连接建立后不断开, 然后再进行报文发送和接收. 2.短连接 Client方与Server每进 ...