- 登录、退出
- 使用Redis储存验证码:(原本添加到session中,减轻服务器压力。将验证码存到Redis中,方便查询检验)
- 因为验证码需要频繁的进行访问与刷新,因此对性能的要求较高;
- 验证码不需要永久保存,通常在很短的时间后就会失效;
- 分布式部署的时候,存在session共享的问题。
- 使用Redis储存登录凭证:(登录信息)如果都正确,会生成一个token,将token返回给浏览器cookie保存,并将token(key)和user 的用户信息(value) 保存在redis(hash)中
- 因为后台在每次处理请求的时候都要查询用户的登录凭证,访问的频率非常高,因此需要使用redis存储。
- 使用Redis存储用户信息
- 将user缓存到Redis中,获取user时,先从Redis获取。取不到时,则从数据库中查询,再缓存到Redis中。因为很多界面都要用到user信息,并发时,频繁的访问数据库,会导致数据库崩溃。变更数据库时,先更新数据库,再清空缓存
- 使用Redis储存验证码:(原本添加到session中,减轻服务器压力。将验证码存到Redis中,方便查询检验)
- 事务管理
- 事务操作:redisTemplate直接调用opfor…来操作redis数据库,每执行一条命令是要重新拿一个连接,因此很耗资源,让一个连接直接执行多条语句的方法就是使用SessionCallback,同样作用的还有RedisCallback,但不常用。
- 点赞、关注:把数据存到Redis里来提升性能(Redis将所有的数据都存放在内存中)
- 点赞使用的是set类型存储,key为点赞对象,set中保存点赞人的ID
- 关注使用的是zSet类型存储,key为被关注者,set保存关注者以及关注时间为score
- 网站数据统计
- HyperLoglog(性能好,且存储空间小)、Bitmap(性能好、且可以统计精确的结果)
- 热帖排行
Redis用户登录鉴权怎么用的?用的什么数据结构?
Redis是一种高性能的内存键值数据库,支持多种数据结构,如字符串、哈希表、列表、集合、有序集合等。在Redis中实现用户登录鉴权,可以使用哈希表和字符串这两种数据结构。
具体实现步骤如下:
- 在Redis中创建一个哈希表,用于存储用户信息。哈希表可以通过Redis的HMSET命令来创建,例如:
HMSET users:user1:name username1:password password1:age age1
上述命令创建了一个名为“users”的哈希表,包含了名为“user1”的条目。该条目包含了用户名“username1”、密码“password1”和年龄“age1”等字段。
- 实现登录功能。当用户输入用户名和密码时,需要从Redis中查询对应的用户信息。可以使用Redis的HGETALL命令来获取整个哈希表的所有字段和值,例如:
HGETALL users
上述命令会返回一个包含所有用户信息的字符串数组,可以解析这个数组得到用户名和密码。然后使用用户输入的用户名和密码与哈希表中的数据进行比较,如果匹配成功则登录成功,否则登录失败。
- 实现鉴权功能。在登录成功后,可以根据用户输入的操作类型(如读操作、写操作等)进行鉴权。可以使用Redis的SADD命令将登录成功的用户添加到集合中,例如:
SADD authorized_users user1
上述命令将“user1”添加到名为“authorized_users”的集合中。然后可以根据用户输入的操作类型,使用Redis的SMEMBERS命令获取指定集合中的所有成员,例如:
SMEMBERS authorized_users
上述命令会返回一个包含所有授权用户的字符串数组,可以根据操作类型来判断当前用户是否具有操作权限。
总的来说,通过Redis的哈希表和集合数据结构可以实现对用户的登录鉴权功能。
1、使用Redis存储验证码
用户第一次访问登录界面,服务器生成随机字符串保存到 cookie 里发送给浏览器,同时生成相应的 key 保存验证码文本到 Redis 里
- 因为用户还未登录,所以无法获取用户信息,无法与验证码进行绑定,因此使用临时凭证作为 key 来存取验证码信息
- 用户访问登录页面的时候,先给用户发一个 cookie,存储随机生成的字符串,以此字符串作为临时凭证
- cookie 有效时间设置比较短以节省开销,设置为 60 秒即可
- Redis 中验证码也设置为 60 秒过期
private static final String PREFIX_KAPTCHA = "kaptcha";
...
// 登录验证码
public static String getKaptchaKey(String owner) {
return PREFIX_KAPTCHA + SPLIT + owner;
}
@GetMapping("/kaptcha")
public void kaptcha(HttpServletResponse response) {
// 生成验证码
...
// 将验证码存入session
// session.setAttribute("kaptcha", text);
// 验证码的归属
String kaptchaOwner = CommunityUtil.generateUUID();
Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
cookie.setMaxAge(60);
cookie.setPath(contextPath);
response.addCookie(cookie);
// 将验证码存入Redis
String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
redisTemplate.opsForValue().set(kaptchaKey, text, 60, TimeUnit.SECONDS);
// 将图片输出给浏览器
...
}
@PostMapping("/login")
public String login(String username, String password, String code, boolean rememberme,
Model model, HttpServletResponse response,
@CookieValue(value = "kaptchaOwner", required = false) String kaptchaOwner) {
// 检查验证码
// String kaptcha = (String) session.getAttribute("kaptcha");
String kaptcha = null;
if(StringUtils.isNotBlank(kaptchaOwner)) {
String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
kaptcha = (String) redisTemplate.opsForValue().get(kaptchaKey);
}
...
}
login 方法中,CookieValue 注解中带上 required = false,防止 cookie 过期后,取不到 cookie 值而报错。
2、使用 Redis 存储登录凭证
原本添加到MySQL中,为减轻每次登录都去查询数据库的压力,将登录凭证ticket缓存在Redis中,防止每次都要进行数据库的查询,提高并发能力。退出登录时,原本要修改数据库中的登录凭证,现在只需要修改Redis即可。
private static final String PREFIX_TICKET = "ticket";
...
// 登录的凭证
public static String getTicketKey(String ticket) {
return PREFIX_TICKET + SPLIT + ticket;
}
原来的 LoginTicketMapper 上加上 @Deprecated 注解
@Mapper
@deprecated
public interface LoginTicketMapper {
...
}
public Map<String, Object> login(String username, String password, long expiredSeconds) {
……
// 生成登录凭证
……
// loginTicketMapper.insertLoginTicket(loginTicket);
String ticketKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
redisTemplate.opsForValue().set(ticketKey, loginTicket);
……
}
退出的时候取出登录凭证,把状态设置为 1,再重新放入 Redis,没有直接删除,保留用户的登录记录,以便之后用到
public void logout(String ticket) {
// loginTicketMapper.updateStatus(ticket, 1);
String ticketKey = RedisKeyUtil.getTicketKey(ticket);
LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(ticketKey);
loginTicket.setStatus(1);
redisTemplate.opsForValue().set(ticketKey, loginTicket);
}
public LoginTicket findLoginTicket(String ticket) {
// return loginTicketMapper.selectByTicket(ticket);
String ticketKey = RedisKeyUtil.getTicketKey(ticket);
return (LoginTicket) redisTemplate.opsForValue().get(ticketKey);
}
Redis存储登录凭证怎么做的?
(ChatGPT)具体实现方法如下:
- 使用Redis的哈希表数据结构(Hash)来存储用户登录凭证。将用户名作为哈希表的键,密码作为值存储。
- 在用户登录时,先从Redis中获取存储的用户登录凭证,然后与用户输入的凭证进行比对。如果比对成功,则表示用户登录成功,否则登录失败。
- 为了提高安全性,可以将密码进行加密处理,例如使用哈希函数(如SHA-256)对密码进行哈希运算,然后将哈希值存储在Redis中。这样可以防止密码被直接窃取,提高系统的安全性。
- 为了避免在每次用户请求时都从Redis中获取登录凭证,可以在登录成功后将凭证存储在本地(如浏览器cookie或localStorage中),然后在后续请求中使用这些凭证来验证用户身份。
需要注意的是,在Redis中存储密码等敏感信息时,应该采取适当的措施来保护Redis服务器的安全,例如限制访问权限、使用强密码等。此外,为了进一步提高安全性,可以考虑使用OAuth等授权机制来避免直接存储密码。
3、使用Redis缓存用户信息
- 当查询的时候,优先从缓存中取值;
- 缓存中取不到的时候再从 MySQL 数据库中取值,再初始化缓存数据,即放入 Redis 中;
- 数据变更时清除缓存数据(如果采取更新缓存中数据的方法可能会带来并发问题,并且比较繁琐);
- 往 Redis 中直接存取对象,会序列化成 Json 字符串
private static final String PREFIX_USER = "user";
……
// 用户
public static String getUserKey(int userId) {
return PREFIX_USER + SPLIT + userId;
}
// 1.优先从缓存中取值
private User getCache(int userId) {
String userKey = RedisKeyUtil.getUserKey(userId);
return (User) redisTemplate.opsForValue().get(userKey);
}
// 2.取不到时初始化缓存数据
private User initCache(int userId) {
User user = userMapper.selectById(userId);
String userKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.opsForValue().set(userKey, user, 3600, TimeUnit.SECONDS);
return user;
}
// 3.数据变更时清除缓存数据
private void clearCache(int userId) {
String userKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.delete(userKey);
}
public User findUserById(int id) {
// return userMapper.selectById(id);
User user = getCache(id);
if (user == null) {
user = initCache(id);
}
return user;
}
在每个 update 的方法后加上 clearCache 清除缓存,以修改头像为例:
public int updateHeader(int userId, String headerUrl) {
// return userMapper.updateHeader(userId, headerUrl);
int rows = userMapper.updateHeader(userId, headerUrl);
clearCache(userId);
return rows;
}
当然评论区也有人提到了,最好用 AOP 来做这件事情
注意点:
- 用户在登录界面时,因为还未登录,无法将用户与验证码进行绑定,所以生成一个随机字符串作为临时凭证,通过 cookie 传给浏览器,并设置 60 秒过期
- login 方法上,使用 @CookieValue 注解时带上 required = false。经测试,60 秒验证码过期后,再登录会报错,取不到 kaptchaOwner
- 退出登录时是否删除登录凭证的问题。老师说不需要删,把状态改为 1,如果后面添加查询登录记录的功能的话会用到。不过评论区中提到,ticket 也就是一串随机字符串,不好查。这个问题着实有待商榷啊(⊙o⊙)…
- 缓存用户信息的三个要点(见上)。其中关于更新时删除缓存数据,评论区提到最好用 AOP 来做