Redis優(yōu)化token校驗主動失效的實現方案
場景:
在普通的token頒發(fā)和校驗中 當用戶發(fā)現自己賬號和密碼被暴露了時修改了登錄密碼后舊的token仍然可以通過系統(tǒng)校驗直至token到達失效時間,這肯定是不安全的,所以系統(tǒng)需要token主動失效的一種能力
解決方案:
我們可以使用redis來實現redis主動失效的的功能
需求實現邏輯:
在每次用戶登錄后頒發(fā)token的同時往redis數據庫中存儲一份頒發(fā)給用戶的token
每次每次用戶請求時除了解析token外還需要查詢redis中是否有當前token有則校驗通過,沒有則校驗失敗
每次用戶修改密碼后刪除redis中當前用所攜帶的token,從而使舊token無法通過token校驗
代碼實現
pom.xml中添加redis坐標
<!--redis坐標-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件application.yml中配置redis相關信息
spring:
data:
redis:
host: redis所在的ip,本機的redis服務填localhost
port: redis服務端口,默認6379
password: redis中設置的密碼,沒有就不需要頒發(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中存儲的數據是線程安全的
ThreadLocalUtil.set(claims);
//放行
return true;
} catch (Exception e) {
//設置響應狀態(tài)碼
response.setStatus(401);
//設置響應字符集和響應內容
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中的數據
ThreadLocalUtil.remove();
}
}
修改密碼后刪除redis中的token
//修改密碼接口中刪除redis中對應的token ValueOperations<String, String> operations = stringRedisTemplate.opsForValue(); operations.getOperations().delete(newToken);
在本地開發(fā)中如果使用本地redis每次開發(fā)前還得去啟動本地redis,就很麻煩,如果你有一個云服務器就可以本地連云服務器redis就不用每次去啟動redis了,下面是實現教程:
前提是讀者已經安裝好了Redis。
因為剛學Linux不久,敲過一些命令,算不上熟悉,對網絡防火墻那一塊也不熟悉。
因此雖然在項目中已經開啟了Redis服務,也寫好配置類:
#redis spring.redis.host=x.x.x.x spring.redis.port=6379 spring.redis.database= 0 spring.redis.timeout=1800000
```java
@Configuration
@EnableCaching // 開啟緩存處理,使用redis,將字典的數據緩存到其中!
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();
}
};
}
/**
* 設置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);
//解決查詢緩存轉換異常的問題
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;
}
/**
* 設置CacheManager緩存規(guī)則
*
* @param factory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解決查詢緩存轉換異常的問題
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;
}
}
但還是啟動不了。
在這里梳理一下:
- 你先要去云服務器(這里以騰訊云為例)到防火墻里面添加端口,我就以默認的6379為例。

- 然后去你的linux上面查看你的防護墻有沒有打開和防火墻有沒有添加對應的端口號。命令如下
#查看linux上面防火墻的狀態(tài) systemctl status firewalld.service #如果是running的,就需要關了 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
到此這篇關于Redis優(yōu)化token校驗主動失效的實現方案的文章就介紹到這了,更多相關Redis token校驗主動失效內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
讓Redis在你的系統(tǒng)中發(fā)揮更大作用的幾點建議
Redis在很多方面與其他數據庫解決方案不同:它使用內存提供主存儲支持,而僅使用硬盤做持久性的存儲;它的數據模型非常獨特,用的是單線程。另一個大區(qū)別在于,你可以在開發(fā)環(huán)境中使用Redis的功能,但卻不需要轉到Redis2014-06-06

