黑马点评项目总结1-使用Session发送验证码和登录login和 使用Redis存储验证码和Redis的token登录

47 篇文章 0 订阅
23 篇文章 0 订阅

黑马先是总结了从session实现登录,然后是因为如果使用了集群方式的服务器的话,存在集群共享session互相拷贝效率低下的问题,接着引出了速度更快的内存型的kv数据库Redis,

使用Session发送验证码和登录login

举个例子:
原来的发送验证码和登录的例子,直接在session中存验证码6

    @Override
    public Result sendCode(String phone, HttpSession session) {
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        //我这里瞎勾八写的验证码是6,图省事
        session.setAttribute("code","6");

        return Result.ok();
    }

从session中获取验证码,然后与从表单输入的验证码相比较,如果一致,那么就是验证码正确,query()方法是查询tb_user中的用户,利用phone字段查询用户,然后如果查询出来用户那么就利用这个电话字段创建用户user对象,如果没查出来,就自动创建新的用户,然后存在UserHolder里面,UserHolder是用静态ThreadLocal存储的User对象。

    @Override public Result login(LoginFormDTO loginFormDTO, HttpSession session) {
        String phone = loginFormDTO.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        String code = (String) session.getAttribute("code");
        if(code == null || !code.equals("6")){
            return Result.fail("验证码错误");
        }
        User user = query().eq("phone", phone).one();
        if(user == null){
            user = createUser(phone);
        }
        session.setAttribute("user",user);
        UserHolder.saveUser(user);
        return Result.ok();

    }

接着如果登录的话,前端界面用户的界面是访问/user/me来返回用户信息,注意这里的me函数一定要返回user,否则黑马点评的界面会又再次跳转到登录界面,非常✓8。黑马点评项目登录有好几次都是因为这个UserHolder的UserDTO为空导致又跳转到登录界面,非常✓8。

    @GetMapping("/me")
    public Result me(){
        // TODO 获取当前登录的用户并返回
        UserDTO user = UserHolder.getUser();
        log.debug("me:{}", user);
        return Result.ok(user);
    }

使用Redis存储验证码和Redis的token登录

UserServicelmpl.java,注意,我们使用Redis返回token的时候,Key是token,存储的是UserMap,UserMap是存储了UserDTO信息的,UserDTO存储了用户信息,包括他的phone,因此,token是和电话号码存在一一对应关系的!!我们后续拦截器拦截的时候,获取了Token之后,利用获取到的Token去Redis里面查询的时候就知道是哪个用户了!!!

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Resource private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result sendCode(String phone, HttpSession session) {
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        //使用了Redis存储验证码,Key是LOGIN_CODE_KEY,value是6
        stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY,"6",RedisConstants.LOGIN_CODE_TTL,
                TimeUnit.MINUTES);
        log.debug("发送验证码成功");
        return Result.ok();
    }
    @Override public Result login(LoginFormDTO loginFormDTO, HttpSession session) {
        String phone = loginFormDTO.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }
        //从内存式的Redis数据库中获取验证码,Key是LOGIN_CODE_KEY,value是6
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY );
        String code = loginFormDTO.getCode();
        if(code == null || !code.equals(cacheCode)){
            return Result.fail("验证码错误");
        }
        User user = query().eq("phone", phone).one();
        if(user == null){
            user = createUser(phone);
        }
        
        String token = UUID.randomUUID().toString(true);
		//不存储User,只存储UserDTO,减轻存储压力,避免存储敏感信息
        UserDTO userDTO= BeanUtil.copyProperties(user,UserDTO.class);
		//把UserDTO信息转化为HashMap
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        String tokenKey = LOGIN_USER_KEY + token;
        //把登录的这个用户以hashMap方式存储到Redis之中,token为Key,取出来的Value才是UserDTO
        stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);
        //设置用户的登录过期时间,防止Redis存储太多用户信息导致内存占用很多
        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.MINUTES);
        UserHolder.saveUser(userDTO);
//        返回token
        return Result.ok(token);

    }

    private User createUser(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName("user"+ RandomUtil.randomString(10));
        return user;
    }
}

拦截器设置:

拦截器前端

在这里插入图片描述

拦截器后端

