redis的实战项目01_短信登录

  • 一、导入黑马点评项目
    • 1. 数据库:
    • 2. 单体项目介绍:
  • 二、基于session实现登录
    • 1、发送短信验证码
      • 1.理论流程
      • 2.代码操作:
    • 2、短信验证码登录、注册
      • 1.理论流程
      • 2.代码操作:
    • 3、校验登录状态
      • 1.理论流程
      • 2.代码操作:
      • 3.隐藏用户敏感信息
  • 三、集群的session共享问题
  • 四、基于redis实现共享session登录
    • 1、发送验证码。如何将验证码存入到redis中?
      • 代码操作:
    • 2、验证码登录。如何将用户信息保存到redis
      • 代码操作:
    • 3、校验登录功能。获取用户信息中的token【hash的key】
      • 代码操作:
    • 4、测试:
  • 五、登录拦截器的优化

一、导入黑马点评项目

1. 数据库:

⚫ tb_user:用户表
⚫ tb_user_info:用户详情表
⚫ tb_shop:商户信息表
⚫ tb_shop_type:商户类型表
⚫ tb_blog:用户日记表(达人探店日记)
⚫ tb_follow:用户关注表
⚫ tb_voucher:优惠券表
⚫ tb_voucher_order:优惠券的订单表

2. 单体项目介绍:

案例是:前后端分离的单体项目,后端部署在Tomcat,前端部署在Nginx服务器上。
当移动端、客户端、app端发起Nginx请求,得到静态资源。页面再通过Nginx,向服务端发起请求,进行查询数据。查询后,返回给前端,前端在进行渲染。

开启Nginx:
D:\Program Files\Nginx_Test\nginx-1.18.0> start .\nginx.exe

二、基于session实现登录

短信登录包括:

  1. 短信的发送
  2. 基于短信验证码的登录、注册
  3. 对登录状态的校验

1、发送短信验证码

1.理论流程

用户通过手机进行获取验证码,进行登录。
1.那么用户会根据手机号请求一个验证码
2.服务端接收到手机号后,进行验证手机号。
2.1校验失败,重新写手机号
2.2校验成功,服务端生成验证码
3.保存验证码到session
4.发送验证码

请求路径:api是表示当前的请求,是一个发向Tomcat服务的请求,会过滤掉。真正的请求路径是:/user/code
请求参数:phone=888888

2.代码操作:

controller层:@PostMapping("code")public Result sendCode(@RequestParam("phone") String phone, HttpSession session) {/***  发送短信验证码并保存验证码*  session:需要把验证码保存到session*/return userService.sendCode(phone,session);}----------------------sever层-----------------------package com.hmdp.service.impl;import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpSession;/*** <p>* 服务实现类* </p>**/
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic Result sendCode(String phone, HttpSession session) {//1. 验证手机号// RegexUtils 是一个工具类,直接将前端传过来的电话,传过去,判断手机格式字是否正常// 如果返回false 是手机号格式正常。 返回true,是手机号错误。if (RegexUtils.isPhoneInvalid(phone)){//2. 验证不通过返回错误提示log.debug("验证码错误.....");return Result.fail("手机号错误,请重新填写");}//3. 验证通过,生成验证码// RandomUtil 是一个工具包。该方法生成6位随机数String code = RandomUtil.randomNumbers(6);//4. 保存验证码到sessionsession.setAttribute("code",code);//5. 发送验证码log.debug("验证码发送成功,{}",code);return Result.ok();}
}
---------手机号正则---------------
public static final String PHONE_REGEX = "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";

2、短信验证码登录、注册

1.理论流程

用户收到了验证码,就可以进行登录或者注册了。
1.用户将手机号和验证码进行提交。
2.服务端进行校验验证码。从session中取出验证码进行比较
2.1比较失败,拒绝登录
2.2比较成功,说明验证码填写的是正确的,但是手机号不能确定是否正确。
3.根据手机号查询用户信息。用户是否存在
3.1如果不存在说明该用户第一次登录,那么为该用户注册成新的用户。然后在进行登录。
3.2该用户以及存在了,可也进行登录
4.登录-》保存用户信息。保存到session中。

2.代码操作:

