东莞做网站设计制作,凡科网建站入门教程,邯郸网站建设兼职,好用管理软件公司思路
jwttoken不设置过期时间
redis管理过期时间#xff0c;并且续签
redis中keylogin:userId, valuejwtUser
再次访问时#xff0c;解析token中userId#xff0c;并且根据过期时间自动续签JWT 实现登录认证 Token 自动续期方案 pom文件配置
!--Redis--并且续签
redis中keylogin:userId, valuejwtUser
再次访问时解析token中userId并且根据过期时间自动续签JWT 实现登录认证 Token 自动续期方案 pom文件配置
!--Redis--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdversion2.3.5.RELEASE/version
/dependency!--JWT--
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-api/artifactIdversion0.10.7/version
/dependency
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-impl/artifactIdversion0.10.7/versionscoperuntime/scope
/dependency
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-jackson/artifactIdversion0.10.7/versionscoperuntime/scope
/dependency!--spring security--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependencyapplication.yml配置
spring:redis:database: 0host: redis_ipport: redis_portconnect-timeout: 30000lettuce:pool:# 最大阻塞等待时间负数表示没有限制max-wait: -1# 连接池中的最大空闲连接max-idle: 10# 连接池中的最小空闲连接min-idle: 0# 连接池中最大连接数负数表示没有限制max-active: 50password: redis_passwordservice层logon方法
Override
public Object login(LoginDTO loginDTO) {UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword());Authentication authenticate;try {// 对登录用户进行认证authenticate authenticationManager.authenticate(authenticationToken);} catch (AuthenticationException ex) {throw new ServiceException(ex.getMessage());}if (Objects.isNull(authenticate)) {throw new ServiceException(登录失败);}JWTUser jwtUser (JWTUser)authenticate.getPrincipal();if(jwtUser.getUserDO().getDeleteFlag()!0){throw new ServiceException(用户已被停用,如需开启,请联系管理员);}LoginSuccessDTO loginSuccessDTO new LoginSuccessDTO();loginSuccessDTO.setUserId(jwtUser.getId());loginSuccessDTO.setUsername(jwtUser.getUsername());loginSuccessDTO.setNickName(jwtUser.getUserDO().getNickName());loginSuccessDTO.setUserRealName(jwtUser.getUserDO().getUserRealName());loginSuccessDTO.setDepartment(jwtUser.getUserDO().getDepartment());MapString, Object userInfo DataConvert.BeanPropertyNameTraverse(loginSuccessDTO);String token JwtUtils.generateToken(userInfo);String userId jwtUser.getId();Date expirationTime JwtUtils.getExpirationTime();redisUtils.set(login: userId, jwtUser, AuthConstant.EXPIRATION_TIME_IN_SECOND, TimeUnit.SECONDS);loginSuccessDTO.setLoginTime(new Date());loginSuccessDTO.setExpireTime(expirationTime);loginSuccessDTO.setToken(token);return loginSuccessDTO;
}Override
public Object logout() {/** 获取SecurityContextHolder中的userId。* 注销操作也是需要携带token的spring security已经在内部对token进行验证* 并能够从redis取出用户信息*/UsernamePasswordAuthenticationToken authentication (UsernamePasswordAuthenticationToken)SecurityContextHolder.getContext().getAuthentication();JWTUser jwtUser (JWTUser)authentication.getPrincipal();String userId jwtUser.getId();redisUtils.del(login:userId);return 注销成功;
}token工具类
package cma.sxqxgxw.utils.jwt;import cma.sxqxgxw.auth.constant.AuthConstant;
import cma.sxqxgxw.common.exception.ServiceException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;import javax.crypto.SecretKey;
import java.util.Map;
import java.util.Date;public class JwtUtils {private static final String SIGNATURE !QWf5g%T^Uf3r32523534634634654754ygrgfdjjuhfdsdf6;/*** 计算token的过期时间** return 过期时间*/public static Date getExpirationTime() {return new Date(System.currentTimeMillis() AuthConstant.EXPIRATION_TIME_IN_SECOND * 1000);}/*** 生成Token* param claims 用户信息* return token*/public static String generateToken(MapString, Object claims) {Date createdTime new Date();//Date expirationTime getExpirationTime();byte[] keyBytes SIGNATURE.getBytes();SecretKey key Keys.hmacShaKeyFor(keyBytes);return Jwts.builder().setClaims(claims).setIssuedAt(createdTime)//.setExpiration(expirationTime).signWith(key, SignatureAlgorithm.HS256).compact();}/*** 从token中获取claim** param token token* return claim*/public static Claims getClaimsFromToken(String token) {try {return Jwts.parser().setSigningKey(SIGNATURE.getBytes()).parseClaimsJws(token).getBody();} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | IllegalArgumentException e) {throw new ServiceException(Token invalided.);}}/*** 获取token的过期时间** param token token* return 过期时间*/public static Date getExpirationDateFromToken(String token) {return getClaimsFromToken(token).getExpiration();}/*** 判断token是否过期** param token token* return 已过期返回true未过期返回false*/public static Boolean isTokenExpired(String token) {Date expiration getExpirationDateFromToken(token);return expiration.before(new Date());}/*** 判断token是否非法** param token token* return 未过期返回true否则返回false*/public static Boolean validateToken(String token) {System.out.println(token valid);return !isTokenExpired(token);}
}redis工具类
Slf4j
Component
public class RedisUtils {private final RedisTemplateString, Object redisTemplate;Autowiredpublic RedisUtils(RedisTemplateString, Object redisTemplate) {this.redisTemplate redisTemplate;}/*** 指定缓存失效时间** param key 键* param time 时间(秒)* return*/public boolean expire(String key, long time) {try {if (time 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** param key 键 不能为null* return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** param key 键* return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {log.info(Redis连接失败可能是Redis服务未启动。 e.getMessage());return false;}}/*** 删除缓存** param key 可以传一个值 或多个*/SuppressWarnings(unchecked)public boolean del(String... key) {if (key ! null key.length 0) {if (key.length 1) {Boolean delete redisTemplate.delete(key[0]);return delete;} else {Long delete redisTemplate.delete((CollectionString) CollectionUtils.arrayToList(key));return true;}}return false;}//String/*** 普通缓存获取** param key 键* return 值*/public Object get(String key) {return key null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** param key 键* param value 值* return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {log.info(插入操作时Redis连接失败可能是Redis服务未启动。 e.getMessage());return false;}}/*** 普通缓存放入并设置时间** param key 键* param value 值* param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* return true成功 false 失败*/public boolean set(String key, Object value, long time, TimeUnit timeUnit) {try {if (time 0) {redisTemplate.opsForValue().set(key, value, time, timeUnit);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}
}jwt过滤器 生成的token中不带有过期时间token的过期时间由redis进行管理 当redis过期时间小于10分钟时redis过期时间续签30分钟
Component
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {private final RedisUtils redisUtils;Autowiredpublic JWTAuthorizationFilter(AuthenticationManager authenticationManager, RedisUtils redisUtils) {super(authenticationManager);this.redisUtils redisUtils;}Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {String token request.getHeader(token);if (!StringUtils.hasText(token)) {request.setAttribute(errorMessage, Token缺失请检查请求Header);chain.doFilter(request, response);return;}try {Claims claims JwtUtils.getClaimsFromToken(token);String userId (String) claims.get(userId);boolean hasKey redisUtils.hasKey(login: userId);if (!hasKey) {request.setAttribute(errorMessage, Token无效或已经过期);throw new ServiceException(Token无效或已经过期);}// 令牌续签long expire redisUtils.getExpire(login: userId);System.out.println(剩余时间 expire);if (expire AuthConstant.RENEWAL_TIME_IN_SECOND) {redisUtils.expire(login: userId, AuthConstant.EXPIRATION_TIME_IN_SECOND);System.out.println(令牌续签 AuthConstant.EXPIRATION_TIME_IN_SECOND 秒);}JWTUser jwtUser (JWTUser) redisUtils.get(login: userId);UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());// 设置认证状态SecurityContextHolder.getContext().setAuthentication(authenticationToken);chain.doFilter(request, response);} catch (ServiceException e) {request.setAttribute(errorMessage, e.getMessage());chain.doFilter(request, response);}}
}过期时间常量
public class AuthConstant {/*** 令牌有效时长*/public static final Long EXPIRATION_TIME_IN_SECOND 60*30L;/*** 过期时间剩余多少时间时续签*/public static final Long RENEWAL_TIME_IN_SECOND 60*10L;
}Redis配置类
package cma.sxqxgxw.common.config.redis;import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Redis配置* 主要指定自定义序列化器,避免序列化反序列化失败*/
Configuration
public class RedisConfig {Beanpublic RedisTemplateString, Object redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplateString, Object redisTemplate new RedisTemplate();// 参照StringRedisTemplate内部实现指定序列化器redisTemplate.setConnectionFactory(redisConnectionFactory);// 设置key的序列化器redisTemplate.setKeySerializer(keySerializer());redisTemplate.setHashKeySerializer(keySerializer());// 设置value的序列化器// 解决autoType is not support.xx.xx的问题String[] acceptNames {org.springframework.security.core.authority.SimpleGrantedAuthority};GenericFastJsonRedisSerializer serializer new GenericFastJsonRedisSerializer(acceptNames);redisTemplate.setValueSerializer(serializer);redisTemplate.setHashValueSerializer(serializer);return redisTemplate;}private RedisSerializerString keySerializer(){return new StringRedisSerializer();}//使用Jackson序列化器private RedisSerializerObject valueSerializer(){return new GenericJackson2JsonRedisSerializer();}
}Jwt拦截器
package cma.sxqxgxw.common.interceptor;import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;Component
public class JwtInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {return true;}}