Redis
数据库设计
/**
* redis key 前缀
*/
public static final String REDIS_KEY_PREFIX = "easylive:";
/**
* 验证码key
*/
public static final String REDIS_KEY_CHECK_CODE = REDIS_KEY_PREFIX + "check_code:";
/**
* Redis key token web
*/
public static final String REDIS_KEY_TOKEN_WEB = REDIS_KEY_PREFIX + "token:web:";
public static final String TOKEN_WEB = "token";
public static final String TOKEN_ADMIN = "adminToken";
public static final String REDIS_KEY_TOKEN_ADMIN = REDIS_KEY_PREFIX + "token:admin:";
- Redis 数据库存储如下:
easylive:token:admin+token(UUID)
- 这里在redis前缀组成的部分键后面加上token,一方面是为了防止存储内容存储。暂且把redis理解为一个
Map
,需要根据键获取值。需要保证键的唯一性。恰好在前后端身份校验时,也需要这样的机制保证用户的唯一性。 easylive:token:admin+token
作为键,值 为account
账户
easylive:token:web+token(UUID)
easylive:token:web+token
作为键,值为TokenUserInfoDto
{"@class":"com.easylive.entity.dto.TokenUserInfoDto",
"userId":null,
"nickName":null,
"avatar":null,
"expireAt":1730826856010,
"token":"5b09d79f-b5c5-4a37-a4b4-3bba8fbad0ec",
"fansCount":null,
"currentCoinCount":null,
"focusCount":null}
- TokenUserInfoDto实体
@JsonIgnoreProperties(ignoreUnknown = true)
public class TokenUserInfoDto implements Serializable {
private static final long serialVersionUID = -6910208948981307451L;
private String userId;
private String nickName;
private String avatar;
private Long expireAt;
private String token;
}
登录
- admin 端登录
@RequestMapping(value = "/login")
public ResponseVO login(HttpServletRequest request,
HttpServletResponse response,
@NotEmpty String account,
@NotEmpty String password, @NotEmpty String checkCode,
@NotEmpty String checkCodeKey) {
try {
if (!checkCode.equalsIgnoreCase((String) redisUtils.get(Constants.REDIS_KEY_CHECK_CODE + checkCodeKey))) {
throw new BusinessException("图片验证码不正确");
}
if (!account.equals(appConfig.getAdminAccount()) || !password.equals(StringTools.encodeByMD5(appConfig.getAdminPassword()))) {
throw new BusinessException("账号或者密码错误");
}
String token = redisComponent.saveTokenInfo4Admin(account);
// 把token 放入cookie 中
saveToken2Cookie(response, token);
return getSuccessResponseVO(account);
} finally {
// 去掉老的admintoken
redisUtils.delete(Constants.REDIS_KEY_CHECK_CODE + checkCodeKey);
Cookie[] cookies = request.getCookies();
String token = null;
for (Cookie cookie : cookies) {
if (cookie.getName().equals(Constants.TOKEN_ADMIN)) {
token = cookie.getValue();
}
}
if (!StringTools.isEmpty(token)) {
redisComponent.cleanToken4Admin(token);
}
}
}
public String saveTokenInfo4Admin(String account) {
String token = UUID.randomUUID().toString();
redisUtils.setex(Constants.REDIS_KEY_TOKEN_ADMIN + token, account, Constants.REDIS_KEY_EXPIRES_DAY);
return token;
}
public void saveToken2Cookie(HttpServletResponse response, String token) {
Cookie cookie = new Cookie(Constants.TOKEN_ADMIN, token);
cookie.setMaxAge(-1);
cookie.setPath("/");
response.addCookie(cookie);
}
总结
- 把用户信息存入
redis
中,并返回对应的token
, 这个token
+ 对应的前缀就是redis
中对应的 键。后期可以通过这个键找到值。从而进行身份校验。 - 返回的
token
相当于是 用户信息的加密映射。存入cookie
,
Cookie cookie = new Cookie(Constants.TOKEN_ADMIN, token);
草草画个图🤡
前端发起请求时进行校验。每次发请求时。后端通过全局请求拦截器。取出 token
,这里有两种取法:
- 前端在每次的请求头中,带上
token
,后端通过request.getHeader(对应的前缀组合成的键)
- 遍历
Cookie
数组,获取其中的对应 键的值。
补充
- 补充一些redis的工具类
RedisComponent
封装了对RedisUtils
的操作
package com.easylive.component;
import com.easylive.entity.constants.Constants;
import com.easylive.entity.dto.TokenUserInfoDto;
import com.easylive.redis.RedisUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.UUID;
/**
* 功能:
* 日期:2024/11/3 下午4:48
*/
@Component
public class RedisComponent {
@Resource
private RedisUtils redisUtils;
/**
* 保存验证码
* @param code
* @return
*/
public String saveCheckCode(String code) {
String checkCode = UUID.randomUUID().toString();
redisUtils.setex(Constants.REDIS_KEY_CHECK_CODE + checkCode, code, Constants.REDIS_KEY_EXPRIES_ONE_MINUTE * 10);
return checkCode;
}
/**
* 获取验证码
* @param checkCodeKey
* @return
*/
public String getCheckCode(String checkCodeKey) {
return (String) redisUtils.get(Constants.REDIS_KEY_CHECK_CODE + checkCodeKey);
}
/**
* 清除验证码
* @param checkCodeKey
*/
public void clearCheckCode(String checkCodeKey) {
redisUtils.delete(Constants.REDIS_KEY_CHECK_CODE + checkCodeKey);
}
/**
* 保存token信息
*
* @param tokenUserInfoDto
*/
public void saveTokenInfo(TokenUserInfoDto tokenUserInfoDto) {
String token = UUID.randomUUID().toString();
tokenUserInfoDto.setExpireAt(System.currentTimeMillis() + Constants.REDIS_KEY_EXPRIES_ONE_DAY * 7);
tokenUserInfoDto.setToken(token);
redisUtils.setex(Constants.REDIS_KEY_TOKEN_WEB + token, tokenUserInfoDto, Constants.REDIS_KEY_EXPRIES_ONE_DAY * 7);
}
/**
* 清除token
*
* @param token
*/
public void cleanToken(String token) {
redisUtils.delete(Constants.REDIS_KEY_TOKEN_WEB + token);
}
/**
* 获取admintoken信息
* @param token
*/
public void cleanToken4Admin(String token) {
redisUtils.delete(Constants.REDIS_KEY_TOKEN_ADMIN + token);
}
/**
* 获取token信息
* @param token
* @return
*/
public TokenUserInfoDto getTokenUserInfo(String token) {
return (TokenUserInfoDto) redisUtils.get(Constants.REDIS_KEY_TOKEN_WEB + token);
}
/**
* 更新token信息
* @param account
* @return
*/
public String saveTokenInfo4Admin(String account) {
String token = UUID.randomUUID().toString();
redisUtils.setex(Constants.REDIS_KEY_TOKEN_ADMIN + token, account, Constants.REDIS_KEY_EXPRIES_ONE_DAY);
return token;
}
/**
* 清除admintoken
* @param token
* @return
*/
public String getTokenInfo4Admin(String token) {
return (String) redisUtils.get(Constants.REDIS_KEY_TOKEN_ADMIN + token);
}
}
RedisUtils.java
package com.easylive.redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component("redisUtils")
public class RedisUtils<V> {
@Resource
private RedisTemplate<String, V> redisTemplate;
private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
public void delete(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
public V get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, V value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
logger.error("设置redisKey:{},value:{}失败", key, value);
return false;
}
}
public boolean keyExists(String key) {
return redisTemplate.hasKey(key);
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean setex(String key, V value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.MILLISECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
logger.error("设置redisKey:{},value:{}失败", key, value);
return false;
}
}
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.MILLISECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public List<V> getQueueList(String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
public boolean lpush(String key, V value, Long time) {
try {
redisTemplate.opsForList().leftPush(key, value);
if (time != null && time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public long remove(String key, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, 1, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public boolean lpushAll(String key, List<V> values, long time) {
try {
redisTemplate.opsForList().leftPushAll(key, values);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public V rpop(String key) {
try {
return redisTemplate.opsForList().rightPop(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public Long increment(String key) {
Long count = redisTemplate.opsForValue().increment(key, 1);
return count;
}
public Long incrementex(String key, long milliseconds) {
Long count = redisTemplate.opsForValue().increment(key, 1);
if (count == 1) {
//设置过期时间1天
expire(key, milliseconds);
}
return count;
}
public Long decrement(String key) {
Long count = redisTemplate.opsForValue().increment(key, -1);
if (count <= 0) {
redisTemplate.delete(key);
}
logger.info("key:{},减少数量{}", key, count);
return count;
}
public Set<String> getByKeyPrefix(String keyPrifix) {
Set<String> keyList = redisTemplate.keys(keyPrifix + "*");
return keyList;
}
public Map<String, V> getBatch(String keyPrifix) {
Set<String> keySet = redisTemplate.keys(keyPrifix + "*");
List<String> keyList = new ArrayList<>(keySet);
List<V> keyValueList = redisTemplate.opsForValue().multiGet(keyList);
Map<String, V> resultMap = keyList.stream().collect(Collectors.toMap(key -> key, value -> keyValueList.get(keyList.indexOf(value))));
return resultMap;
}
public void zaddCount(String key, V v) {
redisTemplate.opsForZSet().incrementScore(key, v, 1);
}
public List<V> getZSetList(String key, Integer count) {
Set<V> topElements = redisTemplate.opsForZSet().reverseRange(key, 0, count);
List<V> list = new ArrayList<>(topElements);
return list;
}
}