Shiro + JWT实现Token验证的快速入门

章节目录1. 用户认证1.1 Session认证1.1.1 什么是Session?1.1.2 什么是Session认证?1. 用户认证用户认证一般分为:Session认证、Token认证(JWT是一种特殊的Token认证)1.1 Session认证Session认证用于一般的Web项目中。1.1.1 什么是Session?HTTP协议是一种无状态协议。即:每次服务端接收客户端的请求时,都是一个全新的请求,服务端并不知道客户端的历史请求。例如说:当客户端进行账号和密码通过了身份认证,接着向服务端发
摘要由CSDN通过智能技术生成

1. 用户认证

进入JWT主题之前,先来回顾一下用户认证吧。用户认证一般分为:Session认证、Token认证(JWT是一种特殊的Token认证)

1.1 Session认证

Session认证用于一般的Web项目中。

1.1.1 Session原理

        HTTP协议是一种无状态协议。即:每次服务端接收客户端的请求时,都是一个全新的请求,服务端并不知道客户端的历史请求。例如说:当客户端进行账号和密码通过了身份认证,接着向服务端发送下一个请求,又需要验证。而Session和Cookie的主要目的就是为了弥补 HTTP 的无状态特性。

        客户端第一次请求服务端时,服务端会为这次请求开辟一块内存空间,创建对象(Session 对象,存储结构为 ConcurrentHashMap),同时生成一个 sessionId ,并通过响应头的 Set-Cookie:JSESSIONID=XXXXXXX 命令,向客户端发送要求设置 Cookie 的响应;客户端收到响应后,在本机客户端设置了一个 JSESSIONID=XXXXXXX 的 Cookie 信息,该 Cookie 的过期时间为浏览器会话结束。接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie 信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId。

服务器可以利用 Session 来存储客户端在同一个会话期间的一些操作记录。

1.1.2 Session认证流程

        接下来就来简述下Session认证流程:当客户端进行身份认证时,如果认证通过,就在服务端的Session中生成一条记录,这个记录里面包含用户信息,然后把 JSESSIONID发送给客户端,客户端收到以后,就把这个 JSESSIONID存储在 Cookie 里,下次这个用户再向服务端发送请求的时候,就带着这个 Cookie 。这样服务端会验证这个 Cookie 里的信息,看能否找到对应的记录。如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。

如下图所示:
在这里插入图片描述

1.1.3 代码实现

public User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
   
	String username = request.getParameter("username");
	String password = request.getParameter("password");
	User user = userService.login(username, password);
	if (user == null) {
   
		throw new AuthenticationException("用户名或密码错误");
	}
	// 返回这次请求关联的当前会话,如果没有会话则创建一个新的
	// 需要在服务器端记录该session
	HttpSession session = request.getSession();
	session.setAttribute("user", user);
	return user;
}
public Object getUserInfo(HttpServletRequest request){
   
	// 从request中获取Cookie,从Cookie中获取sessionid
	// 根据sessionid获取对应的Session对象从session中获取关联的用户信息
	HttpSession session = request.getSession();
	Object user = session.getAttribute("user");
	return user;
}

1.1.4 缺点

  1. sessio需要存在服务器内存中,这样就不能跨实例共享。当下一次请求被分发到另一个实例的时候,就会造成重新登录。
  2. session依赖于浏览器的cookie机制,对于移动客户端就很难支持。移动端使用token。
  3. 在高并发情况下,session放在内存中受限于内存的大小

1.2 Token认证

Token认证一般用于前后端分离的项目

1.2.1 Token认证流程

Token认证 在服务端不需要存储用户的登录记录。其大概的流程是:

  1. 客户端使用用户名跟密码请求登录接口
  2. 登录接口收到请求并验证用户名和密码
  3. 登录接口验证成功后,会生成一个uuid作为token,将用户信息作为值,然后保存到redis缓存中jedis.set(token, user);
  4. 登录接口返回用户信息和token
  5. 浏览器将token保存到本地
  6. 当请求其它接口时就携带token值
  7. 接口根据token去缓存中查,如果找到了就调用接口,如果找不到报token错误(一般通过拦截器来实现检查)

如下图所示:
在这里插入图片描述

1.2.2 代码实现

public String auth(String username, String password) throws AuthenticationException {
   
	User user = userService.login(username, password);
	if (user == null) {
   
		throw new AuthenticationException("用户名或密码错误");
	}
	String token = UUID.randomUUID().toString();
	redisClient.set(token, user);
	return token;
}
public Object getUserInfo(@RequestHeader("token") String token) throws AuthenticationException {
   
	User user = redisClient.get(token);
	if (user == null) {
   
		throw new AuthenticationException("token不可用");
	}
	return user;
}

