导入黑马点评项目
百度网盘拉下来就可以了
基于Session实现登录
- 业务流程图
集群session共享问题
用redis解决
基于Redis实现共享session登录
根据实际选择数据结构,这里选择hash和string
- 发送短信验证码
userServiceImpl
@Override
public Result sendCode(String phone, HttpSession session) {
// 1.校验手机号
if(RegexUtils.isPhoneInvalid(phone)){
// 2.如果不符合
return Result.fail("手机号格式错误!");
}
// 3.如果符合生成验证码
String code = RandomUtil.randomNumbers(6);
// 4.用redis保存登录的验证码,并设置过期时间
stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);
// 5.发送验证码
log.debug("发送短信验证码成功:"+code);
return Result.ok();
}
- 登录
userServiceImpl
@Override
public Result login(LoginFormDTO loginForm, HttpSession session) {
// 1.校验手机号
if(RegexUtils.isPhoneInvalid(loginForm.getPhone())){
// 2.如果不符合
return Result.fail("手机号格式错误!");
}
// 从redis中取出对应手机号的验证码
String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + loginForm.getPhone());
if (code == null || !Objects.equals(loginForm.getCode(), code)) {
return Result.fail("验证码错误!");
}
// 查询用户是否存在
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("phone",loginForm.getPhone());
User user = this.baseMapper.selectOne(queryWrapper);
if (user == null) {
// 用户不存在就直接创建新用户写入数据库
user = new User();
user.setPhone(loginForm.getPhone());
user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
this.baseMapper.insert(user);
}
// 随机生成uuid当作token返回给前端
String token = UUID.randomUUID().toString(true);
UserDTO userDTO = new UserDTO();
// 过滤隐私内容
BeanUtils.copyProperties(user,userDTO);
// 1.BeanUtil.beanToMap将bean转换成map
// 2.setIgnoreNullValue忽略null值
// 3.setFieldValueEditor((fieldName,fieldValue)->return fieldValue.toString()) 对每个字段和值处理,将值转成字符串方便写入redis
Map stringObjectMap = BeanUtil.beanToMap(userDTO,new HashMap(),
CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue)-> fieldValue.toString()));
// 将token写入redis
stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY+token,stringObjectMap);
// 设置过期时间
stringRedisTemplate.expire(LOGIN_USER_KEY+token,LOGIN_USER_TTL,TimeUnit.MINUTES);
// 将token返回给前端,下次前端来的时候带上token就能知道是那个用户
return Result.ok(token);
}
- 自定义过滤器
对用户状态进行刷新,不对未登录用户进行拦截
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.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
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_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;
/**
* @author xc
* @date 2023/4/10 21:52
*/
public class RefreshTokenInterceptor implements HandlerInterceptor {
private StringRedisTemplate redisTemplate;
// 想要在我们自定义的类中使用redis,因为这不是spring的bean,不能被管理,所以不能用自动注入
public RefreshTokenInterceptor(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从请求头中拿到前端带来的token
String token = request.getHeader("authorization");
// 没有token直接放行
if (StrUtil.isBlank(token)) {
return true;
}
// 从redis中没有拿到用户信息直接放行
Map
对未登录用户进行拦截
LoginInterceptor
package com.hmdp.utils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author xc
* @date 2023/4/10 21:52
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在该线程中没有用户信息就拦截
if (UserHolder.getUser() == null) {
response.setStatus(401);
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
自定义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;
/**
* @author xc
* @date 2023/4/10 22:01
*/
@Configuration
public class MVCConfig implements WebMvcConfigurer {
// 因为此bean是springboot管理的可以直接注入
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 对未登录用户访问需要登录网页的拦截
// order是拦截的优先级,数字越大越靠后
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/user/code",
"/voucher/**",
"/blog/hot",
"/shop/**",
"/shop-type/**",
"/user/login",
"/upload/**"
).order(1);
// 所有页面刷新用户时间拦截
registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);
}
}
- 查询用户当前状态
UserController
@GetMapping("/me")
public Result me(){
UserDTO user = UserHolder.getUser();
return Result.ok(user);
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
在 2016 年 6 月份的苹果 WWDC 大会上提到了一项差分隐私技术(Differential Privacy),其作用是对用户的数据进行扰动,然后上传到苹果服务器。苹果能通过这些扰动过的数据计算出用户群体的行为模式,但是对每个用户个体的数据却无法解析。 …