杂七杂八之基于JSON Web Token (JWT) 进行API认证和鉴权(Java版)
在现代Web应用和API开发中,JSON Web Token (JWT) 是一种广泛使用的认证和鉴权机制。JWT不仅简化了认证流程,还提供了安全的令牌传递方式,使得跨域认证变得更加容易。本文将详细介绍如何使用JWT进行API认证和鉴权,并提供一个简单的示例。
什么是JSON Web Token (JWT)?
JSON Web Token (JWT) 是一种开放标准 (RFC 7519),用于在网络应用之间安全地传输信息。JWT是一个紧凑的、URL安全的字符串,可以包含声明(claims),这些声明用于传输关于实体(通常是用户)的信息。JWT通常由三部分组成:
- Header:包含令牌的类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。
- Payload:包含声明,即关于实体的信息。声明可以分为注册声明、公有声明和私有声明。
- Signature:用于验证消息的完整性和确保消息来自可信方。
一个典型的JWT可能如下所示:
JWT的工作原理
- 生成JWT:
● 用户通过用户名和密码进行登录。
● 服务器验证用户身份后,生成一个JWT。
● 服务器将JWT返回给客户端。 - 使用JWT:
● 客户端在每次请求中将JWT放在请求头的Authorization字段中,格式为Bearer 。
● 服务器接收到请求后,解析JWT并验证其签名。
● 服务器根据JWT中的信息进行鉴权,决定是否允许请求。
使用JWT进行API认证和鉴权
第一步:我们首先使用maven引入相关包
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
第二步:我们编写类AuthService,目的是生成对应的token信息
/**
* @author Sam Zhang
* @date 2024-11-09 08:54:54
* @since 0.0.1
* 注释:登录用户生成token信息
*/
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class AuthService {
private static final String SECRET_KEY = "analysis_service";
private static final long EXPIRATION_TIME = 86400000; // 1 day in milliseconds:86400000
/***
* @author Sam Zhang
* @date 2024/11/9 9:00
* @Description: 验证用户名密码,并且进行token生成
* @param: username:目前写死用户名密码
* @param: password:目前写死用户名密码
* @return * @return java.lang.String
*/
public String login(String username, String password) {
// 这里验证用户名和密码
// 验证成功后生成Token
if(username.equals("authUser") && password.equals("authPasswd")){
return generateToken(username);
}else{
return "username or password is incorrect";
}
}
/**
* @author Sam Zhang
* @date 2024/11/9 9:04
* @Description: 生成token
* @param: username:通过用户名生成token
* @return * @return java.lang.String
*/
private String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
}
第三步:我们编写类TokenService,对生成的token进行后续校验使用
/**
* @author Sam Zhang
* @date 2024-11-09 09:03:49
* @since 0.0.1
* 注释:验证Token的合法性
*/
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
public class TokenService {
private static final String SECRET_KEY = "analysis_service";
/***
* @author Sam Zhang
* @date 2024/11/9 9:16
* @Description: 检查token是否还有效
* @param: token:获取的token值
* @return * @return boolean
*/
public boolean isTokenValid(String token) {
if (token != null && token.startsWith("Bearer:[\"")) {
token = token.substring(9,token.length()-2);
try {
// 解析Token并验证
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
return true; // 验证通过
} catch (Exception e) {
return false; // 验证失败
}
}else {
return false; // 验证失败
}
}
public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
}
第四步:对外暴露接口,我们使用SpringBoot框架进行实现,在controller层新建类AuthService,我们提供如下对外接口
import cn.hutool.json.JSONObject;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Sam Zhang
* @date 2024-11-09 09:18:33
* @since 0.0.1
* 注释:鉴权相关服务
*/
@RestController
public class AuthService {
@Autowired
GenerateAuthlmpl generateAuthlmpl;
@ApiOperation(value = "使用用户名密码进行鉴权以获得token信息", notes = "使用用户名密码进行鉴权以获得token信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "jsonObject", value = "后续接口调用都需要鉴权",required=true,paramType = "body")
})
@PostMapping(value = "/gettoken",produces = "application/json;charset=UTF-8")
public JSONObject getToken(@RequestBody JSONObject jsonObject) {
JSONObject result = new JSONObject();
CheckRequestBody checkRequestBody = new CheckRequestBody();
boolean b = checkRequestBody.checkGetToken(jsonObject);
if(!b){
result.append("result",GETTOKENMISSBODY);
result.append("msg","请检查请求body是否缺少对应字段");
return result;
}
//检查没有问题以后,获取对应的字段信息
String username = jsonObject.getStr("username");
String password = jsonObject.getStr("password");
//数据处理后传给后端进行token生成
LoginInfos loginInfos = new LoginInfos();
loginInfos.setUsername(username);
loginInfos.setPassword(password);
result = generateAuthlmpl.generateAuth(loginInfos);
//返回结果
return result;
}
}
第五步:当我们需要获取对应token以后,我们需要对后续的接口进行接口验证,我们其他接口的示例如下
import cn.hutool.json.JSONObject;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Sam Zhang(27988)
* @date 2024-11-06 10:51:40
* @since 0.0.1
* 注释:文件处理相关接口
*/
@RestController
public class DealWithFiles {
private final Logger log = LoggerFactory.getLogger(this.getClass());
ExecutorService executorService = Executors.newFixedThreadPool(1);
@Autowired
DownLoadLogslmpl downLoadLogslmpl;
@ApiOperation(value = "下载日志并且进行分析", notes = "下载日志并且进行分析")
@ApiImplicitParams({
@ApiImplicitParam(name = "Authorization", value = "获取的token信息",required=true,paramType = "header"),
@ApiImplicitParam(name = "jsonObject", value = "分析接口请求内容",required=true,paramType = "body")
})
@PostMapping(value = "/analysis",produces = "application/json;charset=UTF-8")
public JSONObject analysisLogs(@RequestHeader("Authorization") String authHeader, @RequestBody JSONObject jsonObject) {
JSONObject result = new JSONObject();
//校验消息头
TokenService tokenService = new TokenService();
boolean tokenValid = tokenService.isTokenValid(authHeader);
if(!tokenValid){
result.append("result",UNAUTH);
result.append("msg","鉴权校验失败,请检查");
return result;
}
//后续代码省略
}
}
接口验证
鉴权接口
- 接口名称:/gettoken
- 接口方法:post
- 消息头:Content-Type:application/json;charset=UTF-8
- 编码:UTF-8
- 请求消息体格式:Json格式
{
"username": "XXX",
"password": "XXX"
}
● username:鉴权用户名,当前只有这么1个。
● password:鉴权密码,当前只有这么1个。
6. 响应消息体格式:Json格式
{
"msg": [
"token生成成功"
],
"token": [
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoVXNlciIsImlhdCI6MTczMTMwMzMzMywiZXhwIjoxNzMxMzg5NzMzfQ.DlxEJjQpITT_y_5W_lZS67hVJ1PTHQdMI7De1mQKH04"
],
"result": [
100
],
"username": [
"XXX"
]
}
● msg:返回的消息格式。
● token:返回的token信息。
● result:返回的结果码,100表示请求成功。
● username:对应用户鉴权。
需要被验证接口
- 接口名称:/analysis
- 接口方法:post
- 消息头:
● Content-Type:application/json;charset=UTF-8
● Authorization: Bearer:[“token信息”],样例:Authorization: Bearer:[“eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoVXNlciIsImlhdCI6MTczMTMwMzMzMywiZXhwIjoxNzMxMzg5NzMzfQ.DlxEJjQpITT_y_5W_lZS67hVJ1PTHQdMI7De1mQKH04”]
● 消息头如果验证不通过,则会有如下结果返回
{
"msg":["鉴权校验失败,请检查"],
"result":[104]
}
至此,我们基于JSON Web Token (JWT)的鉴权完成。