对Axios的理解

Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 node.js,它提供了简洁的 API 和拦截器等功能,非常适合进行封装以应对复杂的请求场景。下面展示一个简单的 Axios 封装示例,包括基础配置、请求拦截器、响应拦截器以及如何使用。

安装 Axios

首先,你需要确保项目中已经安装了 Axios。如果还未安装,可以通过以下命令安装:

1
npm install axios

Axios 封装

下面的代码示例展示了如何封装 Axios,包括配置基本的请求信息、添加请求拦截器和响应拦截器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import axios from 'axios';

// 创建axios实例
const service = axios.create({
baseURL: 'https://your-api-baseurl.com', // API的base_url
timeout: 5000 // 请求超时时间
});

// 请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做些什么,例如设置token
config.headers['Authorization'] = 'Bearer ' + localStorage.getItem('token');
return config;
},
error => {
// 处理请求错误
console.error('Request Error:', error);
return Promise.reject(error);
}
);

// 响应拦截器
service.interceptors.response.use(
response => {
// 对响应数据做点什么
const res = response.data;
// 根据实际情况调整条件
if (res.code !== 200) {
alert('Error: ' + res.message);
return Promise.reject(new Error(res.message || 'Error'));
} else {
return res;
}
},
error => {
// 处理响应错误
console.error('Response Error:', error);
return Promise.reject(error);
}
);

export default service;

使用封装后的 Axios

封装完成后,你可以在项目的任何地方通过导入封装后的实例来发起请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import request from './path/to/axios'; // 路径根据实际情况修改

// 获取用户信息
const getUserInfo = () => {
return request({
url: '/user/info',
method: 'get'
});
};

// 调用示例
getUserInfo().then(response => {
console.log(response);
}).catch(error => {
console.error(error);
});

通过这种方式,你可以轻松地在整个项目中复用配置好的 Axios 实例,而不必每次请求都重复设置。此外,通过拦截器,你可以在请求发送前后执行一些通用处理,如设置认证信息、处理错误等,大大增强了代码的可维护性和复用性。

理解JWT的组成

JSON Web Token(JWT)是一种开放标准(RFC 7519),用于安全地在两方之间传输信息作为JSON对象。它在Web应用中广泛用于无状态认证机制,特别是在单点登录(SSO)中。JWT的设计目的是可以通过网络传输并且可以自验证和自包含,这意味着它们携带了所有必要的信息,以验证数据的完整性和真实性。一个JWT实际上由三个主要部分组成,它们之间用点(.)分隔,形式为xxxxx.yyyyy.zzzzz。这三部分分别是:

1. Header(头部)

头部通常由两部分组成:令牌的类型(即“JWT”)和正在使用的签名算法(如HMAC SHA256或RSA)。这是告诉接收方如何处理这个令牌的关键信息。头部是一个JSON对象,被编码为Base64Url字符串。

示例Header:

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

2. Payload(有效载荷)

有效载荷部分包含了实际要传输的数据。这里可以包含多个预定义的声明(claims)以及我们想要传输的任何其他数据。预定义的声明可能包括:iss(发行人)、exp(过期时间)、sub(主题)、aud(观众)等。与头部一样,有效载荷也是一个JSON对象,被编码为Base64Url字符串。

示例Payload:

1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

3. Signature(签名)

为了创建签名部分,你必须使用由头部指定的算法(例如,HS256)对头部的Base64编码字符串和有效载荷的Base64编码字符串进行签名,使用一个密钥。签名的目的是保证JWT的头部和有效载荷没有被篡改,并验证发送方的身份。

签名算法示例:

1
2
3
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret)

签名是将头部和有效载荷以Base64Url编码连接起来,然后用头部指定的算法和秘钥进行签名。结果也是一个Base64Url字符串。

将这三部分连接起来,就形成了一个完整的JWT。这个结构使得JWT自包含,拥有关于用户和其他元数据的所有必要信息,同时通过签名验证保证了数据的安全性。JWT的这种设计使其成为跨域认证和信息交换的理想选择。

实现基于JWT(JSON Web Tokens)的单点登录(SSO)方案,主要涉及到用户在一个系统中登录后,无需再次输入凭证即可访问其他系统的功能。这里将介绍一个基本的实现流程和JWT的存储方式。

JWT单点登录实现方案

