孝感网站推广,攻击asp网站,哪个网站可以做担保交易,批量关键词调排名软件目录
一.短信登录
1.1 导入项目
1.2 Session 实现短信登录
1.3 集群的 Session 共享问题
1.4 基于 Redis 实现共享 Session 登录 一.短信登录
1.1 导入项目
数据库准备
-- 创建用户表
CREATE TABLE user (id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID,phone …目录
一.短信登录
1.1 导入项目
1.2 Session 实现短信登录
1.3 集群的 Session 共享问题
1.4 基于 Redis 实现共享 Session 登录 一.短信登录
1.1 导入项目
数据库准备
-- 创建用户表
CREATE TABLE user (id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID,phone VARCHAR(20) NOT NULL UNIQUE COMMENT 手机号,nick_name VARCHAR(50) COMMENT 昵称,create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间
) COMMENT用户表;导入项目 cd nginx目录
start nginx.exe1.2 Session 实现短信登录 发送验证码
Override
public Result sendCode(String phone, HttpSession session) {// 校验手机号格式是否正确if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式不正确!);}// 生成6位随机数字验证码String code RandomUtil.randomNumbers(6);// 将验证码保存到session中session.setAttribute(code, code);// 日志记录验证码实际开发中应发送短信log.info(验证码为: code);return Result.ok();
}登录功能
Override
public Result login(LoginFormDTO loginForm, HttpSession session) {// 获取手机号String phone loginForm.getPhone();// 校验手机号格式if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式错误!);}// 从session中获取验证码Object cacheCode session.getAttribute(code);String code loginForm.getCode();// 校验验证码是否正确if (code null || !cacheCode.toString().equals(code)) {return Result.fail(验证码错误!);}// 根据手机号查询用户User user query().eq(phone, phone).one();if (user null) {// 如果用户不存在则自动注册user new User();user.setPhone(phone);user.setNickName(user_ RandomUtil.randomString(10));save(user);}// 将用户信息存入sessionsession.setAttribute(user, user);return Result.ok();
}拦截器 LoginInterceptor
public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取sessionHttpSession session request.getSession();// 从session中获取用户信息Object user session.getAttribute(user);// 判断用户是否存在if (user null) {// 用户未登录返回401状态码response.setStatus(401);response.getWriter().write(用户未登录!);return false;}// 用户已登录放行return true;}
}在MvcConfig加上拦截器
Configuration
public class MvcConfig implements WebMvcConfigurer {Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(/user/code, // 验证码接口/user/login, // 登录接口/blog/hot, // 热门博客/shop/**, // 商户信息/shop-type/**, // 商户类型/upload/**, // 文件上传/voucher/** // 优惠券);}
}1.3 集群的 Session 共享问题
多台Tomcat不共享session存储空间当请求切换到不同的tomcat服务时导致数据丢失的问题
所以我们把数据存入Redis集群的Redis可以替代session
1.4 基于 Redis 实现共享 Session 登录
我们应该选择String类型存验证码即可value:验证码但是key要区分开来
选择Hash存储用户信息因为每个字段独立比较好去DRUD,内存占用少key用token即可(随机字符串)
之前的session的话tomcat会自动把session的Id存入Cookie,每次请求都会携带Cookie,所以我们需要手动把token返回给客户端每次请求客户端都会携带着token Service
public class UserServiceImpl extends ServiceImplUserMapper, User implements IUserService {Autowiredprivate StringRedisTemplate stringRedisTemplate;Overridepublic Result sendCode(String phone, HttpSession session) {// 校验手机号格式是否正确if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式不正确!);}// 生成6位随机数字验证码String code RandomUtil.randomNumbers(6);// 将验证码保存到Redis中设置过期时间为2分钟stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY phone, code, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);// 日志记录验证码实际开发中应发送短信log.info(验证码为: code);return Result.ok();}
}Override
public Result login(LoginFormDTO loginForm, HttpSession session) {// 获取手机号String phone loginForm.getPhone();// 校验手机号格式if (RegexUtils.isPhoneInvalid(phone)) {return Result.fail(手机号格式错误!);}// 从Redis中获取验证码String cacheCode stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY phone);String code loginForm.getCode();// 校验验证码是否正确if (cacheCode null || !cacheCode.equals(code)) {return Result.fail(验证码错误!);}// 根据手机号查询用户User user query().eq(phone, phone).one();if (user null) {// 如果用户不存在则自动注册user new User();user.setPhone(phone);user.setNickName(user_ RandomUtil.randomString(10));save(user);}// 生成唯一的TokenString token UUID.randomUUID().toString(true);// 将用户信息转换为UserDTOUserDTO userDTO BeanUtil.copyProperties(user, UserDTO.class);// 将用户信息以Hash类型存储到Redis中设置过期时间为36000分钟stringRedisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY token, BeanUtil.beanToMap(userDTO));stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);// 返回Tokenreturn Result.ok(token);
}MvcConfig注入stringRedisTemplate,然后传给LoginInterceptor,因为LoginInterceptor不是bean不能用spring注入其他bean
Configuration
public class MvcConfig implements WebMvcConfigurer {Resourceprivate StringRedisTemplate stringRedisTemplate;Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns(/user/code,/user/login,/blog/hot,/shop/**,/shop-type/**,/upload/**,/voucher/**);}
}
LoginInterceptor
public class LoginInterceptor implements HandlerInterceptor{private final StringRedisTemplate stringRedisTemplate;public LoginInterceptor(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)){//不存在拦截 设置响应状态吗为401未授权response.setStatus(401);return false;}//2.基于token获取redis中用户String keyRedisConstants.LOGIN_USER_KEY token;MapObject, Object userMap stringRedisTemplate.opsForHash().entries(key);//3.判断用户是否存在if (userMap.isEmpty()){//4.不存在则拦截设置响应状态吗为401未授权response.setStatus(401);return false;}//5.将查询到的Hash数据转化为UserDTO对象UserDTO userDTOnew UserDTO();BeanUtil.fillBeanWithMap(userMap,userDTO, false);//6.保存用户信息到ThreadLocalUserHolder.saveUser(userDTO);//7.更新token的有效时间只要用户还在访问我们就需要更新token的存活时间stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.SECONDS);//8.放行return true;}Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//销毁以免内存泄漏UserHolder.removeUser();}
}
用户请求进去拦截器我们试着去获取请求头内的token根据token去查询用户信息判断是否拦截保存在ThreadLocal,刷新token的有效期
但是这个拦截器是拦截需要登录之后才需要进行请求的路径那我如果一直在访问的是不需要拦截的页面的话我还是会过期这就不合理。所以我们需要在这个拦截器前面再加个拦截器然后在新增拦截器上进行保存ThreadLocal和刷新有效期不理解其他
其实就是对之前的拦截器进行功能拆分 MvcConfig
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);//RefreshTokenInterceptor 先于 LoginInterceptor 执行registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);//默认拦截所有请求}}RefreshTokenInterceptor
public class RefreshTokenInterceptor implements HandlerInterceptor {private final StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate stringRedisTemplate;}Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从请求头中获取TokenString token request.getHeader(authorization);if (StrUtil.isBlank(token)) {// 如果Token为空直接放行return true;}// 构造Redis中的KeyString key RedisConstants.LOGIN_USER_KEY token;// 从Redis中获取用户信息MapObject, Object userMap stringRedisTemplate.opsForHash().entries(key);if (userMap.isEmpty()) {// 如果用户信息为空直接放行return true;}// 将用户信息转换为UserDTO对象UserDTO userDTO new UserDTO();BeanUtil.fillBeanWithMap(userMap, userDTO, false);// 将用户信息保存到ThreadLocal中UserHolder.saveUser(userDTO);// 刷新Token的有效期stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);return true;}
}LoginInterceptor
public class LoginInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从ThreadLocal中获取用户信息UserDTO user UserHolder.getUser();if (user null) {// 如果用户未登录返回401状态码response.setStatus(401);response.getWriter().write