依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<!--工具类依赖-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
配置文件
#cookie的名字
jwt.cookieName=mange_token
jwt.pubKeyPath=src\\main\\resources\\rsa\\rsa.pub
jwt.priKeyPath=src\\main\\resources\\rsa\\rsa.pri
jwt.secret=hfut@laboratory_11.4
#生产环境设置为180分钟
jwt.expireMinutes=18000
读取配置文件中jwt信息
- JwtConfig
/**
* 读取配置文件的jwt信息
*/
@Configuration
@ConfigurationProperties(prefix = "jwt")
@Data
public class JwtConfig {
private String cookieName;
private String pubKeyPath;
private String priKeyPath;
private String secret;
private Integer expireMinutes;
/**
* 会在解析私钥报错,
* 所以要使用时还是:RsaUtils.getPrivateKey(jwtConfig.getPriKeyPath())
*
* private PublicKey publicKey;
* private PrivateKey privateKey;
*
* @PostConstruct
* public void createRsaKey() throws Exception {
* publicKey = RsaUtils.getPublicKey(pubKeyPath);
* privateKey = RsaUtils.getPrivateKey(pubKeyPath);
* }
*/
}
实体类
- 五张表(建好后用mybatis-plus逆向生成)
- user: id、username(必须为username)、password、…其他字段
- role: id、role_name、…其他字段
- permession: id、permission_name、…其他字段
- user_role: id、user_id、role_id
- role_permession: id、role_id、permission_id
需要改变的类
- User:需要继承UserDetails,因为Spring Security保存的对象是UserDetails的子类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
//非数据库字段 不然每次jwt的拦截器每次都要再去数据库查对应的权限 影响性能
private List<Permission> perms;
//数据库中其他字段
private Integer status;
//必须加JsonIgnore字段 不然在json序列化和反序列化的时候会带上下面这些函数的返回值
@JsonIgnore
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return perms;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isEnabled() {
return true;
}
}
- Permission:继承GrantedAuthority,因为UserDetails的实现类要返回GrantedAuthority的实现类列表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission implements GrantedAuthority {
private Integer id;
private String permissionName;
//描述(数据库中该类其他字段)
private String permissionDesc;
@JsonIgnore
@Override
public String getAuthority() {
return permissionName;
}
}
- 其他三个实体类无需改变 直接mybatis-plus逆向生成
jwt的Token中存储的对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payload<T> {
//token中载荷的id 在解析token的时候可以获得
private String id;
//这里指的前面的User实体类
private T userInfo;
//token过期的时间 在解析token的时候可以获得
private Date expiration;
}
工具类
jwt包下的
- JwtUtils(如果UserInfo有字段改变 需要修改这个工具类)
public class JwtUtils {
private static final String JWT_PAYLOAD_USER_KEY = "user";
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位分钟
* @return JWT
*/
public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.serialize(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusMinutes(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位秒
* @return JWT
*/
public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.serialize(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusSeconds(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
/**
* 公钥解析token
*
* @param token 用户请求中的token
* @param publicKey 公钥
* @return Jws<Claims>
*/
private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
}
private static String createJTI() {
return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
}
/**
* 获取token中的用户信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
*/
public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload<T> claims = new Payload<>();
claims.setId(body.getId());
claims.setUserInfo(JsonUtils.parse(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
claims.setExpiration(body.getExpiration());
return claims;
}
/**
* 获取token中的载荷信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @return 用户信息
*/
public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) {
Jws<Claims> claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload<T> claims = new Payload<>();
claims.setId(body.getId());
claims.setExpiration(body.getExpiration());
return claims;
}
}
- RsaUtils
public class RsaUtils {
/**
* 从文件中读取公钥
*
* @param filename 公钥保存路径,相对于classpath
* @return 公钥对象
* @throws Exception
*/
public static PublicKey getPublicKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPublicKey(bytes);
}
/**
* 从文件中读取密钥
*
* @param filename 私钥保存路径,相对于classpath
* @return 私钥对象
* @throws Exception
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPrivateKey(bytes);
}
/**
* 获取公钥
*
* @param bytes 公钥的字节形式
* @return
* @throws Exception
*/
public static PublicKey getPublicKey(byte[] bytes) throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
}
/**
* 获取密钥
*
* @param bytes 私钥的字节形式
* @return
* @throws Exception
*/
public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(spec);
}
/**
* 根据密文,生存rsa公钥和私钥,并写入指定文件
*
* @param publicKeyFilename 公钥文件路径
* @param privateKeyFilename 私钥文件路径
* @param secret 生成密钥的密文
* @throws IOException
* @throws NoSuchAlgorithmException
*/
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = new SecureRandom(secret.getBytes());
keyPairGenerator.initialize(1024, secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// 获取公钥并写出
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
writeFile(publicKeyFilename, publicKeyBytes);
// 获取私钥并写出
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
writeFile(privateKeyFilename, privateKeyBytes);
}
private static byte[] readFile(String fileName) throws Exception {
return Files.readAllBytes(new File(fileName).toPath());
}
private static void writeFile(String destPath, byte[] bytes) throws IOException {
File dest = new File(destPath);
if (!dest.exists()) {
dest.createNewFile();
}
Files.write(dest.toPath(), bytes);
}
}
- JwtTokenUtils
@Slf4j
public class JwtTokenUtils {
/**
* 给cookie设置jwtToken
*
*/
public static void setJwtToken(HttpServletRequest request, HttpServletResponse response, Object userInfo) throws Exception {
JwtConfig jwtConfig=BeanUtils.getBean(JwtConfig.class,request);
//新生成token
String token=JwtUtils.generateTokenExpireInMinutes(userInfo,RsaUtils.getPrivateKey(jwtConfig.getPriKeyPath()),jwtConfig.getExpireMinutes());
CookieUtils.setCookie(request,response, jwtConfig.getCookieName(),token,null,null);
}
/**
* 从request中直接读取cookie中的信息 然后重新设置到cookie中
*/
public static void setJwtTokenByInfoFormCookie(HttpServletRequest request, HttpServletResponse response) throws Exception {
Object userInfo= getUserInfoFromToken(request,Object.class);
if(userInfo==null){
log.info(JwtTokenUtils.class.getName()+"setJwtTokenByInfoFormCookie:error");
return;
}
setJwtToken(request,response,userInfo);
}
/**
* 从HttpServletRequest中获PayLoad
*/
public static<T> Payload<T> getPayLoadFromToken(HttpServletRequest request,Class<T> userInfoClass){
JwtConfig jwtConfig = BeanUtils.getBean(JwtConfig.class, request);
String token= CookieUtils.getCookieValue(request,jwtConfig.getCookieName());
Payload payload=null;
try {
PublicKey publicKey = RsaUtils.getPublicKey(jwtConfig.getPubKeyPath());
payload= JwtUtils.getInfoFromToken(token,publicKey,userInfoClass);
return payload;
} catch (Exception e) {
log.info(JwtTokenUtils.class.getName()+"getPayLoadFromToken:error");
return null;
}
}
/**
* 从HttpServletRequest中获取userInfo
*/
public static<T> T getUserInfoFromToken(HttpServletRequest request,Class<T> userInfoClass){
try {
return getPayLoadFromToken(request,userInfoClass).getUserInfo();
}catch (Exception e) {
log.info(JwtTokenUtils.class.getName()+"getUserInfoFromToken:error");
return null;
}
}
}
CookieUtils
/**
*
* Cookie 工具类
*
*/
public final class CookieUtils {
static final Logger logger = LoggerFactory.getLogger(CookieUtils.class);
/**
* 得到Cookie的值, 不编码
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName) {
return getCookieValue(request, cookieName, false);
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null){
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
logger.error("Cookie Decode Error.", e);
}
return retValue;
}
/**
* 得到Cookie的值,
*
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null){
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
logger.error("Cookie Decode Error.", e);
}
return retValue;
}
/**
* 生成cookie,并指定编码
* @param request 请求
* @param response 响应
* @param cookieName name
* @param cookieValue value
* @param encodeString 编码
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, String encodeString) {
setCookie(request,response,cookieName,cookieValue,null,encodeString, null);
}
/**
* 生成cookie,并指定生存时间
* @param request 请求
* @param response 响应
* @param cookieName name
* @param cookieValue value
* @param cookieMaxAge 生存时间
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge) {
setCookie(request,response,cookieName,cookieValue,cookieMaxAge,null, null);
}
/**
* 设置cookie,不指定httpOnly属性
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString) {
setCookie(request,response,cookieName,cookieValue,cookieMaxAge,encodeString, null);
}
/**
* 设置Cookie的值,并使其在指定时间内生效
*
* @param cookieMaxAge
* cookie生效的最大秒数
*/
public static final void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue, Integer cookieMaxAge, String encodeString, Boolean httpOnly) {
try {
if(StringUtils.isBlank(encodeString)) {
encodeString = "utf-8";
}
if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxAge != null && cookieMaxAge>0)
cookie.setMaxAge(cookieMaxAge);
if (null != request)// 设置域名的cookie
cookie.setDomain(getDomainName(request));
cookie.setPath("/");
if(httpOnly != null) {
cookie.setHttpOnly(httpOnly);
}
response.addCookie(cookie);
} catch (Exception e) {
logger.error("Cookie Encode Error.", e);
}
}
/**
* 得到cookie的域名
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
try{
if(request.getMethod().toUpperCase().equals("GET")){
domainName=request.getHeader("Referer").split("/")[2];
}else {
domainName=request.getHeader("Origin").split("//")[1];
}
}catch (NullPointerException e){
domainName=request.getHeader("Host").split(":")[0];
if(StringUtils.isBlank(domainName)){
throw new RuntimeException("无法获得设置cookie的domain");
}
}
return domainName;
}
}
QueryWapperUtils
/**
* mybatis-plus生成查询条件的工具类
*/
public class QueryWapperUtils {
public static QueryWrapper getInWapper(String colum,Object... s){
QueryWrapper queryWrapper=new QueryWrapper();
queryWrapper.in(colum,s);
return queryWrapper;
}
public static QueryWrapper getLikeWapper(String colum,Object s){
QueryWrapper queryWrapper=new QueryWrapper();
queryWrapper.like(colum,s);
return queryWrapper;
}
}
其他几个工具类
- 在我的博客:https://blog.csdn.net/weixin_43934607/article/details/102301364