JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;它是分布式服务权限控制的标准解决方案!
它跟RBAC的区别:两者不冲突,在项目中后台权限服务的数据库设计使用RBAC,而前端项目访问后台微服务的权限校验使用jwt
官网:https://jwt.io
.GitHub上jwt的java客户端:https://github.com/jwtk/jjwt
什么是token:https://www.cnblogs.com/xuxinstyle/p/9675541.html
普通的token:32位UUID
JWT的token:至少64位
JWT的token包含三部分数据:
结论:
1 jwt的一个有规则的token
2 它有三部分组成:Header.payload.signature,每部分都是通过base64加密而成的
3 jwt每个部分都是可以解密的
Base64编码之所以称为Base64,是因为其使用64个字符来对任意数据进行编码,同理有Base32、Base16编码。标准Base64编码使用的64个字符如下:
这64个字符是各种字符编码(比如ASCII码)所使用字符的子集,并可打印。唯一有点特殊的是最后两个字符。
Base64本质上是一种将二进制数据转成文本数据的方案。对于非二进制数据,是先将其转换成二进制形式,然后每连续6比特(2的6次方=64)计算其十进制值,根据该值在上面的索引表中找到对应的字符,最终得到一个文本字符串。假设我们对Hello!进行Base64编码,按照ASCII表,其转换过程如下图所示:
可知Hello!的Base64编码结果为SGVsbG8h,原始字符串长度为6个字符串,编码后长度为8个字符,每3个原始字符经编码成4个字符。
但要注意,Base64编码是每3个原始字符编码成4个字符,如果原始字符串长度不能被3整除,怎么办?使用0来补充原始字符串。
以Hello!!为例,其转换过程为:
Hello!!
Base64编码的结果为 SGVsbG8hIQAA
。最后2个零值只是为了Base64编码而补充的,在原始字符中并没有对应的字符,那么Base64编码结果中的最后两个字符 AA
实际不带有效信息,所以需要特殊处理,以免解码错误。
标准Base64编码通常用 =
字符来替换最后的 A
,即编码结果为 SGVsbG8hIQ==
。因为 =
字符并不在Base64编码索引表中,其意义在于结束符号,在Base64解码时遇到 =
时即可知道一个Base64编码字符串结束。
如果Base64编码字符串不会相互拼接再传输,那么最后的 =
也可以省略,解码时如果发现Base64编码字符串长度不能被4整除,则先补充 =
字符,再解码即可。
解码是对编码的逆向操作,但注意一点:对于最后的两个 =
字符,转换成两个A
字符,再转成对应的两个6比特二进制0值,接着转成原始字符之前,需要将最后的两个6比特二进制0值丢弃,因为它们实际上不携带有效信息。
总结:
1、base64的编码/加密原理
答:原理:将键盘输入的字符用base64编码表示
过程:将键盘输入字符的ascii码值,转成的对应8位二进制,将该二进制6个一组拆分,并计算拆分之后的十进制值,找出十进制值在base64编码表中对应的字母,即完成base64加密
结论:
1.4.JWT交互流程
流程图:
.
步骤翻译:
• 1、用户登录
• 2、服务的认证,通过后根据secret生成token
• 3、将生成的token返回给用户
• 4、用户每次请求携带token
• 5、服务端利解读jwt签名,判断签名有效后,从Payload中获取用户信息
• 6、处理请求,返回响应结果
因为JWT签发的token中已经包含了用户的身份信息,并且每次请求都会携带,这样服务的就无需保存用户信息,甚至无需去数据库查询,就能知道用户身份,完全符合了Rest的无状态规范。
我们逐步演进系统架构设计。需要注意的是:secret是签名的关键,因此一定要保密,我们放到鉴权中心保存,其它任何服务中都不能获取secret。
在微服务架构中,我们可以把服务的鉴权操作放到网关中,将未通过鉴权的请求直接拦截,如图:
流程图解:
• 1、用户请求登录
• 2、Zuul将请求转发到授权中心,请求授权
• 3、授权中心校验完成,颁发JWT凭证
• 4、客户端请求其它功能,携带JWT
• 5、Zuul将jwt交给授权中心校验,通过后放行
• 6、用户请求到达微服务
• 7、微服务将jwt交给鉴权中心,鉴权同时解析用户信息
• 8、鉴权中心返回用户数据给微服务
• 9、微服务处理请求,返回响应
结论:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.9.RELEASEversion>
<relativePath/>
parent>
<groupId>com.czxygroupId>
<artifactId>jwt-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<name>jwt-parentname>
<description>Demo project for Spring Bootdescription>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Hoxton.SR10spring-cloud.version>
<mybatis.starter.version>2.1.1mybatis.starter.version>
<mapper.starter.version>1.2.3mapper.starter.version>
<druid.starter.version>1.1.9druid.starter.version>
<mysql.version>5.1.32mysql.version>
<pageHelper.starter.version>1.2.3pageHelper.starter.version>
<jjwt.version>0.7.0jjwt.version>
<joda-time.version>2.9.6joda-time.version>
<lombok.version>1.18.18lombok.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.6.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis.starter.version}version>
dependency>
<dependency>
<groupId>tk.mybatisgroupId>
<artifactId>mapper-spring-boot-starterartifactId>
<version>${mapper.starter.version}version>
dependency>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
<version>${pageHelper.starter.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>${druid.starter.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>${jjwt.version}version>
dependency>
<dependency>
<groupId>joda-timegroupId>
<artifactId>joda-timeartifactId>
<version>${joda-time.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
略。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jwt-parentartifactId>
<groupId>com.czxygroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>jwt-commonartifactId>
<dependencies>
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
dependency>
<dependency>
<groupId>joda-timegroupId>
<artifactId>joda-timeartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
project>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jwt-parentartifactId>
<groupId>com.czxygroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>jwt-pojoartifactId>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
project>
Goods商品实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
private Integer skuid;
private String goodsName;
private Double price;
}
User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}
1、用户未登陆状态,可以搜索商品信息
2、goods-search为商品搜索服务,接收用户页面搜索请求
实现步骤:
1、pojo
2、controller
4、service
5、dao
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.czxygroupId>
<artifactId>jwt-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>goods-searchartifactId>
<packaging>jarpackaging>
<name>goods-searchname>
<description>Demo project for Spring Bootdescription>
<dependencies>
<dependency>
<groupId>com.czxygroupId>
<artifactId>jwt-pojo01artifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.czxygroupId>
<artifactId>jwt-common01artifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
spring:
application:
name: goods-service
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
server:
port: 7000
模拟商品搜索功能
@SpringBootApplication
public class GoodsSearch01Application {
public static void main(String[] args) {
SpringApplication.run(GoodsSearch01Application.class, args);
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.czxygroupId>
<artifactId>jwt-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>user-serviceartifactId>
<packaging>jarpackaging>
<name>user-servicename>
<description>Demo project for Spring Bootdescription>
<dependencies>
<dependency>
<groupId>com.czxygroupId>
<artifactId>jwt-pojo01artifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.czxygroupId>
<artifactId>jwt-common01artifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
spring:
application:
name: userservice
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
server:
port: 8000
@SpringBootApplication
public class UserService01Application {
public static void main(String[] args) {
SpringApplication.run(UserService01Application.class, args);
}
}
授权中心的主要职责:
因为生成jwt,解析jwt这样的行为以后在其它微服务中也会用到,因此我们会抽取成工具。我们把鉴权中心进行聚合,一个工具module,一个提供服务的module
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>com.czxygroupId>
<artifactId>jwt-parentartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>jwt-authartifactId>
<packaging>jarpackaging>
<name>jwt-authname>
<description>Demo project for Spring Bootdescription>
<dependencies>
<dependency>
<groupId>com.czxygroupId>
<artifactId>jwt-pojo01artifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>com.czxygroupId>
<artifactId>jwt-common01artifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
spring:
application:
name: authservice
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
server:
port: 9000
@SpringBootApplication
public class JwtAuth01Application {
public static void main(String[] args) {
SpringApplication.run(JwtAuth01Application.class, args);
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>jwt-parentartifactId>
<groupId>com.czxygroupId>
<version>0.0.1-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>jwt-zuulartifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
server:
port: 10010 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: goods-service # 路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
uri: lb://goodsservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Path=/search/** # 这个是按照路径匹配,只要以/user/开头就符合要求
- id: user-service
uri: lb://userservice
predicates:
- Path=/user/**
- id: auth-service
uri: lb://authservice
predicates:
- Path=/auth/**
@Component
@Order(-1)
public class JWTFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String url = exchange.getRequest().getURI().getPath();
System.out.println(url);
// 1 判断URL
if(url.toString().contains("login")){
System.out.println("无需登录,直接放行");
return chain.filter(exchange);
}
// 2.获取请求参数
// MultiValueMap params = exchange.getRequest().getQueryParams();
// // 3.获取authorization参数
// String token = params.getFirst("authorization");
List<String> tokens = exchange.getRequest().getHeaders().get("authorization");
// 4判断是否为空
if(tokens!=null&&(tokens.size()==1)){
// 5 解析token
Claims claims = JWTUtil.parseToken(tokens.get(0), "user");
//6 判断解析是否成
if(claims!=null){
//7 成功了,放行
return chain.filter(exchange);
}
}
// 8.拦截
// 8.1.禁止访问,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 8.2.结束处理
return exchange.getResponse().setComplete();
}
}