项目背景:
该项目是一个给用户使用的客户端,由于时间比较紧,而且需要考虑到组内开发学习成本所以直接使用的轻量级JWT,并没有使用spring security和OAuth2等;该项目使用技术点:springBoot、spring mvc、mybatis、Spring admin、actuactor、swagger、swaggerdocs等常用技术
因为jwt无法对token进行内部登出以及内部自动化续签(内部登出与内部自动化续签是指前端在不改变token的情况下进行销毁token和延长token有效期),基于此项目在服务内部做了这些处理以及前端vue针对自动化续签的结果进行更替以及继续请求原有的请求
1、首先进行登录:
登录api:
这里使用的是公私钥的方式,当然使用何种加密方式无所谓,自己根据jjwt官网首页做修改都可以
import com.sioo.sioohyclient.common.constants.JWTConstants;
import com.sioo.sioohyclient.common.model.UserInfo;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ObjectUtils;
import org.joda.time.DateTime;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Date;
/**
* @Description: JWT工具类
* @Param:
* @return:
* @Author: fanghuaiming
* @Date:
*/
@Slf4j
public class JWTUtils {
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expireMinutes 过期时间,单位分
* @return
*/
public static String generateToken(UserInfo userInfo, PrivateKey privateKey,int expireMinutes){
return Jwts.builder()
.claim(JWTConstants.JWT_KWT_USER_NAME,userInfo.getUserName())
.claim(JWTConstants.JWT_KWT_Uid,userInfo.getUid())
.claim(JWTConstants.JWT_KWT_PRICE,userInfo.getPrice())
.claim(JWTConstants.JWT_KWT_SHOWRPT,userInfo.getShowRpt())
.claim(JWTConstants.JWT_KWT_ISNEW,userInfo.getIsNew())
.claim(JWTConstants.JWT_KWT_APIRPT,userInfo.getApiRpt())
.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
.signWith(SignatureAlgorithm.RS256,privateKey)
.compact();
}
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥字节数组
* @param expireMinutes 过期时间,单位秒
* @return
*/
public static String generateToken(UserInfo userInfo,byte[] privateKey,int expireMinutes) throws Exception {
return Jwts.builder()
.claim(JWTConstants.JWT_KWT_USER_NAME,userInfo.getUserName())
.claim(JWTConstants.JWT_KWT_Uid,userInfo.getUid())
.claim(JWTConstants.JWT_KWT_PRICE,userInfo.getPrice())
.claim(JWTConstants.JWT_KWT_SHOWRPT,userInfo.getShowRpt())
.claim(JWTConstants.JWT_KWT_ISNEW,userInfo.getIsNew())
.claim(JWTConstants.JWT_KWT_APIRPT,userInfo.getApiRpt())
.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
.signWith(SignatureAlgorithm.RS256, RsaUtils.getPrivateKey(privateKey))
.compact();
}
/**
* 公钥解析token
*
* @param token 用户请求中的token
* @param publicKey 公钥
* @return
*/
private static Jws<Claims> parserToken(String token, PublicKey publicKey){
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
}
/**
* 公钥解析token
*
* @param token 用户请求中的token
* @param publicKey 公钥字节数组
* @return
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
private static Jws<Claims> parserToken(String token,byte[] publicKey) throws Exception {
return Jwts.parser().setSigningKey(RsaUtils.getPublicKey(publicKey))
.parseClaimsJws(token);
}
/**
* 获取token中的用户信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
*/
public static UserInfo getInfoFromToken(String token,PublicKey publicKey){
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
return new UserInfo(
ObjectUtils.toString(body.get(JWTConstants.JWT_KWT_USER_NAME)),
(Integer)body.get(JWTConstants.JWT_KWT_Uid),
(double)body.get(JWTConstants.JWT_KWT_PRICE),
(Integer)body.get(JWTConstants.JWT_KWT_SHOWRPT),
(Integer)body.get(JWTConstants.JWT_KWT_ISNEW),
(Integer)body.get(JWTConstants.JWT_KWT_APIRPT)
);
}
/**
* 获取token中的用户信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥字节数组
* @return
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
public static UserInfo getInfoFromtoken(String token,byte[] publicKey) throws Exception {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
return new UserInfo(
ObjectUtils.toString(body.get(JWTConstants.JWT_KWT_USER_NAME)),
(Integer)body.get(JWTConstants.JWT_KWT_Uid),
(double)body.get(JWTConstants.JWT_KWT_PRICE),
(Integer)body.get(JWTConstants.JWT_KWT_SHOWRPT),
(Integer)body.get(JWTConstants.JWT_KWT_ISNEW),
(Integer)body.get(JWTConstants.JWT_KWT_APIRPT)
);
}
/**
* @Description: 获取当前token的有效期设置
* @Param:
* @return:
* @Author: fanghuaiming
* @Date: 1:59 PM 2019/7/29
*/
public static Date getExpirationDateFromToken(String token,PublicKey publicKey) {
//获取当前token的body
Claims body = parserToken(token, publicKey).getBody();
//获取当前token的有效期
return body.getExpiration();
}
/**
* @Description: 是否已经过期
* @Param:
* @return:
* @Author: fanghuaiming
* @Date: 2:01 PM 2019/7/29
*/
public static Boolean isTokenExpired(String token, PublicKey publicKey) {
try {
final Date expiration = getExpirationDateFromToken(token,publicKey);
return expiration.before(new Date());
} catch (ExpiredJwtException expiredJwtException) {
log.error("查看token是否已经过期发生异常,异常内容:{}",expiredJwtException.getMessage());
return true;
}
}
}
涉及到的相关方法和配置:
/**
* @Description: jwt使用的constants常量
* @Param:
* @return:
* @Author: fanghuaiming
* @Date:
*/
public abstract class JWTConstants {
/**
* 用户名
*/
public static final String JWT_KWT_USER_NAME = "userName";
/**
* 用户id
*/
public static final String JWT_KWT_Uid = "uid";
/**
* 用户单价
*/
public static final String JWT_KWT_PRICE = "price";
/**
* 用户前台是否显示报告
*/
public static final String JWT_KWT_SHOWRPT= "showRpt";
/**
* 用户是否新用户
*/
public static final String JWT_KWT_ISNEW= "isNew";
/**
* 是否开通接口状态:0为开通,1为不开通
*/
public static final String JWT_KWT_APIRPT= "apiRpt";
}
import com.sioo.sioohyclient.common.utils.RsaUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* @Description: JWT相关配置
* @Param:
* @return:
* @Author: fanghuaiming
* @Date:
*/
@ConfigurationProperties(prefix = "siooclient.jwt")
public class JwtProperties {
/**
* 密钥
*/
private String secret;
/**
* 公钥地址
*/
private String pubKeyPath;
/**
* 私钥地址
*/
private String priKeyPath;
/**
* token过期时间
*/
private int expire;
/**
* 公钥
*/
private PublicKey publicKey;
/**
* 私钥
*/
private PrivateKey privateKey;
/**
* salt
*/
private String salt;
private static final Logger logger = LoggerFactory.getLogger(JwtProperties.class);
@PostConstruct
public void init(){
try {
File pubKey = new File(pubKeyPath);
File priKey = new File(priKeyPath);
if(!pubKey.exists() || !priKey.exists()) {
//生成公钥和私钥
RsaUtils.generateKey(pubKeyPath, priKeyPath, secret);
if(logger.isDebugEnabled()){
logger.debug