LoginInterceptor.java
UserServicelmpl.java的@Override public Result login(LoginFormDTO loginFormDTO, HttpSession session)函数return Result.ok(token);直接返回了token到前端界面,前端界面再把这个token存储到请求头的’authorization’里面。

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    private StringRedisTemplate stringRedisTemplate;
    public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override // 拦截请求
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("authorization");//根据authorization获取token
        if (StrUtil.isBlank(token)) {
            response.setStatus(401);
            return false;
        }
        //根据token从Redis里面获取UserMap
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);
        //把UserMap从HashMap形式转化为Java Bean。
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //存储到UserHolder的ThreadLocal里面
        UserHolder.saveUser(userDTO);
        //刷新过期时间
        stringRedisTemplate.expire(LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 放行
        return true;
    }
    @Override // 拦截响应
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用户
        UserHolder.removeUser();
    }
}
好的,以下是使用 Spring Boot 和 Redis 实现用户登录后用户信息存储以及验证使用 token 的步骤: 1. 首先需要在 pom.xml 中添加 Redis 相关的依赖,例如:jedis、spring-boot-starter-data-redis 等。 2. 在 application.properties 文件中配置 Redis 相关信息,如下: ``` spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.password= ``` 3. 创建一个 UserService 类,用于操作 Redis 存储用户信息和 token。具体包括以下方法: - saveUser(User user):将用户信息存储Redis 中。 - getUserById(String id):根据用户 id 从 Redis 中获取用户信息。 - deleteUserById(String id):根据用户 id 从 Redis 中删除用户信息。 - saveToken(String token, String userId):将 token 和用户 id 存储Redis 中。 - getUserIdByToken(String token):根据 tokenRedis 中获取用户 id。 示例代码如下: ``` @Service public class UserService { @Autowired private StringRedisTemplate redisTemplate; private static final String REDIS_KEY_PREFIX_USER = "user:"; private static final String REDIS_KEY_PREFIX_LOGIN = "login:"; public void saveUser(User user) { String key = REDIS_KEY_PREFIX_USER + user.getId(); redisTemplate.opsForHash().put(key, "id", user.getId()); redisTemplate.opsForHash().put(key, "username", user.getUsername()); redisTemplate.opsForHash().put(key, "password", user.getPassword()); } public User getUserById(String id) { String key = REDIS_KEY_PREFIX_USER + id; String username = (String) redisTemplate.opsForHash().get(key, "username"); String password = (String) redisTemplate.opsForHash().get(key, "password"); User user = new User(); user.setId(id); user.setUsername(username); user.setPassword(password); return user; } public void deleteUserById(String id) { String key = REDIS_KEY_PREFIX_USER + id; redisTemplate.delete(key); } public void saveToken(String token, String userId) { redisTemplate.opsForValue().set(REDIS_KEY_PREFIX_LOGIN + token, userId, Duration.ofMinutes(30)); } public String getUserIdByToken(String token) { return redisTemplate.opsForValue().get(REDIS_KEY_PREFIX_LOGIN + token); } } ``` 4. 在登录接口中,验证用户信息是否正确,并将 token 和用户 id 存储Redis 中。示例代码如下: ``` @RestController public class LoginController { @Autowired private UserService userService; @PostMapping("/login") public String login(@RequestBody User user, HttpServletResponse response) { // 验证用户信息是否正确 if (!"admin".equals(user.getUsername()) || !"123456".equals(user.getPassword())) { return "用户名或密码错误"; } // 将 token 和用户 id 存储Redis 中 String token = UUID.randomUUID().toString(); userService.saveToken(token, user.getId()); // 将 token 存储到 Cookie 中 Cookie cookie = new Cookie("token", token); cookie.setMaxAge(30 * 60); cookie.setPath("/"); response.addCookie(cookie); return "登录成功"; } } ``` 5. 在需要验证用户信息的接口中,从 Redis 中获取用户信息,并验证用户是否已登录。示例代码如下: ``` @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/user") public String getUser(@RequestParam String userId, HttpServletRequest request) { // 从 Redis 中获取用户信息 User user = userService.getUserById(userId); if (user == null) { return "用户不存在"; } // 验证用户是否已登录 String token = null; Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("token".equals(cookie.getName())) { token = cookie.getValue(); break; } } } if (token == null) { return "用户未登录"; } String loginUserId = userService.getUserIdByToken(token); if (!userId.equals(loginUserId)) { return "用户未登录"; } // TODO:返回用户信息 } } ``` 以上就是使用 Spring Boot 和 Redis 实现用户登录后用户信息存储以及验证使用 token 的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dareu_4523

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值