JWT:JSON Web Token,是通过数字签名,以JSON对象为载体在服务间安全传输信息的方式。JWT由三部分组成:头信息、有效载体和签名。头信息包括:令牌类型和签名算法信息;有效载体是使用一个JSON对象作为数据内容,由于只是采用base64运算,并没有进行加密,因此通常存放一些非敏感数据;签名是采用不可逆签名算法对base64后的头信息拼接上点拼接上base64后的有效载体及签名秘钥进行运算得出,防止token信息被篡改,最后生成的Token是由base64后的头信息拼接上点拼接上base64后的有效载体拼接上点拼接上base64后的签名信息组成。
JWT组成:
// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
将这三部分用.
连接成一个完整的字符串,构成了最终的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
JAVA程序使用JWT
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
package com.tech.jwt.utils;
import com.tech.jwt.domain.User;
import io.jsonwebtoken.*;
import java.util.Date;
import java.util.UUID;
/**
* @author lw
* @since 2021/12/29
*/
public class JwtUtil {
private static final String secretKey="hello jwt key";
/**
* 生成 JWT TOKEN
* @param user 用户信息
* @return String token
*/
public static String createToken(User user){
JwtBuilder jwtBuilder = Jwts.builder();
String token = jwtBuilder
//header
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//payload
.claim("userId", user.getUserId())
.claim("userName", user.getUserName())
.claim("role", user.getRole())
.setSubject("admin-test")
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
.setId(UUID.randomUUID().toString())
//signature
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
return token;
}
/**
* 解析 JWT TOKEN
* @param token JWT TOKEN
* @return User user
*/
public static User parse(String token){
JwtParser parser = Jwts.parser();
Jws<Claims> claimsJws = parser.setSigningKey(secretKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
System.out.println(claims.get("userId"));
System.out.println(claims.get("userName"));
System.out.println(claims.get("role"));
System.out.println(claims.getId());
System.out.println(claims.getSubject());
System.out.println(claims.getExpiration());
User user=new User();
user.setUserId(Long.valueOf(claims.get("userId").toString()));
user.setUserName((String)claims.get("userName"));
user.setRole((String)claims.get("role"));
return user;
}
public static void main(String[] args) {
User user = new User();
user.setUserId(1L);
user.setUserName("张三");
user.setRole("admin");
String token = createToken(user);
System.out.println(token);
User u = parse(token);
System.out.println(u);
}
}
package com.tech.jwt.domain;
import lombok.Data;
/**
* @author lw
* @since 2021/12/29
*/
@Data
public class User {
private Long userId;
private String userName;
private String userPwd;
private String role;
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
", userPwd='" + userPwd + '\'' +
", role='" + role + '\'' +
'}';
}
}
package com.tech.jwt.controller;
import com.tech.jwt.domain.User;
import com.tech.jwt.utils.JwtUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
/**
* @author lw
* @since 2021/12/29
*/
@RestController
public class UserController {
/**
* 登录
* @param user user
* @return jwt token
*/
@GetMapping("login")
String login(User user){
//去数据库查询校验登录信息
if(user.getUserName().equals("admin") && user.getUserPwd().equals("123")){
user.setUserId(1L);
user.setRole("admin");
return JwtUtil.createToken(user);
}
return null;
}
/**
* 校验 jwt token
* 通常在拦截器中校验
* @param token jwt token
* @return 校验结果
*/
@GetMapping("valid")
boolean valid(@RequestHeader("token") String token ){
try {
JwtUtil.parse(token);
} catch (Exception e) {
return false;
}
return true;
}
}
封装了一个JWT工具类提供创建JWT Token和解析JWT Token的方法,登录接口,根据用户提交的登录信息,校验通过后生成JWT Token,客户端保存token,以后访问服务时需要在请求头中携带token。Token校验接口,在拦截器中进行处理,应用程序访问服务时在拦截器校验Header中携带token的有效性,如果有效则返回请求的服务资源,如果无效则返回认证失败。
一般是在请求头里加入Authorization
,并加上Bearer
标注:
fetch('api/user/1', {
headers: {
'Authorization': 'Bearer ' + token
}
})
服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:
在实际开发中,用户登录校验成功后会随机生成一个key,比如uuid,使用这个随机生成的key作为key,用户信息作为value,保存在Redis中,并设置失效时间。然后创建一个JWT TOKEN,使用随机生成的key作为jwt的有效载体,将生成好的JWT Token返给前端,前端保存在本地,当访问后端服务时,前端需要在请求头中携带 jwt token,后端通过拦截器对请求进行拦截,校验token是否有效,如果token有效,则在jwt token的有效载体中获取key,然后使用该key在redis中查找是否存在,如果存在说明登录验证通过,如果不存在说明验证登录信息已过期。