SpringBoot 2.x 使用 JWT(JSON Web Token)
一、跨域认证遇到的问题
由于多终端的出现,很多的站点通过 web api restful 的形式对外提供服务,采用了前后端分离模式进行开发,因而在身份验证的方式上可能与传统的基于 cookie 的 Session Id 的做法有所不同,除了面临跨域提交 cookie 的问题外,更重要的是,有些终端可能根本不支持 cookie。
JWT(JSON Web Token) 是一种身份验证及授权方案,简单的说就是调用端调用 api 时,附带上一个由 api 端颁发的 token,以此来验证调用者的授权信息。
一般流程是下面这样:
1. 用户向服务器发送用户名和密码。
2. 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
3. 服务器向用户返回一个 session_id,写入用户的 Cookie。
4. 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
5. 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。
这种模式的问题在于扩展性不好。单机没有问题,如果是服务器集群、跨域的服务导向架构或者用户禁用了 cookie ,就不行了。
二、解决方案
1. 单机和分布式应用下登录校验,session 共享
单机和多节点
tomcat应用登录检验①、单机
tomcat应用登录,sesssion保存在浏览器和应用服务器会话之间,用户登录成功后,服务端会保证一个session,也会给客户端一个sessionId,客户端会把sessionId保存在cookie中,用户每次请求都会携带这个sessionId。②、多节点
tomcat应用登录,开启session数据共享后,每台服务器都能够读取session。缺点是每个session都是占用内存和资源的,每个服务器节点都需要同步用户的数据,即一个数据需要存储多份到每个服务器,当用户量到达百万、千万级别的时,占用资源就严重,用户体验特别不好!!分布式应用中
session共享①、真实的应用不可能单节点部署,所以就有个多节点登录
session共享的问题需要解决。tomcat支持session共享,但是有广播风暴;用户量大的时候,占用资源就严重,不推荐②、
Reids集群,存储登陆的token,向外提供服务接口,Redis可设置过期时间(服务端使用UUID生成随机64位或者128位token,放入Redis中,然后返回给客户端并存储)。③、用户第一次登录成功时,需要先自行生成
token,然后将token返回到浏览器并存储在cookie中,
并在Redis服务器上以token为key,用户信息作为value保存。后续用户再操作,可以通过HttpServletRequest对象直接读取cookie中的token,并在Redis中取得相对应的用户数据进行比较(用户每次访问都携带此token,服务端去Redis中校验是否有此用户即可)。④、 缺点:必须部署
Redis,每次必须访问Redis,IO开销特别大。
2. 最终解决方案:使用 JWT 实现 Token 认证
JWT 的原理
服务器认证以后,生成一个 JSON 对象发回给用户,以后用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。也就是说服务器就不保存任何 session 数据了,即服务器变成无状态了,从而比较容易实现扩展。
简单来说,就是通过一定规范来生成 token,然后可以通过解密算法逆向解密 token,这样就可以获取用户信息
优点和缺点
优点:生产的 token 可以包含基本信息,比如 id、用户昵称、头像等信息,避免再次查库;存储在客户端,不占用服务端的内存资源
缺点:token 是经过 base64 编码,所以可以解码,因此 token 加密前的对象不应该包含敏感信息(如用户权限,密码等)
JWT 格式组成:头部+负载+签名 ( header + payload + signature )
头部:主要是描述签名算法。
负载:主要描述是加密对象的信息,如用户的 id 等,也可以加些规范里面的东西,如 iss 签发者,exp 过期时间,sub 面向的用户。
签名:主要是把前面两部分进行加密,防止别人拿到 token 进行base 解密后篡改 token。
3. 案例图设计

