将验证码 (session.setAttribute("code", code));用户信息 (session.setAttribute("user", userDTO))改为存入redis中
将随机生成的token作为登录凭证,放在请求头中的authorization字段
并设置两层拦截器,解决状态登录刷新的问题
业务流程图1
业务流程图2
具体实现:
UserServiceImpl- package com.hmdp.service.impl;
- import cn.hutool.core.bean.BeanUtil;
- import cn.hutool.core.bean.copier.CopyOptions;
- import cn.hutool.core.lang.UUID;
- import cn.hutool.core.util.RandomUtil;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.hmdp.dto.LoginFormDTO;
- import com.hmdp.dto.Result;
- import com.hmdp.dto.UserDTO;
- import com.hmdp.entity.User;
- import com.hmdp.mapper.UserMapper;
- import com.hmdp.service.IUserService;
- import com.hmdp.utils.RegexUtils;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.stereotype.Service;
- import javax.annotation.Resource;
- import javax.servlet.http.HttpSession;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.concurrent.TimeUnit;
- import static com.hmdp.utils.RedisConstants.*;
- import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;
- /**
- * <p>
- * 服务实现类
- * </p>
- *
- * @author ztn
- * @since 2025-9-10
- */
- @Slf4j
- @Service
- public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
- @Resource
- private StringRedisTemplate stringRedisTemplate;
- @Override
- public Result sendCode(String phone, HttpSession session) {
- //1.校验手机号,isPhoneInvalid是无效
- if(RegexUtils.isPhoneInvalid(phone)){
- //2.如果不符合,返回错误信息
- return Result.fail("手机号格式错误!");
- }
- //3.符合,生成验证码
- String code = RandomUtil.randomNumbers(6);
- //4.保存验证码到redis //set key value ex 120 两分钟有效期
- // session.setAttribute("code",code);
- // stringRedisTemplate.opsForValue().set("login:code" + phone,code,2, TimeUnit.MINUTES);
- stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
- //5.发送验证码
- log.debug("发送短信验证码成功,验证码:{}",code);
- //返回ok
- return Result.ok();
- }
- @Override
- public Result login(LoginFormDTO loginForm, HttpSession session) {
- String phone = loginForm.getPhone();
- //1.校验手机号,isPhoneInvalid是无效
- if(RegexUtils.isPhoneInvalid(phone)){
- //2.如果不符合,返回错误信息
- return Result.fail("手机号格式错误!");
- }
- //3.从redis中获取并校验验证码
- String cachecode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
- String code = loginForm.getCode();
- if(cachecode == null && !cachecode.equals(code)){
- //4.不一致,报错
- return Result.fail("验证码错误");
- }
- //4.一致,根据手机号查询用户select * from tb_user where phone = ?
- User user = query().eq("phone", phone).one();
- //5.判断用户是否存在
- if(user == null){
- //6.不存在,创建用户并保存
- user = createUserWithphone(phone);
- }
- //7.保存用户信息到redis中
- //7.1生成随机token,作为登录令牌
- String token = UUID.randomUUID().toString(true);
- //7.2将user对象转为HashMap存储
- UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
- Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
- CopyOptions.create()
- .setIgnoreNullValue(true)
- .setFieldValueEditor((fieldName,fieldValue) ->fieldValue.toString()));
- //7.3存储
- stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token,userMap);
- //7.4有效期
- stringRedisTemplate.expire(LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);
- //8.返回token
- return Result.ok(token);
- }
- private User createUserWithphone(String phone) {
- //1.创建用户
- User user = new User();
- user.setPhone(phone);
- user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
- //2.保存用户
- save(user);
- return user;
- }
- }
复制代码 RefreshTokenInterceptor- package com.hmdp.utils;
- import cn.hutool.core.bean.BeanUtil;
- import cn.hutool.core.util.StrUtil;
- import com.hmdp.dto.UserDTO;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.lang.Nullable;
- import org.springframework.web.servlet.HandlerInterceptor;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.util.Map;
- import java.util.concurrent.TimeUnit;
- import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;
- public class RefreshTokenInterceptor implements HandlerInterceptor {
- private StringRedisTemplate stringRedisTemplate;
- public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
- this.stringRedisTemplate = stringRedisTemplate;
- }
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- //1.获取请求头中的token
- String token = request.getHeader("authorization");
- if(StrUtil.isBlank(token)){
- return true;
- }
- //2.获取redis中的用户
- Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
- .entries(RedisConstants.LOGIN_USER_KEY + token);
- //3.判断用户是否存在
- if (userMap.isEmpty()) {
- return true;
- }
- //5.将查询到的Hash数据转为userdto对象
- UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
- //6.存在,保存用户信息到ThreadLocal
- UserHolder.saveUser(userDTO);
- //7.刷新token有效期
- stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token,LOGIN_USER_TTL, TimeUnit.MINUTES);
- //8.放行
- return true;
- }
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
- //移除用户
- UserHolder.removeUser();
- }
- }
复制代码 LoginInterceptor- package com.hmdp.utils;
- import org.springframework.web.servlet.HandlerInterceptor;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- public class LoginInterceptor implements HandlerInterceptor {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- //判断是否要拦截(ThreadLocal中是否有用户)
- if(UserHolder.getUser() == null){
- response.setStatus(401);
- return false;
- }
- return true;
- }
- }
复制代码 MvcConfig- package com.hmdp.config;
- import com.hmdp.utils.LoginInterceptor;
- import com.hmdp.utils.RefreshTokenInterceptor;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.core.StringRedisTemplate;
- import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- import javax.annotation.Resource;
- @Configuration
- public class MvcConfig implements WebMvcConfigurer {
- @Resource
- private StringRedisTemplate stringRedisTemplate;
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(new LoginInterceptor())
- .excludePathPatterns(//放行
- "/user/code",
- "/user/login",
- "/blog/hot",
- "/shop/**",
- "/upload/**",
- "/shop-type/**",
- "/voucher/**"
- ).order(1);
- registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
- }
- }
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |