简介
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT结构
JWT 的三个部分依次如下。
Header(头部)
Payload(负载)
Signature(签名)
1.Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。
{
“alg”: “HS256”,
“typ”: “JWT”
}
上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
2.Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。这个 JSON 对象也要使用 Base64URL 算法转成字符串。
3.Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
生产token和验证token的测试demo
@Test
void contextLoads() {
//生产token
Calendar instance = Calendar.getInstance();
instance.add(Calendar.SECOND,1000);
String token = JWT.create()
//设置载体
.withClaim("username","zhangsan")
.withClaim("userid",12)
//token过期时间
.withExpiresAt(instance.getTime())
//签名
.sign(Algorithm.HMAC256("123456"));
System.out.println(token);
}
@Test
void contextLoads2() {
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("123456")).build();
//验证token
DecodedJWT verify = jwtVerifier.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDM2Mjk3NDYsInVzZXJpZCI6MTIsInVzZXJuYW1lIjoiemhhbmdzYW4ifQ.G8LCea3egNGrBLNr996iX6-ir0i7dqJkFWJaqGKclGI\n");
//验证成功后取出载体
System.out.println(verify.getClaims().get("username").toString());
System.out.println(verify.getClaims().get("userid").asInt());
}
整合springboot登录功能demo
引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
配置yml文件(这里不查数据库,直接在这里定义用户正确的登录名和密码)
server:
port: 8888
Login:
username: zhangsan
password: 123456
创建实体
@Data
public class User {
private Integer id;
private String password;
private String username;
}
创建JWTutils
public class JWTUtils {
//获取token
public static String getToken(User u){
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE,7); //默认令牌过期时间7天
JWTCreator.Builder builder = JWT.create();
builder.withClaim("userId",u.getId())
.withClaim("username",u.getUsername());
String token = builder.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(u.getPassword()));
return token;
}
//验证token合法性 成功返回token
public static DecodedJWT verify(String token){
//获取登录用户真正的密码假如数据库查出来的是123456
String password = “123456”;
JWTVerifier build = JWT.require(Algorithm.HMAC256(password)).build();
DecodedJWT verify = build.verify(token);
return verify;
}
}
创建jwt拦截器
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
try {
JWTUtils.verify(token);
}catch (SignatureVerificationException e){
e.printStackTrace();
System.out.println("无效签名");
return false;
}catch (TokenExpiredException e){
e.printStackTrace();
System.out.println("token过期");
return false;
}catch (AlgorithmMismatchException e){
e.printStackTrace();
System.out.println("token算法不一致");
return false;
}catch (Exception e){
e.printStackTrace();
System.out.println("token无效");
return false;
}
return true;
}
}
将拦截器注入到MVC
@Configuration
public class IntercaptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor())
//拦截的路径
.addPathPatterns("/**")
//排除登录接口
.excludePathPatterns("/user/login");
}
}
contoller测试
@RestController
public class UserLoginContoller {
@Value("${Login.username}")
private String realUsername;
@Value("${Login.password}")
private String realPassword;
@GetMapping("user/login")
public String login(String username,String password){
if(username.equals(realUsername) && password.equals(realPassword)){
User u = new User();
u.setPassword(password);
u.setUsername(username);
String token = JWTUtils.getToken(u);
return token;
}
return "登录失败!账号或者密码不对!";
}
@GetMapping("user/test")
public String testLogin(){
return "登录成功后,才可以访问!";
}
}
先登录login成功之后会返回token,然后用postman Header带着token访问user/test验证