springboot获取payload_SpringBoot集成JWT实现token验证什么是JWT
Json web token (JWT), 是为了在⽹络应⽤环境间传递声明⽽执⾏的⼀种基于JSON的开放标准((RFC 7519).定义了⼀种简洁的,⾃包含
的⽅法⽤于通信双⽅之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使⽤HMAC算法或者是RSA的公私秘钥对进⾏签名。
JWT请求流程
image.png
1. ⽤户使⽤账号和⾯发出post请求;
2. 服务器使⽤私钥创建⼀个jwt;
3. 服务器返回这个jwt给浏览器;
4. 浏览器将该jwt串在请求头中像服务器发送请求;
5. 服务器验证该jwt;
6. 返回响应的资源给浏览器。
JWT的主要应⽤场景
⾝份认证在这种场景下,⼀旦⽤户完成了登陆,在接下来的每个请求中包含JWT,可以⽤来验证⽤户⾝份以及对路由,服务和资源的访问权
限进⾏验证。由于它的开销⾮常⼩,可以轻松的在不同域名的系统中传递,所有⽬前在单点登录(SSO)中⽐较⼴泛的使⽤了该技术。 信息交
换在通信的双⽅之间使⽤JWT对数据进⾏编码是⼀种⾮常安全的⽅式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过
伪造的。
优点
1.简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量⼩,传输速度也很快
2.⾃包含(Self-contained):负载中包含了所有⽤户所需要的信息,避免了多次查询数据库
3.因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语⾔的,原则上任何web形式都⽀持。
4.不需要在服务端保存会话信息,特别适⽤于分布式微服务。
`
JWT的结构
JWT是由三段信息构成的,将这三段信息⽂本⽤.链接⼀起就构成了JWT字符串。
就像这样:
JWT包含了三部分:
Header 头部(标题包含了令牌的元数据,并且包含签名和/或加密算法的类型)
Payload 负载 (类似于飞机上承载的物品)
Signature 签名/签证
Header
JWT的头部承载两部分信息:token类型和采⽤的加密算法。
{
"alg": "HS256",
"typ": "JWT"
}
声明类型:这⾥是jwt
声明加密的算法:通常直接使⽤ HMAC SHA256
加密算法是单向函数散列算法,常见的有MD5、SHA、HAMC。
MD5(message-digest algorithm 5) (信息-摘要算法)缩写,⼴泛⽤于加密和解密技术,常⽤于⽂件校验。校验?不管⽂件多⼤,经过MD5后都能⽣成唯⼀的MD5值
SHA (Secure Hash Algorithm,安全散列算法),数字签名等密码学应⽤中重要的⼯具,安全性⾼于MD5
HMAC (Hash Message Authentication Code),散列消息鉴别码,基于密钥的Hash算法的认证协议。⽤公开函数和密钥产⽣⼀个固定长度的值作为认证标识,⽤这个标识鉴别消息的完整性。常⽤于接⼝签名验证
Payload
载荷就是存放有效信息的地⽅。
有效信息包含三个部分
1.标准中注册的声明
2.公共的声明
3.私有的声明
标准中注册的声明 (建议但不强制使⽤) :
iss: jwt签发者
sub: ⾯向的⽤户(jwt所⾯向的⽤户)
aud: 接收jwt的⼀⽅
exp: 过期时间戳(jwt的过期时间,这个过期时间必须要⼤于签发时间)
nbf: 定义在什么时间之前,该jwt都是不可⽤的.
iat: jwt的签发时间
jti: jwt的唯⼀⾝份标识,主要⽤来作为⼀次性token,从⽽回避重放攻击。
公共的声明 :
公共的声明可以添加任何的信息,⼀般添加⽤户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
私有的声明 :
私有声明是提供者和消费者所共同定义的声明,⼀般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明⽂信息。
Signature
jwt的第三部分是⼀个签证信息,这个签证信息由三部分组成:
header (base64后的)
payload (base64后的)
secret
这个部分需要base64加密后的header和base64加密后的payload使⽤.连接组成的字符串,然后通过header中声明的加密⽅式进⾏加盐secret组合加密,然后就构成了jwt的第三部分。
密钥secret是保存在服务端的,服务端会根据这个密钥进⾏⽣成token和进⾏验证,所以需要保护好。
下⾯来进⾏SpringBoot和JWT的集成
引⼊JWT依赖,由于是基于Java,所以需要的是java-jwt
com.auth0
java-jwt
3.4.0
需要⾃定义两个注解
⽤来跳过验证的PassToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
需要登录才能进⾏操作的注解UserLoginToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
spring ioc注解@Target:注解的作⽤⽬标
@Target(ElementType.TYPE)——接⼝、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——⽅法
@Target(ElementType.PARAMETER)——⽅法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包
@Retention:注解的保留位置
RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码⽂件中不包含。
RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class⽂件中存在,但JVM将会忽略,运⾏时⽆法获得。
RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运⾏时被JVM或其他使⽤反射机制的代码所读取和使⽤。
@Document:说明该注解将被包含在javadoc中
@Inherited:说明⼦类可以继承⽗类中的该注解
简单⾃定义⼀个实体类User,使⽤lombok简化实体类的编写
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
String Id;
String username;
String password;
}
需要写token的⽣成⽅法
public String getToken(User user) {
String token="";
token= ate().Id())
.sign(Algorithm.Password()));
return token;
}
Algorithm.HMAC256():使⽤HS256⽣成token,密钥则是⽤户的密码,唯⼀密钥的话可以保存在服务端。
withAudience()存⼊需要保存在token的信息,这⾥我把⽤户ID存⼊token中
接下来需要写⼀个去获取token并验证token
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = Header("token");// 从 http 请求头中取出 token
// 如果不是映射到⽅法直接通过
if(!(object instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod)object;
Method Method();
//检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = Annotation(PassToken.class);
if (quired()) {
return true;
}
}
//检查有没有需要⽤户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = Annotation(UserLoginToken.class);
if (quired()) {
// 执⾏认证
if (token == null) {
throw new RuntimeException("⽆token,请重新登录");
}
// 获取 token 中的 user id
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RuntimeException("401");
}
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("⽤户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = quire(Algorithm.Password())).build(); try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("401");
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论