Redis
NoSQL 数据库
key - value 存储系统(区别于 MySQL,他存储的是键值对)
redis 内存不能无限增加,所以一定要设置过期时间。
Redis 数据结构
- String 字符串类型: name: “yupi”
- List 列表:names: [“yupi”, “dogyupi”, “yupi”]
- Set 集合:names: [“yupi”, “dogyupi”](值不能重复)
- Hash 哈希:nameAge: { “yupi”: 1, “dogyupi”: 2 }
- Zset 集合:names: { yupi - 9, dogyupi - 12 }(适合做排行榜)
- bloomfilter(布隆过滤器,主要从大量的数据中快速过滤值,比如邮件黑名单拦截)
- geo(计算地理位置)
- hyperloglog(pv / uv)
- pub / sub(发布订阅,类似消息队列)
- BitMap (1001010101010101010101010101)
示例
令牌主动失效机制
- 登录成功后,给浏览器响应令牌的同时,把该令牌存储到redis中
- LoginInterceptor拦截器中,需要验证浏览器携带的令牌,同时需要获取redis中存储的与之相同的令牌
- 用户修改密码成功后,删除redis中存储的旧令牌
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring-data-xxx 就是封装了使用 java 操作某个数据库的依赖(数据访问框架,定义增删改查的接口)。
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/big_event
username: root
password: root
data:
redis:
host: localhost
port: 6379
database: 0 # 默认为0,一共 16个db
@Resource
private RedisTemplate redisTemplate;
@PostMapping("/login")
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password) {
User loginUser = userService.findByUserName(username);
if (loginUser == null) {
return Result.error("用户名错误");
}
if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
Map<String, Object> claims = new HashMap<>();
claims.put("id", loginUser.getId());
claims.put("username", loginUser.getUsername());
String token = JwtUtil.genToken(claims);
// 获取操作对象
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
// 将 token 存储到 redis 中
operations.set(token, token, 1, TimeUnit.HOURS);
return Result.success(token);
}
return Result.error("密码错误");
}
@PatchMapping("/updatePwd")
public Result updatePwd(@RequestBody Map<String, String> params,@RequestHeader("Authorization") String token) {
// 校验参数
String oldPwd = params.get("old_pwd");
String newPwd = params.get("new_pwd");
String rePwd = params.get("re_pwd");
if (!StringUtils.hasLength(oldPwd) || !StringUtils.hasLength(newPwd) || !StringUtils.hasLength(rePwd)) {
return Result.error("缺少必要的参数");
}
Map<String, Object> map = ThreadLocalUtil.get();
String username = (String) map.get("username");
User loginUser = userService.findByUserName(username);
if (!loginUser.getPassword().equals(Md5Util.getMD5String(oldPwd))) {
return Result.error("原密码不正确");
}
if (!rePwd.equals(newPwd)) {
return Result.error("两次填写的密码不正确");
}
// service
userService.updatePwd(newPwd);
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.getOperations().delete(token);
return Result.success();
}
package com.heo.interceptors;
import com.heo.pojo.Result;
import com.heo.utils.JwtUtil;
import com.heo.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Map;
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
try {
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
String redisToken = operations.get(token);
if (redisToken == null) {
throw new RuntimeException();
}
Map<String, Object> claims = JwtUtil.parseToken(token);
ThreadLocalUtil.set(claims);
return true;
} catch (Exception e) {
response.setStatus(401);
return false;
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ThreadLocalUtil.remove();
}
}
此外,还可以写一下配置类(非必须),配置序列化器是必须的,因为不配置会出现乱码:
package com.sky.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建redis模板对象...");
RedisTemplate redisTemplate = new RedisTemplate();
// 设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置 redis key 的序列化器(防止key出现乱码)
// redisTemplate.setKeySerializer(RedisSerializer().string()); 也可以
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
加上 @Slf4j 就可以直接使用 log 来记录日志。

1311

被折叠的 条评论
为什么被折叠?