三、代码演示案例
pom.xml 文件引入依赖和实体类
<!-- 依赖可以减少实体类 getter/setter等方法书写 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JWT相关 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency> ==================================================================================== @Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable { private Integer id;
private String openid;
private String name;
private String headImg;
private String phone;
private String sign;
private Integer sex;
private String city;
private Date createTime; }
生成 JWT 工具类
public class JwtUtil { // 主题
public static final String SUBJECT = "RookieLi"; // 秘钥
public static final String SECRETKEY = "Rookie666"; // 过期时间
public static final long EXPIRE = 1000 * 60 * 60 * 24 * 7; //过期时间,毫秒,一周 // 生成 JWT
public static String geneJsonWebToken(User user) { if (user == null ||
user.getId() == null ||
user.getName() == null ||
user.getHeadImg() == null) { return null;
}
String token = Jwts.builder()
.setSubject(SUBJECT)
.claim("id", user.getId())
.claim("name", user.getName())
.claim("img", user.getHeadImg())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.signWith(SignatureAlgorithm.HS256, SECRETKEY).compact(); return token;
} // 校验 JWT
public static Claims checkJWT(String token) { try {
final Claims claims = Jwts.parser().setSigningKey(SECRETKEY).
parseClaimsJws(token).getBody();
return claims; } catch (Exception e) {
e.printStackTrace();
}
return null;
}
}测试 JWT 工具类
public class JwtUtilTest { @Test
public void testGeneJwt(){ User user = new User();
user.setId(999);
user.setHeadImg("I'm busy");
user.setName("Rookie");
String token = JwtUtil.geneJsonWebToken(user);
System.out.println(token); } @Test
public void testCheck(){ // 下面此 token 字符串是上面的结果生成的,每次不一样,不是写死的
String token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJSb29raWVMaSIsImlkIjo5OTksIm5hbWUiOiJSb29raWUiLCJpbWciOiJJJ20gYnVzeSIsImlhdCI6MTU2NzMxNjk4NywiZXhwIjoxNTY3OTIxNzg3fQ.FJh41VwVh2gh5-_cOG0SOgoO3dR_ZcK9VWNNskWqKl0";
Claims claims = JwtUtil.checkJWT(token);
if(claims != null){
String name = (String)claims.get("name");
String img = (String)claims.get("img");
int id =(Integer) claims.get("id");
System.out.println(name);
System.out.println(img);
System.out.println(id);
}else{
System.out.println("非法token");
}
}
}
参考博客:
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
SpringBoot 2.x 使用 JWT(JSON Web Token)的更多相关文章
- 如何在SpringBoot中集成JWT(JSON Web Token)鉴权
这篇博客主要是简单介绍了一下什么是JWT,以及如何在Spring Boot项目中使用JWT(JSON Web Token). 1.关于JWT 1.1 什么是JWT 老生常谈的开头,我们要用这样一种工具 ...
- Java JWT: JSON Web Token
Java JWT: JSON Web Token for Java and Android JJWT aims to be the easiest to use and understand libr ...
- JWT(JSON Web Token) 【转载】
JWT(JSON Web Token) 什么叫JWTJSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. 一般来说,互联网用户认证是这样子的. 1.用户向服务器发送用户名和密码. ...
- [更新]一份包含: 采用RSA JWT(Json Web Token, RSA加密)的OAUTH2.0,HTTP BASIC,本地数据库验证,Windows域验证,单点登录的Spring Security配置文件
没有任何注释,表怪我(¬_¬) 更新: 2016.05.29: 将AuthorizationServer和ResourceServer分开配置 2016.05.29: Token获取采用Http Ba ...
- ( 转 ) 什么是 JWT -- JSON WEB TOKEN
什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点 ...
- 关于JWT(Json Web Token)的思考及使用心得
什么是JWT? JWT(Json Web Token)是一个开放的数据交换验证标准rfc7519(php 后端实现JWT认证方法一般用来做轻量级的API鉴权.由于许多API接口设计是遵循无状态的(比如 ...
- 什么是JWT(Json Web Token)
什么是 JWT (Json Web Token) 用户认证是计算机安全领域一个永恒的热点话题. JWT 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519). 该to ...
- API安全验证之JWT(JSON WEB TOKEN) OLCMS
假如www.olcms.com/getUserInfo获取用户信息,你怎么知道当前用户是谁?有人说登陆时候我把他UID写入session了,如果是API接口,没有session怎么办,那么就需要把UI ...
- 5分钟搞懂:JWT(Json Web Token)
https://www.qikegu.com/easy-understanding/892 JWT 基于token的用户认证原理:让用户输入账号和密码,认证通过后获得一个token(令牌),在toke ...
- JWT(Json Web Token)认证
目录 JWT(Json Web Token) JWT的数据结构 JWT的用法 JWT验证流程
随机推荐
- mocha.js
mocha 如果你听说过“测试驱动开发”(TDD:Test-Driven Development),单元测试就不陌生. 单元测试是用来对一个模块.一个函数或者一个类来进行正确性检验的测试工作. 比如对 ...
- Struts2学习笔记 - namespace命名空间
默认的命名空间“ namespace="" ”. 根命名空间 “ namespace="/" ”. <package name="test&qu ...
- 手写实现indexOf
突然奇想.自己手写一个indexOf package com.toov5.test; public class Test8 { public static int find(String str1, ...
- Android学习--apk打包过程
1. 使用aapt工具,给所有的res目录下的资源文件生成对应的id,id会被放进R.java文件中 2. JavaC编译器,将所有Java文件转换为Class文件,其中,内部类会分别生成.class ...
- Android开发时包名、签名、渠道和版本号的易坑点(转)
本文中总结一下 Android 开发中容易被忽视的一些注意事项吧: 一.谨慎选择包名 包名 (Package Name) 就相当于一款应用在户口本上登记的名字,是系统用来区分不同应用的字段.重复的包名 ...
- leetcode上一些常见的链表问题
92-按规定区间反转链表 思路:可以考虑成一种把前后数字的结点断开重新组合的问题 /** * Definition for singly-linked list. * struct ListNode ...
- 自动化测试之CSS定位
之前做自动化测试一直用RF框架来操作,发现了明显与unittest的灵活性相差一点. 重新温习了unittest框架,其中这个框架,元素定位是难点,以前更多的使用JQUERY方式定位, 发现其实与CS ...
- JVM内存图
- Python系列——常用第三方库
幕布视图(更加方便.明了):https://mubu.com/doc/AqoVZ8x6m0 参考文献:嵩天老师的Python讲义 模块 定义 计算机在开发过程中,代码越写越多,也就越难以维护,所以为了 ...
- application/json和application/x-www-form-urlencoded参数接收
application/json ajax请求中content-type:application/json代表参数以json字符串传递给后台,controller接收需要@RequestBody 接收 ...