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 ...
随机推荐
- 【转载】C#中ArrayList集合类使用Remove方法指定元素对象
ArrayList集合是C#中的一个非泛型的集合类,是弱数据类型的集合类,可以使用ArrayList集合变量来存储集合元素信息,任何数据类型的变量都可加入到同一个ArrayList集合中,在Array ...
- js两个不同类型值比较Boolean(0=='')
写js遇到的问题 本以为 Boolean(0=='') 结果为true 可是在控制台执行 Boolean(0==' ')trueBoolean(0==null)false 百度得知,两个不同类型值比较 ...
- net start mysql 失败提示“NET HELPMSG 3534”
lz使用的window系统8.0.16版本的mysql,以下四步解决如上问题: 1. mysqld -remove 2. mysqld --initialize 3.mysqld -install(m ...
- linux mysql连接
1. 添加头文件 # apt-get install libmysqlclient-dev 引入头文件 #include <mysql/mysql.h> 2. 举例 MYSQL *mysq ...
- Spark ML协同过滤推荐算法
一.简介 协同过滤算法[Collaborative Filtering Recommendation]算法是最经典.最常用的推荐算法.该算法通过分析用户兴趣,在用户群中找到指定用户的相似用户,综合这些 ...
- nginx 一个端口布署多个单页应用(history路由模式)。
目前web开发 使用一般前后端分离技术,并且前端负责路由.为了美观,会采用前端会采用h5 history 模式的路由.但刷新页面时,前端真的会按照假路由去后端寻找文件.此时,后端必须返回index(i ...
- spec开发思路以及理解
一.spec说明 描述:编写SEPC采用创联公司自主开发的CIT语言,它是一种过程化的.类似数据库编码的语言.SPEC中除了关键字外提倡使用中文. 理解:可以理解为业务逻辑层.链接前台页面和后台数据库 ...
- postgresql源代码结构
转载学习: 德哥培训! 源码下载: https://www.postgresql.org/ftp/source/ 1.postgressql源码目录结构 2.src目录结构
- php fsockopen()方法,简化,异步非阻塞调用
介绍在项目中遇到一个问题,就是php是同步的读取下来的,如果一个方法请求的时间长了一点, 那么整个程序走下去将会遇到阻塞,现在我想触发这个方法,但是又不影响我下下面的程序正常的走下去.查了一上午的方法 ...
- C#常用的图片处理方法-图片剪切、图片压缩、多图合并代码
/// <summary> /// 图片转成圆角方法二 /// </summary> private Bitmap WayTwo(Bitmap bitmap) { //usin ...