实施 Token 验证的方法挺多的,还有一些标准方法,比如 JWT。

2. JWT

2.1 JWT简介

        JWT 全称 Json Web Token。它是 RFC 7519 中定义的,用于安全地将信息作为 Json 对象进行传输的一种形式。JWT 中存储的信息是经过“数字签名”的,因此可以被信任和理解。可以使用 HMAC 算法或使用 RSA/ECDSA 的公用/专用 密钥对 JWT 进行签名。

JWT的主要用途:

  1. 认证:一旦用户登录,后面每个请求都会包含 JWT,从而允许用户访问该令牌所允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小。
  2. 信息交换:JWT 是能够安全传输信息的一种方式。通过使用公钥/私钥对 JWT 进行签名认证。此外,由于签名是使用 head 和 payload 计算的,因此你还可以验证内容是否遭到篡改。

2.2 JWT格式

JWT 主要由三部分组成,每个部分用 . 进行分割,各个部分分别是:

  1. Header
  2. Payload
  3. Signature

如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

2.2.1 Header

Header是 JWT 的标头,包括两部分信息:

  • 令牌的类型:JWT
  • 加密算法:HMAC SHA256 或 RSA

如:

{"alg": "HS256", "typ": "JWT"}

然后将 Header 进行 base64编码 构成了第一部分:

eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9

base64编码
Base64是一种用64个字符来表示任意二进制数据的编码方法

Java中可以使用java.util.Base64进行编码、解码。如:

public class Test {
   
    public static void main(String[] args) throws Exception{
   
        Base64.Encoder encoder = Base64.getEncoder();
        Base64.Decoder decoder = Base64.getDecoder();

        String header = "{\"alg\": \"HS256\", \"typ\": \"JWT\"}";
        byte[] headerBytes = header.getBytes();
        // 编码
        String encodeHeader = encoder.encodeToString(headerBytes);
        System.out.println(encodeHeader);
        // 解码
        byte[] decode = decoder.decode(encodeHeader);
        System.out.println(new String(decode, "UTF-8"));
    }
}

在这里插入图片描述

2.2.2 Payload

Payload 中包含一个声明。声明是有关实体(通常是用户)和其他数据的声明。共有三种类型的声明:registered, public 和 private 声明

2.2.2.1 registered 声明

registered 声明:包含一组建议使用的预定义声明,主要包括:

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
2.2.2.2 public 声明

public 声明:公共的声明,可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。

2.2.2.3 private 声明

private 声明:自定义声明,旨在在同意使用它们的各方之间共享信息,既不是注册声明也不是公共声明。

如:

{"sub":"1234567890","name":"John Doe","iat":1516239022}
  • name:自定义字段
  • sub/iat:标准声明

然后将 Payload进行 base64编码 构成了第二部分:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ

2.2.3 Signature

Signature表示签证信息,它包含三个部分:

  • header (base64后的)
  • payload (base64后的)
  • secret

比如我们需要 HMAC SHA256 算法进行签名

HMACSHA256(
	base64UrlEncode(header) + "." +
	base64UrlEncode(payload),
	secret
)

签名用于验证消息在此过程中没有更改,并且对于使用私钥进行签名的令牌,它还可以验证 JWT 的发送者的真实身份

2.3 JWT的应用场景

详细地请查看:理解JWT的使用场景和优劣

应用场景:

  1. 一次性验证:如:用户注册后需要发一封邮件让其激活账户
  2. restful api的无状态认证:使用 jwt 来做 restful api 的身份认证

好了,接下来就是用Shrio集成JWT咯!

3. Shiro 集成JWT

其实,这个项目的代码是在这篇博客-----使用SpringBoot集成Shiro的简易教程 的代码上进行改编的。不过,为了各位读者方便,这里还是会贴出源代码的

3.1 项目信息

3.1.1 开发环境

IDEA:2018.2(lombok插件)
SpringBoot:2.3.1.RELEASE
Shiro:1.3.2
JWT:3.2.0

3.1.2 项目结构图

在这里插入图片描述

3.1.3 功能描述

        在此项目中规定:每次请求时,需要在请求头中带上 token ,通过 token 来检验权限;如果没有带上token,则说明当前为游客状态或者其他不需要验证的接口

3.1.4 项目依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<!-- java-jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</vers
  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值