本文主要是介绍JWT介绍及其基本使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
JWT介绍及其基本使用
官网:https://jwt.io/
什么是JWT
全称:JSON Web Token(JSON Web令牌)
一个开放标准(RFC 7519) ,它定义了一种紧凑和自包含的方式, 用于作为 JSON 对象在各方之间安全地传输信息。此信息可以进行验证和信任,因为它是经过数字签名的。JWT 可以使用机密(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名。
虽然可以对 JWT 进行加密,以便在各方之间提供保密性,但是我们将关注已签名的Token。签名Token可以验证其中包含的声明的完整性,而加密Token可以向其他方隐藏这些声明。当使用公钥/私钥对对令牌进行签名时,该签名还证明只有持有私钥的一方才是对其进行签名的一方( 签名技术是保证传输的信息不被篡改,并不能保证信息传输的安全 )。
JWT有什么作用
JWT最常见的场景就是授权认证,一旦用户登录之后,后续的每个请求都将包含JWT。系统在每次处理用户请求之前都要先通过JWT安全校验,通过之后再去进行处理。
解决了使用session的跨域问题
JWT的结构
JWT由三部分组成,他们分别为:
- Header:包含了算法名称,token名称
- Payload:包含注册声明、公共声明、私有声明
- Signature:由加密后的Header和Payload再次进行二次加密构成
在请求当中的出现形式为Header.Payload.Signature,如下:
JWT的实际应用
JWT依赖引入
<!--JWT(Json Web Token)登录支持-->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>
JWT工具类
package com.liangtl.utils;import com.liangtl.dao.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Component
@ConfigurationProperties("jwt.data")
public class JwtTokenUtil {private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);private static final String CLAIM_KEY_USERNAME = "sub";private static final String CLAIM_KEY_CREATED = "created";private String secret = "jwt-token-secret";private Long expiration = 604800L;private String tokenHead = "Bearer";/*** 根据负责生成JWT的token*/private String generateToken(Map<String, Object> claims) {return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate()).signWith(SignatureAlgorithm.HS256, secret).compact();}/*** 从token中获取JWT中的负载*/private Claims getClaimsFromToken(String token) {Claims claims = null;try {claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();} catch (Exception e) {LOGGER.error("JWT格式验证失败:{}", token);}return claims;}/*** 生成token的过期时间*/private Date generateExpirationDate() {return new Date(System.currentTimeMillis() + expiration * 1000);}/*** 从token中获取登录用户名*/public String getUserNameFromToken(String token) {String username;try {Claims claims = getClaimsFromToken(token);username = claims.getSubject();} catch (Exception e) {username = null;}return username;}/*** 验证token是否还有效** @param token 客户端传入的token* @param User 从数据库中查询出来的用户信息*/public boolean validateToken(String token, User User) {String username = getUserNameFromToken(token);return username.equals(User.getUserName()) && !isTokenExpired(token);}/*** 判断token是否已经失效*/private boolean isTokenExpired(String token) {Date expiredDate = getExpiredDateFromToken(token);return expiredDate.before(new Date());}/*** 从token中获取过期时间*/private Date getExpiredDateFromToken(String token) {Claims claims = getClaimsFromToken(token);return claims.getExpiration();}/*** 根据用户信息生成token*/public String generateToken(User User) {Map<String, Object> claims = new HashMap<>();claims.put(CLAIM_KEY_USERNAME, User.getUserName());claims.put(CLAIM_KEY_CREATED, new Date());return generateToken(claims);}/*** 当原来的token没过期时是可以刷新的** @param oldToken 带tokenHead的token*/public String refreshHeadToken(String oldToken) {if (StringUtils.isEmpty(oldToken)) {return null;}String token = oldToken.substring(tokenHead.length());if (StringUtils.isEmpty(token)) {return null;}//token校验不通过Claims claims = getClaimsFromToken(token);if (claims == null) {return null;}//如果token已经过期,不支持刷新if (isTokenExpired(token)) {return null;}//如果token在30分钟之内刚刷新过,返回原tokenif (tokenRefreshJustBefore(token, 30 * 60 * 1000)) {return token;} else {claims.put(CLAIM_KEY_CREATED, new Date());return generateToken(claims);}}/*** 判断token在指定时间内是否刚刚刷新过** @param token 原token* @param time 指定时间(秒)*/private boolean tokenRefreshJustBefore(String token, int time) {Claims claims = getClaimsFromToken(token);Date created = claims.get(CLAIM_KEY_CREATED, Date.class);Date refreshDate = new Date();//刷新时间在创建时间的指定时间内if (refreshDate.after(new Date(System.currentTimeMillis() - time)) && refreshDate.before(new Date())) {return true;}return false;}
}
接口定义
Controller
package com.liangtl.api;import com.liangtl.dao.User;
import com.liangtl.framework.common.ResponseData;
import com.liangtl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/system")
public class LoginController {@Autowiredprivate UserService userService;@PostMapping("/login")public ResponseData login(@RequestBody User user) {return userService.login(user);}
}
Service
package com.liangtl.service.impl;import com.liangtl.dao.User;
import com.liangtl.framework.common.ErrorCode;
import com.liangtl.framework.common.ResponseData;
import com.liangtl.framework.exception.TokenException;
import com.liangtl.service.UserService;
import com.liangtl.utils.JwtTokenUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Overridepublic ResponseData login(User user) {if (user == null || StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) {throw new TokenException(ErrorCode.USER_ERROR_CODE, ErrorCode.USER_ERROR_MSG);}if ("admin".equals(user.getUserName()) && "123456".equals(user.getPassword())) {String token = jwtTokenUtil.generateToken(user);return ResponseData.success(token);}return ResponseData.failure();}
}
api测试结果
(tips:以上代码不完整,报错并非代码逻辑有问题而是有缺失类,如果完全copy的话需要适当调整使用)
后续相关思路
- 接口请求验证:在拦截器中先校验token来验证用户是否有权限访问接口
- 自动续期:考虑将token放在redis中并且设置过期时间,这样每次发起请求的时候可以设置一个拦截器刷新redis的过期时间以实现自动续期的效果
。。。
这篇关于JWT介绍及其基本使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!