模块一:登录模块
发送验证码
Controller层
/*** 发送手机验证码*/@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {return userService.sendCode(phone, session);}
Service接口
public interface IUserService extends IService<User> {Result sendCode(String phone, HttpSession session);
}
**Service接口实现类 **
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result sendCode(String phone, HttpSession session) {//1.校验手机号if (RegexUtils.isPhoneInvalid(phone)){//2.如果不符合,返回错误信息return Result.fail("手机号格式错误!");}//3.符合生成验证码String code = RandomUtil.randomNumbers(6);//4.保存到redis中 login:codestringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);//5.发送验证码log.debug("发送短信验证码成功,验证码:{}", code);//6.返回okreturn Result.ok();}
短信验证登录、注册
Controller层
/*** 登录功能* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码*/@PostMapping("/login")public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){return userService.login(loginForm, session);}
Service接口
public interface IUserService extends IService<User> {Result login(LoginFormDTO loginForm, HttpSession session);
}
Service接口实现类
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1.校验手机号String phone = loginForm.getPhone();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)){return Result.fail("验证码错误");}//3.一致,根据手机号查询用户 MyBatisPlus实现单表查询(要看一下)User user = query().eq("phone", phone).one();//4.判断用户是否存在if(user == null){user = createUserWithPhone(phone);}//5.判断用户是否存在if (user == null) {//6.创建用户保存session.setAttribute("user", user);}//7.1随机生成token作为登录令牌String token = UUID.randomUUID().toString(true);//7.2将User对象转为Hash存储UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(), //处理非String类型数据CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));//7.3存储String tokenKey = LOGIN_USER_KEY + token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);//设置token有效期stringRedisTemplate.expire(LOGIN_USER_KEY, LOGIN_USER_TTL, TimeUnit.MINUTES);return Result.ok(token);}private User createUserWithPhone(String phone) {User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));save(user);return user;}
}
拦截器
拦截器(2个)注册部分
@Configuration
public class MvcConfig implements WebMvcConfigurer {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//登录拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/code","/user/login","/blog/hot","/shop/**","/shop-type/**","/upload/**","/voucher/**").order(1);//token刷新拦截器,设置为优先拦截registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);}
}
登录部分-拦截器
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.判断是否需要拦截(TreadLocal中是否有用户)if(UserHolder.getUser() == null){//没有,需要拦截,设置状态码response.setStatus(401);//拦截return false;}//有用户,则放行return true;}
}
刷新token-拦截器
public class RefreshTokenInterceptor implements HandlerInterceptor {/*** 注意此处只能使用构造函数的方式进行注入* LoginInterceptor类的对象是手动new出来的* spring创建的对象可以帮助依赖注入,自己手动创建的对象没人帮你依赖注入*/private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取请求头中的tokenString token = request.getHeader("authorization");if(StrUtil.isBlank(token)){return true;}//2.基于token获取redis中的用户String key = RedisConstants.LOGIN_USER_KEY + token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);//3.判断用户是否存在if (userMap.isEmpty()){return true;}//5.将查询到的hash数据转为UserDTO对象UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);//6.存在,保存用户信息到TreadLocal中UserHolder.saveUser(userDTO);//7.刷新token有效期stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);//8.放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}