最近在使用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了解与实战的更多相关文章

  1. 实战模拟│JWT 登录认证

    目录 Token 认证流程 Token 认证优点 JWT 结构 JWT 基本使用 实战:使用 JWT 登录认证 Token 认证流程 作为目前最流行的跨域认证解决方案,JWT(JSON Web Tok ...

  2. JWT—JSON Web Token - 理解JWT网络间应用用户安全认证交互设计

    原文地址:http://blog.leapoahead.com/2015/09/06/understanding-jwt/ 官网地址:https://jwt.io/ JSON Web Token(JW ...

  3. JSON Web Token (JWT),服务端信息传输安全解决方案。

    JWT介绍 JSON Web Token(JWT)是一种开放标准(RFC 7519),它定义了一种紧凑独立的基于JSON对象在各方之间安全地传输信息的方式.这些信息可以被验证和信任,因为它是数字签名的 ...

  4. Django REST framework(DRF)

    Django REST framework(DRF) FBV与CBV CBV源码分析 Restful接口规范 DRF简单配置使用和源码解析 DRF序列化器 DRF10大接口 DRF视图集 三大组件 j ...

  5. JWT实战:使用axios+PHP实现登录认证

    上一篇文中,我们学习了什么是JWT(Json Web Token),今天我们来结合实例给大家讲述JWT的实战应用,就是如何使用前端Axios与后端PHP实现用户登录鉴权认证的过程. 查看演示 下载源码 ...

  6. ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露

    一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...

  7. 【从零开始搭建自己的.NET Core Api框架】(四)实战!带你半个小时实现接口的JWT授权验证

    系列目录 一.  创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...

  8. 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本 ...

  9. OAuth2简易实战(三)-JWT

    1. OAuth2简易实战(三)-JWT 1.1. 与OAuth2授权码模式差别 授权服务器代码修改 @Configuration @EnableAuthorizationServer public ...

随机推荐

  1. 八.软件自动化和web测试

    1.软件自动化测试 1.1  自动化测试的概念   自动化测试:就是通过测试工具或其他手段,按照测试工程师的预定计划对软件产品进行自动化的测试   软件测试自动化涉及到测试流程.测试体系.自动化编译以 ...

  2. 03、新手必须掌握的Linux命令

    Ⅰ. 常用系统工作命令 1. echo 命令 echo命令用于在终端输出字符串货变量提取后的值,格式为"echo [字符串 | $变量]" 例:把指定字符串"LinxuH ...

  3. html5单词

    < meta   charset = " UTF-8 " >         国内编码     (meta-标签用来描述一个HTML网页文档的属性  charset-字 ...

  4. 【转载】C#中ArrayList集合类使用Remove方法指定元素对象

    ArrayList集合是C#中的一个非泛型的集合类,是弱数据类型的集合类,可以使用ArrayList集合变量来存储集合元素信息,任何数据类型的变量都可加入到同一个ArrayList集合中,在Array ...

  5. webpack面试题

    1.webpack的核心概念 Entry:入口,Webpack进行打包的起始点(文件) Output:出口,webpack编译打包生成的bundle(打包文件) Loader:模块加载(转换)器,将非 ...

  6. 另类WebShell监测机制--基于auditd

    鸣  谢 VSRC感谢业界小伙伴——老陈投稿精品原创类文章.VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将有好礼相送,我们为您准备的丰富奖品包括但不仅限于:MacbookAir.VSRC定制 ...

  7. Appium-desktop元素定位

    一.打开 appium-desktop ,点击 start session 二.打开后,点击屏幕右上角的搜索按钮 三.然后会打开配置页面,在本地服务配置信息同上面写的代码链接配置.填入正确的信息后,点 ...

  8. 面试常问的join

    少壮不努力,老大徒伤悲 工作大半辈子了,来到个陌生的过度,从零开始,像个应届毕业生一样投入茫茫人才市场,碰的满头包. 凡是涉及到sql server的都会问,join的问题,不烦记录下: SQL的jo ...

  9. git设置本地账户

    问题描述: git很方便,git本地如果记住账户信息 问题解决: vscode Git 全局设置: git config --global user.name "mvpbang" ...

  10. python基础笔记-字符串

    字符串是 Python 中最常用的数据类型.我们可以使用引号(‘或”)来创建字符串. def main(): str1 = 'hello,world' print(len(str1))#计算字符串的长 ...