本系列作品持续更新中~
Redis入门到精通【基础篇】【一】
Redis入门到精通【基础篇】【二】
Redis入门到精通【基础篇】【三】
Redis入门到精通【实战篇】【一】
前言
经过基础篇的学习,相信读者们以及对
Redis
有了一定的了解,那么接下来我们就开始Redis
实战篇的训练,帮助大家进一步学习和掌握Redis
项目前置资料
实战篇教程引用自黑马程序员Redis入门到实战教程,在此感谢老师的辛苦教学。此外笔者对教程内容进行优化和精简,还请读者们放心食用
基于Session的短信登录
基于Session
的短信登录有三大步骤:
1.发送短信验证码
2.短信验证码登录和注册
3.校验登录状态
以下是短信登录模块的流程图:
发送短信验证码
Service层的实现
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public Result sendCode(String phone, HttpSession session) {
// 1.校验手机号
if (RegexUtils.isPhoneInvalid(phone)) {
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误!");
}
// 3.符合,生成验证码
String code = RandomUtil.randomNumbers(6);
// 4.保存验证码到session
session.setAttribute("code", code);
// 5.发送验证码,本篇的旨`Redis的应用,发送验证码需外接API操作等,不是本篇重心,因此此处采用日志的伪代码实现
log.debug("发送验证码成功,验证码为:" + code);
// 返回ok
return Result.ok();
}
}
Controller层的实现
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private IUserService userService;
@Resource
private IUserInfoService userInfoService;
/**
* 发送手机验证码
*/
@PostMapping("code")
public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {
// TODO 发送短信验证码并保存验证码
return userService.sendCode(phone, session);
}
}
短信注册和登录
Service层的实现
在UserService
实现以下代码:
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1.校验手机号
String phone = loginForm.getPhone();
if (RegexUtils.isPhoneInvalid(phone)) {
// 2.如果不符合,返回错误信息
return Result.fail("手机号格式错误!");
}
// 2.校验验证码
Object cacheCode = session.getAttribute("code");
String code = loginForm.getCode();
if (cacheCode == null || !cacheCode.toString().equals(code)) {
// 3.不一致,报错
return Result.fail("验证码错误!");
}
// 4.一致,根据手机号查询用户
User user = query().eq("phone", phone).one();
// 5.判断用户是否存在
if (user == null){
// 6.不存在,创建新用户并保存
user = createUserWithPhone(phone);
}
// 7.保存用户信息到session中
session.setAttribute("user", user);
return Result.ok();
}
private User createUserWithPhone(String phone) {
// 创建用户
User user = new User();
user.setPhone(phone);
user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
// 保存用户
save(user);
return user;
}
Controller层的实现
在UserController
实现以下代码:
@PostMapping("/login")
public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){
// TODO 实现登录功能
return userService.login(loginForm, session);
}
登录校验拦截器
设计登录拦截器
在Util
包中新建LoginInterceptor
类,如下:
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.获取session
HttpSession session = request.getSession();
// 2.获取session中的用户
Object user = session.getAttribute("user");
// 3.判断用户是否存在
if (user == null) {
// 4.用户不存在,拦截
response.setStatus(401);
return false;
}
// 5.用户存在,保存用户信息到ThreadLocal
UserHolder.saveUser((User) user);
// 6.放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 移除用户
UserHolder.removeUser();
}
}
配置拦截器
在config
包下新建MvcConfig
类来配置拦截器,如下:
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/user/code",
"/user/login",
"/blog/hot",
"/shop/**",
"/shop-type/**",
"/upload/**",
"/voucher/**"
);
}
}
Controller层的实现
在UserController
实现以下代码:
@GetMapping("/me")
public Result me(){
// TODO 获取当前登录的用户并返回
User user = UserHolder.getUser();
return Result.ok(user);
}
隐藏用户敏感信息
新建UserDTO
我们新建UserDTO
来保存User
中部分信息
@Data
public class UserDTO {
private Long id;
private String nickName;
private String icon;
}
修改UserHolder类
我们将UserHolder
类中的User
都改为UserDTO
,这样在保存,获取和移除操作中只需使用部分User
信息,即保护了用户隐私,又大大减轻了内存压力,如下:
public class UserHolder {
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
public static void saveUser(UserDTO user){
tl.set(user);
}
public static UserDTO getUser(){
return tl.get();
}
public static void removeUser(){
tl.remove();
}
}
在使用UserHolder
的实例中也需修改User
为UserDTO
对象
本节结束
本系列作品持续更新中~