// Service
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. 发送验证码
log.debug("短信验证码, code: {}", code);
// 6. 返回ok
return Result.ok();
}
// Service
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1. 检验手机号
String phone = loginForm.getPhone();
if(RegexUtils.isPhoneInvalid(phone)){
// 2. 如果不符合, 返回错误信息
return Result.fail("手机号格式错误!");
}
// 3. 检验验证码
Object cacheCode = session.getAttribute("code");
String code = loginForm.getCode();
if(cacheCode == null || !cacheCode.toString().equals(code)){
// 4. 如果不一致, 返回错误信息
return Result.fail("验证码错误!");
}
// 5. 一致, 根据手机号查询用户
User user = query().eq("phone", phone).one();
// 6. 判断用户是否存在
if(user == null){
// 7. 不存在, 创建用户并保持
user = createWithPhone(phone);
}
// 8. 保存用户到session
session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
return Result.ok();
}
private User createWithPhone(String phone) {
// 1. 创建用户
User user = new User();
user.setPhone(phone);
user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
// 2. 保存用户
save(user);
return user;
}
// Interceptor
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. 不存在, 拦截, 返回401状态码
response.setStatus(401);
return false;
}
// 5. 存在, 保存用户信息到ThreadLocal
UserHolder.saveUser((UserDTO) user);
// 6. 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
// Config
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/shop/**",
"/voucher/**",
"/shop-type/**",
"/upload/**",
"/blog/hot",
"/user/code",
"/user/login"
);
}
}
// Service
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private StringRedisTemplate stringRedisTemplate;
@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);
// 4. 保存验证码到redis
stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
// 5. 发送验证码
log.debug("短信验证码, code: {}", code);
// 6. 返回ok
return Result.ok();
}
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1. 检验手机号
String phone = loginForm.getPhone();
if(RegexUtils.isPhoneInvalid(phone)){
// 2. 如果不符合, 返回错误信息
return Result.fail("手机号格式错误!");
}
// 3. 检验验证码
String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
String code = loginForm.getCode();
if(cacheCode == null || !cacheCode.equals(code)){
// 4. 如果不一致, 返回错误信息
return Result.fail("验证码错误!");
}
// 5. 一致, 根据手机号查询用户
User user = query().eq("phone", phone).one();
// 6. 判断用户是否存在
if(user == null){
// 7. 不存在, 创建用户并保持
user = createWithPhone(phone);
}
// // 8. 保存用户到session
// session.setAttribute("user", BeanUtil.copyProperties(user, UserDTO.class));
// 8. 保存用户到redis
// 8.1. 随机生成token, 作为登录令牌
String token = UUID.randomUUID().toString(true);
// 8.2. 将User对象转为Hash存储
UserDTO userDTO = new UserDTO();
BeanUtils.copyProperties(user, userDTO);
String userJson = JSON.toJSONString(userDTO);
Map userMap = JSON.parseObject(userJson, Map.class);
userMap.forEach((key, value)->{
userMap.put(key, String.valueOf(value));
});
// 8.3. 存储
String tokenKey = LOGIN_USER_KEY + token;
stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
// 8.4. 设置token有效期
stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
// 9. 返回token
return Result.ok(token);
}
...
}
// Interceptor
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 {
// 1. 获取请求头中的token
String token = request.getHeader("authorization");
if(StrUtil.isBlank(token)){
// 不存在, 拦截, 返回401状态码
response.setStatus(401);
return false;
}
// 2. 基于token获取redis中的用户
Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
.entries(LOGIN_USER_KEY + token);
// 3. 判断用户是否存在
if(userMap.isEmpty()){
// 4. 不存在, 拦截, 返回401状态码
response.setStatus(401);
return false;
}
// 5. 将查询到的Hash数据转为UserDTO对象
String userJson = JSON.toJSONString(userMap);
UserDTO userDTO = JSON.parseObject(userJson, UserDTO.class);
// 6. 存在, 保存用户信息到ThreadLocal
UserHolder.saveUser(userDTO);
// 7. 刷新token有效期
stringRedisTemplate.expire(LOGIN_USER_KEY + token, LOGIN_USER_TTL, TimeUnit.MINUTES);
// 8. 放行
return true;
}
...
}
// Interceptor
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1. 判断是否需要拦截(ThreadLocal中是否有用户)
if(UserHolder.getUser() == null){
// 没有, 需要拦截, 设置状态码
response.setStatus(401);
// 拦截
return false;
}
// 有用户, 则放行
return true;
}
}
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(StringUtils.isBlank(token)){
return true;
}
// 2. 基于token获取redis中的用户
Map<Object, Object> userMap = stringRedisTemplate.opsForHash()
.entries(LOGIN_USER_KEY + token);
// 3. 判断用户是否存在
if(userMap.isEmpty()){
return false;
}
// 5. 将查询到的Hash数据转为UserDTO对象
String userJson = JSON.toJSONString(userMap);
UserDTO userDTO = JSON.parseObject(userJson, UserDTO.class);
// 6. 存在, 保存用户信息到ThreadLocal
UserHolder.saveUser(userDTO);
// 7. 刷新token有效期
stringRedisTemplate.expire(LOGIN_USER_KEY + token, LOGIN_USER_TTL, TimeUnit.MINUTES);
// 8. 放行
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
// Config
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// token刷新拦截器
registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);
// 登录拦截器
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/shop/**",
"/voucher/**",
"/shop-type/**",
"/upload/**",
"/blog/hot",
"/user/code",
"/user/login"
).order(1);
}
}
黑马程序员. Redis入门到实战教程
https://gitee.com/Y_cen/redis