Jwt的新手入门教程
1.Jwt究竟是什么东东?
学习交流君羊【】暗号:67
再贴官方的定义:
What is JSON Web Token?
JSON Web Token (JWT) is an open (RFC 7519) that a and self- way for as a JSON . This can be and it is . JWTs can be using a (with the HMAC ) or a / key pair using RSA or ECDSA.
JWTs can be to also
, we will focus on . can the
of the it, while
hide those from other . When are using
/ key pairs, the also that only the
party the key is the one that it.
我的理解总结:Jwt全称是Json Web Token,顾名思义就是一种通过Json方式来传输的令牌,并且他最大的特点就是 ,签发者可以使用各种的加密方式对信息进行签名,更重要的是Jwt还能验证token是否被篡改或者token是否正确。当然了,这种方式注定是不安全的。我们很容易地就可以在官网得到这一结论。文章来源乐字节
可以看到,只要我们获得token字符串,就可以获取到里面的大部分信息,除了签名的密钥。
所以Jwt简单来说,就是简单地存储部分不那么重要的信息,通过Json,对客户端进行验证的一种方式。
2.Jwt的组成
从官网的界面,我们可以得知,Jwt由三部分组成。
"alg": "HS256","typ": "JWT"
这个的含义就是"alg"–(算法) 是HS256。
{"sub": "1234567890","name": "John Doe","admin": true
}
但是要注意的是,记录的这些信息是完全公开的,所以千万不能把用户或者系统的敏感数据放到中,只是负责记录简单信息,并不具备加密的功能。
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
可以说这部分是Jwt的重点,因为他承担着两个作用,第一个:验证在传输过程中没有受到篡改 第二个:验证签发人的身份
详细说一下这两个作用。
验证在传输过程中没有受到篡改,这个原理就比较好理解,因为中有一个字符串,也就是,这个是由我们来设置的,相当于私钥,只有我们自己才知道,别人是不知道的。那么在后续验证过程中,只要我们自己生产的Token与客户端传来的Token进行比对,如果一致那就证明该Token没有受到篡改,反之则证明客户端传来的Token是非法的。
验证签发人的身份,其实第二个作用是从第一个作用中展现而来的,因为如果能证明Token在传输过程中没有受到篡改,那就更加说明服务端是这个签名的签发者,因为只有签发者才知道私钥。
其实我们也可以认为是盐(salt),我们知道如果单纯地在数据库中存储明文密码,或者是只经过一重MD5加密的密码,是非常的不安全,因为就算是经过MD5加密,仍然可以通过暴力穷举的方式来进行破解,可是如果在用户密码的基础上,加上我们生成的随机或固定的字符串,然后再进行加密,那么安全程度会大大提升。
如果不知道盐(salt)的童鞋,可以去bind搜索一下相关资料,其实理解起来就是用户密码+我们设置的随机或固定字符串再进行多重加密。
3.编码实现
我们来创建这几个类。
配置类:
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**").excludePathPatterns("/login");}@Beanpublic JwtInterceptor authenticationInterceptor() {return new JwtInterceptor();}}
控制类:
@RestController
public class UserController {@PostMapping("/login")public Map, Object> login(@RequestBody User user) {Map, Object> map = new HashMap<>();String username = user.getUsername();String password = user.getPassword();// 省略 账号密码验证// 验证成功后发送tokenString token = JwtUtil.sign(username, password);if (token != null) {map.put("code", "200");map.put("message", "认证成功");map.put("token", token);return map;}map.put("code", "403");map.put("message", "认证失败");return map;}@GetMapping(value = "/api/test")public String get(){System.out.println("执行了get请求");return "success";}
}
服务类:
@Service
public class UserService {public String getPassword(){return "admin";}
}
实体类 User
@Data
public class User {private String username;private String password;}
自定义拦截器类
@Component
public class JwtInterceptor implements HandlerInterceptor {@AutowiredUserService userService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从 http 请求头中取出 tokenString token = request.getHeader("token");// 如果请求不是映射到方法直接通过if(!(handler instanceof HandlerMethod)){return true;}if (token != null){String username = JwtUtil.getUserNameByToken(request);// 这边拿到的 用户名 应该去数据库查询获得密码,简略,步骤在service直接获取密码boolean result = JwtUtil.verify(token,username,userService.getPassword());if(result){System.out.println("通过拦截器");return true;}}return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
Jwt工具类
public class JwtUtil {// Token一天后过期public static final long EXPIRE_TIME = 1000 * 60 * 60 * 24;//检验Token是否正确public static boolean verify(String token, String username, String secret) {try {// 设置加密算法Algorithm algorithm = Algorithm.HMAC256(secret);JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();// 效验TOKENDecodedJWT jwt = verifier.verify(token);return true;} catch (Exception exception) {return false;}}public static String sign(String username, String secret) {//现在系统的时间 + 一天Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);//对密码进行加密Algorithm algorithm = Algorithm.HMAC256(secret);// 附带username信息return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);}public static String getUserNameByToken(HttpServletRequest request) {String token = request.getHeader("token");DecodedJWT jwt = JWT.decode(token);return jwt.getClaim("username").asString();}}
具体代码,可以加入我的企鹅君羊:、暗号67
也可以看我制作的视频教程:代码教程
这里代码的则是用户的密码。
我们先通过向/login接口发送我们的账号密码,得到Jwt根据我们的账号密码生成的token。
{"code": "200","message": "认证成功","token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzIzMTg1NjksInVzZXJuYW1lIjoiYWRtaW4ifQ.c_m3z11UOcFcS_hZN9KNlidzZ2j6y_Ugkb9awHQ3FGY"
}
这里认证成功后,服务器如果不经过我们其他的存储操作,是不会对生成的token进行持久化或其他控制的,所以一旦签发出去,这个token串就会变成无状态了。
接着,我们访问一下我们的api测试接口,因为我们在里配置了全局拦截器,且重写了类,除了/login路径,其他全路径都需要请求的(请求头)中带有token字段,且需验证token成功后才会允许访问,否则进行拦截。
4.管理的状态
要做到管理的状态,我们可以通过把token存储到Redis数据库中,通过设置key的过期时间,就可以做到对Jwt的过期操作,同时也能够对Token进行续签,失效等等操作。这部分先不去仔细探究,有个思路就可以了。具体的编码实现我相信也不难。文章来源乐字节
具体代码,可以加入我的企鹅君羊:、暗号67
也可以看我制作的视频教程:代码教程