1. 认证中心建立

  • 设置一个认证中心,所有的登录请求都通过这个中心进行。
  • 用户首次登录时,通过用户名和密码等凭证向认证中心发送请求。

2. 验证用户并生成JWT

  • 认证中心验证用户的凭证。
  • 凭证验证通过后,认证中心生成一个JWT,其中包含用户的身份信息和其他必要的声明(claims),然后将这个JWT返回给用户。

3. JWT的存储

  • 客户端接收到JWT后,可以选择将其存储在Cookie、LocalStorage或SessionStorage中。每种方式都有其适用场景和安全性考虑。

4. 访问受保护的资源

  • 用户在访问其他系统(服务提供者)的受保护资源时,需要在请求的Authorization头部附带JWT。
  • 系统接收到请求后,从Authorization头部提取JWT,验证JWT的有效性。验证通过后,根据JWT中的信息提供相应的服务。

5. JWT的续签和失效处理

  • JWT通常设有过期时间。系统需要合理设置过期时间,并提供JWT续签的机制。
  • 用户访问时,如果JWT已经接近过期,系统可以自动续签一个新的JWT返回给用户。

JWT的存储方式

  • 将JWT存储在Cookie中是常见的做法,可以利用HttpOnly和Secure属性来增强安全性。
  • Cookie跨域名不共享,但可以设置为对特定域名有效,适用于子域共享Token。

2. LocalStorage

  • LocalStorage提供了更大的存储空间,易于前端JS脚本读取和管理。
  • 但LocalStorage易受XSS攻击,需小心处理存储的数据。

3. SessionStorage

  • SessionStorage的作用域限于当前会话,关闭浏览器标签页后数据消失,适用于敏感度较高的场景。
  • 与LocalStorage相似,也易受XSS攻击。

安全考虑

  • 在JWT中不应包含敏感数据,因为JWT是可解码的。
  • 使用HTTPS传输JWT,避免中间人攻击。
  • 考虑JWT的刷新策略和失效处理,防止过期的Token被滥用。
  • 防范XSS和CSRF攻击,当JWT存储在Cookie中时,使用SameSite属性可以减少CSRF攻击的风险。

通过上述方案,可以实现一个基于JWT的单点登录系统,同时注意安全性和用户体验的平衡。

当用户登录时间较长或用户信息数据较大时,确实可能导致JWT的内容变大。这不仅会增加客户端和服务器之间传输数据的负担,还可能引起一些性能和安全问题。以下是几种处理策略:

1. 减少JWT负载

  • 仅包含必要信息:确保JWT中只包含进行用户认证和授权所必需的信息,比如用户ID、用户名或角色等。敏感信息或不必要的信息不应包含在JWT中。
  • 使用用户会话ID:而不是直接在JWT中存储大量用户信息,可以仅在JWT中存储一个会话ID或令牌ID。然后,应用服务器可以使用这个ID从数据库或内存中检索完整的用户信息。

2. 分割JWT

  • 如果确实需要传输大量信息,可以考虑将JWT分割成多个较小的Token,每个Token负责不同的任务。例如,一个Token用于身份验证,另一个Token用于授权。

3. 使用JWT仅作为会话令牌

  • JWT只用作会话令牌(Session Tokens),而具体的用户信息、权限等则存储在服务器端的会话存储中。这样,JWT本身保持轻量级,而用户相关的数据则由服务器管理。

4. 刷新Token

  • 实现Token刷新机制。当用户活跃时,定期刷新JWT,同时在刷新过程中更新或减少JWT的负载。这样可以确保JWT中的信息是最新的,同时避免Token因过期而频繁重新登录。

5. 状态服务器

  • 对于非常大型的应用,可以引入一个状态服务器(如OAuth2.0的授权服务器),专门负责管理用户的登录状态和会话信息。这样,JWT只需包含一个指向该状态服务器的引用标识符,由状态服务器处理具体的用户数据和权限检查。

安全性考虑

  • 确保所有敏感操作(如Token刷新、用户信息的检索和更新)都通过安全的通道进行,最好是HTTPS,以防止中间人攻击。
  • 对于存储在客户端的JWT和其他认证信息,要注意防范XSS攻击,避免存储过多敏感信息。
  • 对于服务端,需要确保对用户信息的访问进行严格的权限控制,防止未授权访问。

通过上述策略,可以有效地管理JWT的大小,同时保持系统的性能和安全性。