什么是 JJWT:
JJWT 是一个提供端到端的JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。
JJWT 快速入门
1. 在 mengxuegu-member-util/pom.xml 添加 jjwt 依赖( jdk8 以下)
<dependencies> <!--jwt令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
</dependencies>
如果使用 JDK9 还要添加以下依赖:
默认情况下,在 java SE 9.0 中 将不再包含 java EE 的 Jar 包,而 JAXB API 是 java EE 的 API ,因此我们要手动导
入这个 Jar 包 。
<dependencies> <!--jwt令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId> .
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
<dependencies> <!--jwt令牌-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
</dependencies>
</dependency>
</dependencies>
创建普通测试类 TestJwt ,用于生成 token :
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class TestJwt {
public static void main(String[] args) {
String token = createJwt();
System.out.println("生成的令牌:"+ token);
}
// 生成令牌
public static String createJwt () {
JwtBuilder builder= Jwts.builder().setId("11111") // 是字符串
.setSubject("admin") // 主题 如用户名
.setIssuedAt(new Date()) // 签发时间
.signWith(SignatureAlgorithm.HS256,"ceshiyuan"); // 签名密钥
return builder.compact();
}
//eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMTExMSIsInN1YiI6ImFkbWluIiwiaWF0IjoxNTkwNDYyNDUyfQ.EbF6wKi KRzFrJVShGurQnfhK36mDp2_ykiYTj5J0uOc
再次运行,会发现每次运行的结果是不一样的,因为我们每次的签发时间是不一样的。
解析 jwt 令牌:
当服务端生成 token 后发给客户端,客户端在下次向服务端发送请求时需要携带这个 token ( 这就好像是拿着一张门票一 样) ,那服务端接到这个 token 应该解析出 token 中的信息 ( 例如用户名 ), 根据这些信息 查询数据库返回相应的结果。
在 TestCreateJwt 类添加一个方法 parserJwt 来解析 jwt 令牌中信息:
package com.ape.springboot.controller;
import com.sun.org.apache.xml.internal.security.algorithms.SignatureAlgorithm;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
public class TestCreateJwt {
public static void main(String[] args) {
String token = createJwt();
System.out.println("生成的令牌:" + token);
parserJwt(token);
}
// 生成令牌
public static String createJwt() {
JwtBuilder builder = Jwts.builder().setId("11111") // 是字符串
.setSubject("admin") // 主题 如用户名 .setIssuedAt(new Date()) // 签发时间
.signWith(SignatureAlgorithm.HS256, "ceshiyuan"); // 签名密钥
return builder.compact();
}
// 解析令牌
public static void parserJwt(String token) {
Claims claims = Jwts.parser().setSigningKey("ceshiyuan") // 签名密钥要一致
.parseClaimsJws(token).getBody();
System.out.println("id:" + claims.getId());
System.out.println("subject:" + claims.getSubject());
// System.out.println("IssuedAt:"+claims.getIssuedAt());
}
}
如果将 token 或签名秘钥篡改一下,会发现运行时就会报错,所以解析 token 也就是验证 token
校验 Jwt 令牌是否过期
一般我们并不希望签发的 token 是永久生效的,所以我们可以为 token 添加一个过期时间。
// 生成令牌
public static String
createJwt
() {
// 当前时间毫秒数
long now
=
System
.
currentTimeMillis
();
// 过期时间为
10
秒
long exp
=
now
+
1000
*
10
;
JwtBuilder builder =
Jwts
.
builder
().
setId
(
"11111"
)
//
是字符串
. setSubject
(
"admin"
)
//
主题 如用户名
. setIssuedAt
(
new
Date
())
//
签发时间
. signWith
(
SignatureAlgorithm
.
HS256
,
"ceshiyuan"
)
//
签名密钥
. setExpiration
(
new
Date
(
exp
));
//
过期时间
+++++++
return builder
.
compact
();
}
测试:先生成一个token ,然后等待10秒后再去解析它,发现报如下错
Exception in thread "main" io.jsonwebtoken.ExpiredJwtException: JWT expired at
创建JWT工具类
JwtUtil 代码实现如下:
import io .
jsonwebtoken
.
Claims
;
import io .
jsonwebtoken
.
JwtBuilder
;
import io .
jsonwebtoken
.
Jwts
;
import io .
jsonwebtoken
.
SignatureAlgorithm
;
import org .
springframework
.
boot
.
context
.
properties
.
ConfigurationProperties
;
import org .
springframework
.
context
.
annotation
.
Configuration
;
import org .
springframework
.
stereotype
.
Component
;
import java .
util
.
Date
;
@Component
@ConfigurationProperties ( prefix
=
"mengxuegu.jwt.config"
)
public class JwtUtil {
// 密钥
private String secretKey
;
// 单位秒,默认 7
天
private long expires
=
60
*
60
*
24
*
7
;
public String getSecretKey
() {
return secretKey ;
}
public void setSecretKey
(
String
secretKey
) {
this . secretKey
=
secretKey
;
}
public long getExpires
() {
return expires ;
}
public void setExpires
(
long
expires
) {
this . expires
=
expires
;
}
/**
* 生成 JWT
* @param id
*/
public String createJWT
(
String
id
,
String
subject
,
Boolean
isLogin
) {
long nowMillis =
System
.
currentTimeMillis
();
Date now = new
Date
(
nowMillis
);
JwtBuilder builder = Jwts
.
builder
().
setId
(
id
)
. setSubject (
subject
)
. setIssuedAt (
now
)
. signWith (
SignatureAlgorithm
.
HS256
,
secretKey
)
. claim (
"isLogin"
,
isLogin
);
if ( expires
>
0
) {
// expires 乘以 1000
是毫秒转秒
builder . setExpiration
(
new
Date
(
nowMillis
+
expires
*
1000
));
3.
在
mengxuegu-member-api
模块的
application.yml
中添加配置
业务层添加 login 方法
校验登录用户名密码,登录成功后,生成 token 响应给客户端
1. 在 com.mengxuegu.member.service.impl.IStaffffService
接口添加
login
抽象方法
2. 在 com.mengxuegu.member.service.impl.StaffffServiceImpl
实现
login
方法
}
return builder .
compact
();
}
/**
* 解析 JWT
* @param jwtToken
*/
public Claims parseJWT (
String
jwtToken
){
return Jwts .
parser
().
setSigningKey
(
secretKey
)
. parseClaimsJws (
jwtToken
).
getBody
();
}
}
application.yml 中添加配置
ceshiyuan :
jwt :
config :
secretKey : mengxuegu
# jwt
令牌密钥
expires : 604800
#
单位秒,
7
天
校验登录用户名密码,登录成功后,生成 token 响应给客户端
@Autowired
JwtUtil jwtUtil ;
@Override
public Result login
(
String
username
,
String
password
) {
Result error = Result
.
error
(
"
用户名或密码错误
"
);
if ( StringUtils
.
isBlank
(
username
)
|| StringUtils .
isBlank
(
password
)) {
return error ;
} 控制层添加 login 方法
在 com.mengxuegu.member.controller.AuthController 控制类中添加
login
方法
测试登录
登录请求,发送 POST 请求
localhost:6666/user/login
// 1. 通过用户名查询
Staff staff = getByUsername
(
username
);
if ( staff
==
null
) {
return error ;
}
// 2. 存在,判断密码是否正确 (
输入的密码,数据库加密的密码
)
if ( !
new
BCryptPasswordEncoder
().
matches
(
password
,
staff
.
getPassword
())) {
return error ;
}
// 3. 生成 token
响应
String jwt =
jwtUtil
.
createJWT
(
staff
.
getId
()
+
""
,
staff
.
getUsername
(),
true
);
// 手动封装个 json
对象
{token: jwt}
Map < String
,
String
>
map
=
new
HashMap
<>
();
map . put
(
"token"
,
jwt
);
return Result .
ok
(
map
);
}
控制层添加 login 方法
/**
* 登录
* @return
*/
@PostMapping ( "/login"
)
public Result login
(
@RequestBody
Staff staff
) {
return staffService .
login
(
staff
.
getUsername
(),
staff
.
getPassword
());
}
测试登录
登录请求,发送 POST 请求
localhost:6666/user/login
{
"username" : "admin"
,
"password" : "123456"
}