-----------controller ---------/*** 登录功能* @RequestBody注解: 前端穿过来的是JSON格式,就必须用requestBody注解。并且实体类来接受* @param loginForm 登录参数,包含手机号、验证码;或者手机号、密码.* session:登录的时候需要把用户信息存到session,验证码校验也需要用到session*/@PostMapping("/login")public Result login(@RequestBody LoginFormDTO loginForm, HttpSession session){//  实现登录功能return userService.login(loginForm,session);}------------service-------------------------public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService/*** 进行登录功能* @param loginForm 前端传来的用户信息* @param session* @return*/@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1. 判断手机号格式是否正确String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)){//2.如果不正确则直接返回错误log.debug("手机号:{}错误",phone);Result.fail("手机号格式错误");}//3. 判断验证码是否一致,session中对比//从session中获取验证码,也就是后端生成的验证码Object cacheCode = session.getAttribute("code");//前端传过来的验证码String code = loginForm.getCode();//如果验证码为空,或者不一致,则返回验证码错误if (code ==null || cacheCode.toString().equals(code) ){log.debug("验证码:{}错误",code);Result.fail("验证码错误");}//4. 用户是否存在  select * from t_user where phone=?// 查询表t_user,当“表中字段”中有等于phone的信息,进行查询处理,查询一条。返回的是一个user实体类User user = query().eq("phone", phone).one();//5 如果用户不存在则创建用户,保存到数据库if (user == null){user = createUser(phone);}//5. 如果用户存在,保存到sessionsession.setAttribute("user",user);return Result.ok();}/*** 创建用户,添加到数据库中* @param phone* @return*/private User createUser(String phone) {User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));//将用户信息插入到 t_user表中save(user);return  user;}

3、校验登录状态

1.理论流程

用户登录成功后,在进行访问其他页面的的时候,需要进行校验登录状态。
1.基于session进行校验。 基于cookie中的session的id,拿到session。

之前将用户信息保存到了session。session是基于cookie,每个session都有一个id,保存在浏览器的cookie当中。
当用户来访问其他页面时,那么就会携带着自己的cookie,而cookie有session的id。那么就可以基于session中的id,拿到session。从而在session中获取用户信息。

2.判断session有无用户
2.1如果没有,则拦截请求
2.2如果有,则缓存用户信息,将用户信息保存到ThreadLocal。这样其他业务就可以直接从ThreadLoad中获取用户信息。

ThreadLoad是线程域对象,在业务当中,每一个请求到达微服务,都会是一个独立的线程。
ThreadLoad会将数据保存到每一个线程的内部,在线程内部创建一个map来保存。
每一个请求来了以后,都有独立的存储空间,请求之间相互没有干扰。

3.放行。

后续其他请求,就可以从ThreadLoad中取出自己的用户信息

前端在请求接口时,会携带cookie。而登录的凭证就是session的id,而该id是存在cookie其中。
那么服务端根据这个session的id得到session。
从session中获取用户信息。
在根据该用户信息去数据库判断,有无该用户,
如果没有则拦截。
如果有,则保护信息到ThreadLoad,在进行放行;

在springmvc中,拦截器可以在所有controller执行前,进行执行。
那么所有的请求,先经过拦截器,在进行访问各个controller。
拦截器:实现用户登录的校验。
–校验后:
那么后续的业务当中【比如说订单、点赞、评论】,都是需要用户的信息
所有说各个controller也需要的到用户信息。
那么就需要将拦截器拦截后得到的信息,传给各个controller。 需要注意的是:线程安全
–解解方法:ThreadLoad
当拦截器拦截了用户信息后,将用户信息保存到ThreadLoad。
ThreadLoad是一个线程域对象,每一个进入Tomcat的请求都是一个独立的线程。
ThreadLoad就会在线程内开一个内存空间,进行保存对应的用户。这样每个用户相互不干扰
– controller需要用到用户信息时,在从ThreadLoad中取出用户

2.代码操作:

-----------------拦截器:----------
package com.hmdp.utils;import com.hmdp.entity.User;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;/*** 用户登录拦截器* @Author Hu*/
public class LoginInterceptor implements HandlerInterceptor {/*** pre:前置拦截。 进入controller执行,进行校验。* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic 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. 如果存在则保存到ThreadLoadUserHolder.saveUser((User) user);//6. 放行return true;}/*** 渲染之后,返回用户之前。 用户执行完毕后,销毁对应的用户信息。* 避免用户泄露* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {UserHolder.removeUser();}
}-----------配置拦截器的configuration-----------
package com.hmdp.config;import com.hmdp.utils.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 配置拦截器* 可以让LoginInterceptor拦截器生效。**/
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {/*** 添加拦截器* @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {//添加拦截器InterceptorRegistration interceptorRegistration = registry.addInterceptor(new LoginInterceptor());// 添加配置可以放行哪些路径interceptorRegistration.excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login");}
}----------------controller类进行返回用户信息--------@GetMapping("/me")public Result me(){//  获取当前登录的用户并返回return Result.ok(UserHolder.getUser());}
----ThreadLoad类的信息------该信息是隐藏了用户信息累的实体类package com.hmdp.utils;import com.hmdp.dto.UserDTO; /*** 创建ThreadLoad*/
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();}
}

3.隐藏用户敏感信息

登录校验功能,返回的信息比较多。比如密码这些重要信息不应该进行返回到前端。

为什么前端收到了所有的用户信息?
从后往前推:

  1. controller接口:校验成功后,将用户信息返回给前端,返回的是User完整的信息。而user信息是从UserHolder中得到的
  2. 拦截器: 从session中获取的用户信息,就是完整的用户信息。直接通过UserHolder的save方法,将所有的用户信息,添加到了拦截器。
  3. 那么谁给session存的信息呢?是登录业务时:从数据库中查询了所有的用户信息。直接放到了session中。

解决方法:
4. 也就是上的第三步:将数据库查询出来的用户信息,放到新pojo类中,在放到session中。
5. 也就是第二步:获取session中的就是UserDTo了。不在是整个user用户信息了


改动的代码:

三、集群的session共享问题

session共享问题:多台Tomcat并不共享session共享空间,当请求切换到不同Tomcat服务时导致数据丢失的问题。

存储到ATomcat服务器时,在其他的Tomcat服务器中是看不到的。

四、基于redis实现共享session登录


1、发送验证码。如何将验证码存入到redis中?

  • redis的数据类型:用String类型
    验证码就是6位数字。
  • Redis的Key:手机号
    确保每一个手机号来验证时,保存的key都是不一样的。那么就用每一个手机号来作为redis的key。
    当登录时,取验证码时,将前端传过来的手机号作为key,来获取验证码,进行比较登录成功。
  • Redis的value:就是验证码

代码操作:

2、验证码登录。如何将用户信息保存到redis

  • 数据类型:hash
    保存的是用户信息,用户对象。
  • redis的key:随机token为key存储用户数据。比如用UUID来生成。
    key的要求:key唯一
  • redis的value:保存整个用户信息

String类型:把java对象序列化为Jason字符串,保存在redis。看起来简单,把整个JSON数据变成一个字符串了。占用内存多,除了保存自身外,还有JSON串的格式,比如冒号、大括号
hash类型:value是一个hashMap类型。把java中每一个字段保存在redis。每个字段是独立的,可以针对单个字段做CRUD。占用更少的内存。只需要保存数据本身就可以了。

代码操作:


-----用户登录service层---------- /*** 进行登录功能* @param loginForm 前端传来的用户信息* @param session* @return*/@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {//1. 判断手机号格式是否正确String phone = loginForm.getPhone();if (RegexUtils.isPhoneInvalid(phone)){//2.如果不正确则直接返回错误log.debug("手机号:{}错误",phone);Result.fail("手机号格式错误");}/*//3. 判断验证码是否一致,session中对比//从session中获取验证码,也就是上面方法获取验证码时,后端生成的验证码Object cacheCode = session.getAttribute("code");*/// 3. 从redis中获取验证码String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);String code = loginForm.getCode();// 4. 如果验证码为空,或者不一致,则返回验证码错误if (code ==null || !cacheCode.equals(code) ){log.debug("验证码:{}的错误",code);return Result.fail("验证码错误");}//5. 用户是否存在  select * from t_user where phone=?/*查询表t_user,当“表中字段”中有等于phone的信息,进行查询处理,查询一条。返回的是一个user实体类*/User user = query().eq("phone", phone).one();//6 如果用户不存在则创建用户,保存到数据库if (user == null){// 6.如果用户不存在则创建用户,保存到数据库user = createUser(phone);}/*//7. 如果用户存在,保存到session。// 通过hutool类中的对象,将用户的部分信息放到session中。session.setAttribute("user",BeanUtil.copyProperties(user, UserDTO.class)); */// 7.如果用户存在,以hash的形式保存到redis中// 7.1生成token,作为登录令牌String token = UUID.randomUUID().toString(true);//7.2 将User对象转为Hash存储. UserDTO是用户的部分信息,发送给前端即可。UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);/*** beanToMap是将user实体类转为HashMap类型* 而StringRedisTemplate<string,string>都是string类型。而userDTO中的id是Long类型,所以报错了* 允许对key/value进行自定义*  对字段值的修改器,修改字段值*/Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(), CopyOptions.create().setIgnoreNullValue(true) // 忽略空的值.setFieldValueEditor((fieldName,fieldVaule)->fieldVaule.toString()));//7.3 存储用户信息,保存到redis中。 key:token, key:value是用户信息String tokenKey = LOGIN_USER_KEY+ token;stringRedisTemplate.opsForHash().putAll(tokenKey,userMap);//7.4 为该key,设置有效期stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL, TimeUnit.MINUTES);// 8 将token,返回给前端return Result.ok(token);}/*** 创建用户,添加到数据库中* @param phone* @return*/private User createUser(String phone) {User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));//将用户信息插入到 t_user表中save(user);return  user;}

3、校验登录功能。获取用户信息中的token【hash的key】

将redis中token返回给前端。访问接口时就会携带这token来访问接口,服务端就会根据token,获取用户信息。在进行判断是否存在,能否访问接口等等校验功能,以及业务代码功能。

代码操作:

-------interceptor拦截器代码:-------------
package com.hmdp.utils;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;
import java.util.concurrent.TimeUnit;/*** 用户登录拦截器* @Author Hu*/
public class LoginInterceptor implements HandlerInterceptor {/*** pre:前置拦截。 进入controller执行,进行校验。* @param request* @param response* @param handler* @return* @throws Exception*/private StringRedisTemplate stringRedisTemplate;public LoginInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取session对象String token = request.getHeader("authorization");if (StrUtil.isBlank(token)){return false;}// 2. 基于token,获取用户信息String redisKey = RedisConstants.LOGIN_USER_KEY+token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(redisKey);//3. 判断用户是否存在if (userMap.isEmpty() ){//4. 如果不存在,则拦截response.setStatus(401);return false;}//5.将查询到的Hash类型数据,转换为userDTO对象UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 6.将userDTO对象,保存到ThreadLoadUserHolder.saveUser(userDTO);//7. 刷新token有效值stringRedisTemplate.expire(redisKey,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);// 8.放行return true;}/*** 渲染之后,返回用户之前。 用户执行完毕后,销毁对应的用户信息。* 避免用户泄露* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {UserHolder.removeUser();}
}

在拦截器中:不能使用@Aotuwired、@Resurces注解,自动导入。
因为interceptor拦截器类是手动new出来的,并不是@Component、@Configuration等注解来构建的。也就是说不是由spring来构建的,就不能进行依赖注入。
所以说通过构造函数注入,谁来注入呢?那就是谁用了类,谁就来注入该类。而MVC的configuration拦截器用到了。从而mvcConfig来帮助redisTemplate来注入到spring中。

4、测试:

五、登录拦截器的优化

用户访问任何一个页面,都会进行刷新登录token的倒计时:


----------拦截所有请求,都进行刷新token----------
package com.hmdp.utils;import cn.hutool.core.bean.BeanUtil;
import com.hmdp.dto.UserDTO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;public class RefreshTokenInterceptor implements HandlerInterceptor {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");//2. 根据token获取对象用户信息String redisKey = RedisConstants.LOGIN_USER_KEY+token;Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(redisKey);//3.判断用户是否存在if (userMap.isEmpty()){return true;}// 4. 将查询到的userMap转为UserDTOUserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);// 5. 存在,将用户信息存入ThreadLoadUserHolder.saveUser(userDTO);// 6. 刷新token有效期stringRedisTemplate.expire(redisKey,RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);// 7.放行return  true;}/*** 渲染之后,返回用户之前。 用户执行完毕后,销毁对应的用户信息。* 避免用户泄露* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {UserHolder.removeUser();}
}---------拦截部分请求,刷洗token---------
package com.hmdp.utils;import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 用户登录拦截器* @Author Hu*/
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 判断是否拦截: 查看ThreadLoad是否有用户if (UserHolder.getUser() == null){response.setStatus(401);return false;}// 8.放行return true;}}--------mvcConfig配置拦截器:
package com.hmdp.config;import com.hmdp.utils.LoginInterceptor;
import com.hmdp.utils.RefreshTokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 配置拦截器* 可以让LoginInterceptor拦截器生效。**/
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {/*** 添加拦截器* @param registry*/@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//添加登录拦截器。 拦截部分请求,优先级低InterceptorRegistration interceptorRegistration = registry.addInterceptor(new LoginInterceptor());// 添加配置可以放行哪些路径interceptorRegistration.excludePathPatterns("/shop/**","/voucher/**","/shop-type/**","/upload/**","/blog/hot","/user/code","/user/login").order(1);// 添加刷新拦截所有请求。 设置优先级高于 拦截部分请求registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0);}
}

redis的实战项目01_模拟短信登录业务相关推荐

  1. Redis实战篇一 (短信登录)

    Redis企业实战(黑马点评) 项目整体架构 项目部署 后端部署 前端部署 短信登陆 基于Session实现登录 集群的Session共享问题 基于Redis实现共享session登录 解决状态登录刷 ...

  2. Redis:黑马点评项目之用户短信登录

    一.表结构 建表sql语句: /*Navicat Premium Data TransferSource Server : localSource Server Type : MySQLSource ...

  3. ThinkPHP6项目基操(15.实战部分 阿里云短信redis)

    阿里云短信redis 一.安装阿里云短信SDK 二.封装到项目lib中 三.radis记录验证码 1. 安装redis服务 2. 可视化redis管理软件 3. PHP安装redis扩展 4. 配置缓 ...

  4. 黑马点评Redis实战(短信登录;商户查询缓存)

    黑马点评 通过一个类似于大众点评的项目了解学习redis在实战项目中的使用,下面是项目中会涉及到的模块: 一.导入黑马点评项目 导入springboot项目,导入sql脚本到数据库,开启nginx,更 ...

  5. Redis框架(三):大众点评项目 基于Session的短信登录

    大众点评项目 基于Session的短信登录 需求:基于Session实现短信验证登录 基于Session的短信登录 发送手机验证码 实现登录 (注意MyBatisP的接口使用) 新的问题 Spring ...

  6. Redis(五) - Redis企业实战之短信登录

    文章目录 一.导入黑马点评项目 1. 导入SQL 2. 前后端分离 3. 导入后端项目 3.1 将后端项目导入到 Idea 中 3.2 注意:修改application.yaml文件中的mysql.r ...

  7. 《Redis实战篇》一、短信登录

    1.1.导入黑马点评项目 1.1.1 .导入SQL 1.1.2.有关当前模型 手机或者app端发起请求,请求我们的nginx服务器,nginx基于七层模型走的事HTTP协议,可以实现基于Lua直接绕开 ...

  8. 1.Redis实战—短信登录

    短信登录: 基于Redis缓存: (1)发送短信验证码: 实现逻辑 : 先校验手机号 , 不符合 直接返回错误信息 , 使用的是封装的返回前端的方法 符合 , 生成一个随机验证码 , 使用的是huTo ...

  9. Redis实战——短信登录

    目录 1 基于Seesion实现短信登录 1.1 发送短信验证码 1.2 登录功能 2 使用Redis进行短信验证码校验登录 2.1 Seesion方法存在的问题 2.2 发送短信验证码 2.3  验 ...

最新文章

  1. linux系统文件分类,Linux系统文件概念和文件类型
  2. Magento用的哪个php框架,对magento MVC框架的了解
  3. 有关AutoCompleteBox组件的研究[5][Final]_集成搜索引擎搜索建议(Search Suggestion)——Silverlight学习笔记[40]...
  4. 关于 VB,VC,Delphi,SDK 的笑话
  5. Android 进行单元測试难在哪-part3
  6. SPSS视频教程大合集
  7. linux 桌面美化指南,Linux_9方面立体式地美化Ubuntu桌面,总结了一下桌面美化的设置。 - phpStudy...
  8. php设置个性域名,利用nginx泛域名解析配置二级域名和多域名,实现二级域名子站,用户个性独立子域名。...
  9. mac视频太大怎么压缩 苹果电脑怎么压缩视频大小的软件
  10. char *p = hello world!
  11. OpenCV开发笔记(六十二):红胖子8分钟带你深入了解亚像素角点检测(图文并茂+浅显易懂+程序源码)
  12. 多搜- 多个网站一起搜(舆情监控版)
  13. cannot find -lxxx问题
  14. python制作海报_用python制作“除夕夜倒计时”海报,新的一年你准备好了么?
  15. 基于FFmpeg的视频播放器之十二:seek
  16. 【优化】--Squid优化汇总
  17. 美摄SDK Alpha产品说明书
  18. JAVA17安装体验JFX17抢先体验
  19. c语言printf snm,vc++实现SNMP信息刺探程序
  20. 常用EMAIL的pop和smtp服务器地址

热门文章

  1. 2020CCPC威海 J.Steins;Game
  2. 如何做好生产物料管理
  3. tny278功能参数_电源管理芯片TNY278G
  4. Linux误删libc.so.6如何恢复
  5. SpringBoot不使用Quartz集成定时任务的两种方式
  6. 梯度下降法的不同形式及优缺点
  7. 基于MFC的纸牌游戏
  8. SmartImageView的简单使用
  9. 把标准C++视为一个新语言
  10. 梯度消亡--学习笔记