Redis優(yōu)化token校驗主動失效的實現(xiàn)方案
場景:
在普通的token頒發(fā)和校驗中 當用戶發(fā)現(xiàn)自己賬號和密碼被暴露了時修改了登錄密碼后舊的token仍然可以通過系統(tǒng)校驗直至token到達失效時間,這肯定是不安全的,所以系統(tǒng)需要token主動失效的一種能力
解決方案:
我們可以使用redis來實現(xiàn)redis主動失效的的功能
需求實現(xiàn)邏輯:
在每次用戶登錄后頒發(fā)token的同時往redis數(shù)據(jù)庫中存儲一份頒發(fā)給用戶的token
每次每次用戶請求時除了解析token外還需要查詢redis中是否有當前token有則校驗通過,沒有則校驗失敗
每次用戶修改密碼后刪除redis中當前用所攜帶的token,從而使舊token無法通過token校驗
代碼實現(xiàn)
pom.xml中添加redis坐標
<!--redis坐標--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
配置文件application.yml中配置redis相關(guān)信息
spring: data: redis: host: redis所在的ip,本機的redis服務(wù)填localhost port: redis服務(wù)端口,默認6379 password: redis中設(shè)置的密碼,沒有就不需要
頒發(fā)token時往redis中存儲token
//UserController中添加私有屬性stringRedisTemplate并實例化 @Autowired private StringRedisTemplate stringRedisTemplate; //登錄接口中把token存到redis 過期時間3小時 stringRedisTemplate.opsForValue().set(token,token,3, TimeUnit.HOURS);
登錄時校驗是否redis中有當前token
package org.example.intercopters; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.example.utils.JwtUtil; import org.example.utils.ThreadLocalUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import java.util.Map; @Component //注冊攔截器 將其放入ioc容器中 public class LoginInterceptor implements HandlerInterceptor { @Autowired private StringRedisTemplate redisTemplate; //創(chuàng)建登錄身份校驗攔截器 @Override //請求開始前觸發(fā)的攔截方法 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //令牌驗證 String token = request.getHeader("Authorization"); //去除token的前綴標記"Bearer " var newToken = token.contains("Bearer ")?token.substring("Bearer ".length()):token; try { **//從redis獲取相同的token String redisToken = redisTemplate.opsForValue().get(newToken); if(redisToken == null){ //redis失效 throw new RuntimeException("token失效"); }** Map<String, Object> claims = JwtUtil.parseToken(token); //把用戶信息存儲到ThreadLocal中,tomcat會在每次接口請求時創(chuàng)建一個線程 而ThreadLocal中存儲的數(shù)據(jù)是線程安全的 ThreadLocalUtil.set(claims); //放行 return true; } catch (Exception e) { //設(shè)置響應(yīng)狀態(tài)碼 response.setStatus(401); //設(shè)置響應(yīng)字符集和響應(yīng)內(nèi)容 response.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); String errorMessage = "未登錄"; response.getWriter().write("{\"error\": \"" + errorMessage + "\"}"); //不放行 return false; } } @Override //請求完成后觸發(fā)的攔截方法 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { //清空ThreadLocal中的數(shù)據(jù) ThreadLocalUtil.remove(); } }
修改密碼后刪除redis中的token
//修改密碼接口中刪除redis中對應(yīng)的token ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); operations.getOperations().delete(newToken);
在本地開發(fā)中如果使用本地redis每次開發(fā)前還得去啟動本地redis,就很麻煩,如果你有一個云服務(wù)器就可以本地連云服務(wù)器redis就不用每次去啟動redis了,下面是實現(xiàn)教程:
前提是讀者已經(jīng)安裝好了Redis。
因為剛學(xué)Linux不久,敲過一些命令,算不上熟悉,對網(wǎng)絡(luò)防火墻那一塊也不熟悉。
因此雖然在項目中已經(jīng)開啟了Redis服務(wù),也寫好配置類:
#redis spring.redis.host=x.x.x.x spring.redis.port=6379 spring.redis.database= 0 spring.redis.timeout=1800000
```java @Configuration @EnableCaching // 開啟緩存處理,使用redis,將字典的數(shù)據(jù)緩存到其中! public class RedisConfig { private RedisCacheManager build; /** * 自定義key規(guī)則 * * @return */ @Bean public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); } }; } /** * 設(shè)置RedisTemplate規(guī)則 * * @param redisConnectionFactory * @return */ @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解決查詢緩存轉(zhuǎn)換異常的問題 ObjectMapper om = new ObjectMapper(); // 指定要序列化的域,field,get和set,以及修飾符范圍,ANY是都有包括private和public om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // 指定序列化輸入的類型,類必須是非final修飾的,final修飾的類,比如String,Integer等會跑出異常 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); //序列號key value redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; } /** * 設(shè)置CacheManager緩存規(guī)則 * * @param factory * @return */ @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //解決查詢緩存轉(zhuǎn)換異常的問題 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // 配置序列化(解決亂碼的問題),過期時間600秒 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } }
但還是啟動不了。
在這里梳理一下:
- 你先要去云服務(wù)器(這里以騰訊云為例)到防火墻里面添加端口,我就以默認的6379為例。
- 然后去你的linux上面查看你的防護墻有沒有打開和防火墻有沒有添加對應(yīng)的端口號。命令如下
#查看linux上面防火墻的狀態(tài) systemctl status firewalld.service #如果是running的,就需要關(guān)了 systemctl stop firewalld.service [root@VM-4-12-centos ~]# firewall-cmd --query-port=6379/tcp#查詢 no [root@VM-4-12-centos ~]# firewall-cmd --add-port=6379/tcp#添加 success
- 最后你還需要去你的redis.conf文件查看你的保護有沒有打開,默認的端口號有沒有注釋掉:
做以下的修改,你就可以通過你的項目連接到Redis。
# bind 127.0.0.1 ::1 protected-mode no port 6379
到此這篇關(guān)于Redis優(yōu)化token校驗主動失效的實現(xiàn)方案的文章就介紹到這了,更多相關(guān)Redis token校驗主動失效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
redis cluster集群模式下實現(xiàn)批量可重入鎖
本文主要介紹了使用redis cluster集群版所遇到的問題解決方案及redis可重入鎖是否會有死鎖的問題等,具有一定的參考價值,感興趣的可以了解一下2024-02-02讓Redis在你的系統(tǒng)中發(fā)揮更大作用的幾點建議
Redis在很多方面與其他數(shù)據(jù)庫解決方案不同:它使用內(nèi)存提供主存儲支持,而僅使用硬盤做持久性的存儲;它的數(shù)據(jù)模型非常獨特,用的是單線程。另一個大區(qū)別在于,你可以在開發(fā)環(huán)境中使用Redis的功能,但卻不需要轉(zhuǎn)到Redis2014-06-06