手机验证码+Redis登录验证+token+登录拦截

文章目录

  • 手机验证码+Redis登录验证+token+登录拦截
  • 解决方案
  • 思想
  • 以阿里云为例
    • 1.阿里云官网开通短信服务
    • 2.创建签名
    • 3.创建短信模板
    • 4.依赖短信服务的包
    • 5.编写发短信的工具类
    • 6.前端发送请求携带手机号到后端
    • 7.开启Redis
    • 8.后端接收数据
    • 9.token的使用
    • 10.使用工具类做到对象与字符串之间的转换
    • 11.前端用户收到验证码后发送登录请求
    • 12.每次请求都携带token
    • 13.定义后端拦截器
    • 14.定义异常处理类
    • 15.前端验证状态码
    • 16.用户数据

解决方案

  • 各种云平台
  • 各种数据商
  • 各种第三方的延伸产品

思想

  • 用户使用手机验证码登录
  • 前端点击按钮通过第三方云平台发送短信验证码
  • 在发送验证码前先判断用户是否存在
  • 如果用户存在则发送验证码,同时把验证码存入redis中,key为手机号value为随机验证码
  • 用户收到验证码后点击登录进行登录验证
  • 通过前端传的手机号在redis中查询数据,判断验证码是否匹配
  • 如果匹配则登录成功,同时清除redis中的验证码数据
  • 此时生成一串uuid
  • 首先将uuid作为key,用户信息作为value存入redis中,以免前端保存用户信息不安全
  • 再把uuid作为参数生成token传到前端,然后再把token保存至浏览器中
  • 定义一个拦截器
  • 前端每次请求时都在请求头中携带token信息
  • 后端定义过滤器,判断请求头中携带token是否正确
  • 如果判断错误则抛出异常,定义异常处理类返回状态码
  • 状态码如果未登录则在前端跳转至登录页面,如果登录状态则正常访问

以阿里云为例

1.阿里云官网开通短信服务

2.创建签名

3.创建短信模板

4.依赖短信服务的包

<dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.1</version></dependency>

5.编写发短信的工具类

public class SMSUtils {//参数一表示接收短信的手机号
//参数二表示发送的验证码public static void sendSms(String phone, String code) {try {//加载外部配置文件InputStream resourceAsStream = SMSUtils.class.getClassLoader().getResourceAsStream("sms.properties");Properties properties = new Properties();properties.load(resourceAsStream);//初始化发送人com.aliyun.dysmsapi20170525.Client client = createClient(properties.getProperty("accessKeyId"), properties.getProperty("accessKeySecret"));SendSmsRequest sendSmsRequest = new SendSmsRequest()//要发送的手机号.setPhoneNumbers(phone)//签名.setSignName("xxxx")//模板编号.setTemplateCode("SMS_173425292")//发送的验证码.setTemplateParam("{\"code\":" + code + "}");//发送短信验证码client.sendSms(sendSmsRequest);} catch (Exception e) {e.printStackTrace();}}public static com.aliyun.dysmsapi20170525.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {Config config = new Config()// 您的AccessKey ID.setAccessKeyId(accessKeyId)// 您的AccessKey Secret.setAccessKeySecret(accessKeySecret);// 访问的域名config.endpoint = "dysmsapi.aliyuncs.com";return new com.aliyun.dysmsapi20170525.Client(config);}}

6.前端发送请求携带手机号到后端

7.开启Redis

  • 导入jedis的依赖
 <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version></dependency>
  • 编写redis工具类
public class RedisUtils {private static JedisPool jedisPool;//初始化连接池static {InputStream resourceAsStream = RedisUtils.class.getClassLoader().getResourceAsStream("redis.properties");Properties properties = new Properties();try {properties.load(resourceAsStream);} catch (IOException e) {e.printStackTrace();}//初始化连接池的各种属性JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(Integer.parseInt(properties.getProperty("maxTotal")));jedisPoolConfig.setMaxIdle(Integer.parseInt(properties.getProperty("maxIdle")));jedisPool = new JedisPool(jedisPoolConfig, properties.getProperty("host"), Integer.parseInt(properties.getProperty("port")));}//从连接池中取出一个jedispublic static Jedis getJedis(){return jedisPool.getResource();}//存值得方法public static void  setCache(String key,String value,String redisKey){Jedis jedis = getJedis();//存入redis中值jedis.set(redisKey+key,value);//把连接放回连接池jedis.close();}//取值的方法public static String getCache(String key,String redisKey){Jedis jedis = getJedis();//以key取redis中的值String s = jedis.get(redisKey+key);jedis.close();//返回取出的值return s;}
}

8.后端接收数据

 @GetMapping("getCode")public AxiosResult<Void> getCode(String phone){//通过手机号查询数据库验证用户是否存在User userByPhone = userService.findUserByPhone(phone);if (userByPhone!=null){//如果数据存在则生成6位随机数字验证码int code =(int) (Math.random()*(999999-100000+1)+100000);//先把验证码存入redis中,key为前端传入的手机号,value为随机数字验证码RedisUtils.setCache(phone,code+"",RedisKey.PREFIX_KEY);//调用工具类发送短信验证码SMSUtils.sendSms(phone,code+"");return AxiosResult.success();}return AxiosResult.error();}

9.token的使用

  • 引入依赖
         <dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.10.3</version></dependency>
  • 编写token工具类
@Component
public class TokenUtils {//自定义秘钥private static final String SECRET = "0x6c717a";//生成tokenpublic String getToken(String uuid) {try {//参数为自定义秘钥Algorithm algorithm = Algorithm.HMAC256(SECRET);String token = JWT.create()//签名.withIssuer("阿飞")//token中携带的值.withClaim("uuid", uuid).sign(algorithm);return token;} catch (JWTCreationException exception) {//Invalid Signing configuration / Couldn't convert Claims.}return "";}//解析tokenpublic String verifyToken(String token){try {                           //秘钥Algorithm algorithm = Algorithm.HMAC256(SECRET);JWTVerifier verifier = JWT.require(algorithm)//签名.withIssuer("阿飞").build(); //Reusable verifier instance//解析tokenDecodedJWT jwt = verifier.verify(token);//通过生成token是存入参数的key取出Claim uuid = jwt.getClaim("uuid");//返回从token中解析出的值return uuid.asString();} catch (JWTVerificationException exception){System.out.println(exception.toString());}return "";}
}

10.使用工具类做到对象与字符串之间的转换

  • 依赖jackson的包
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><scope>provided</scope></dependency>
  • 创建工具类
public class JsonUtils {private static ObjectMapper objectMapper = new ObjectMapper();//对象转字符串方法public static String Obj2Str(Object o) {try {String s = objectMapper.writeValueAsString(o);return s;} catch (JsonProcessingException e) {e.printStackTrace();}return "";}//字符串转对象方法public static <T>T Str2Obj( String str, Class<T> clazz){try {T t = objectMapper.readValue(str, clazz);return t;} catch (IOException e) {e.printStackTrace();}return null;}
}

11.前端用户收到验证码后发送登录请求

 @PostMapping("doLogin")public AxiosResult<String> doLogin(String phone,String code){//通过前端传来的手机号从redis中取值String cache = RedisUtils.getCache(phone, RedisKey.PREFIX_KEY);//判断redis中的值与前端传来的验证码是否一致if(code.equals(cache)){//通过手机号查询数据库查出用户信息User user = userService.findUserByPhone(phone);//生成一个uuidString uuid = UUID.randomUUID().toString();//把用户信息转成字符串存入redis中RedisUtils.setCache(uuid, JSONUtils.obj2str(user),RedisKey.LOGIN_USER);//传入uuid生成token返回前端String token = TokenUtils.getToken(uuid);return AxiosResult.success(token);}return AxiosResult.error();}

12.每次请求都携带token

axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么config.baseURL = 'http://localhost:8080/';//添加请求头Authorization: Bearer <token>格式config.headers.Authorization="Bearer "+localStorage.getItem("token");return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});

13.定义后端拦截器

  • 编写java代码
//创建一个类实现HandlerInterceptor接口
public class LoginInterceptor implements HandlerInterceptor {@Override//重写本方法public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求头中的数据String authorization = request.getHeader("Authorization");//判断token是否合法,如果不合法则抛出自定义异常,合法则返回true继续执行代码if(StringUtils.isEmpty(authorization)){throw new MyException(EnumStatus.NO_LOGIN);}if(!authorization.startsWith("Bearer ")){throw new MyException(EnumStatus.NO_LOGIN);}String[] s = authorization.split(" ");if (s[1].equals("null")){throw new MyException(EnumStatus.NO_LOGIN);}String s1 = s[1];DecodedJWT decodedJWT = TokenUtils.verifierToken(s1);if (decodedJWT==null){throw new MyException(EnumStatus.NO_LOGIN);}return true;}
}
  • 配置拦截器

配置文件的方式

<!-- 在spring的xml配置文件中配置拦截器-->
<mvc:interceptors><mvc:interceptor><!--要拦截的请求路径--><mvc:mapping path="/**"/><!--放行的请求,一般登录请求和获取验证码的请求要放行--><mvc:exclude-mapping path="/doLogin"/><mvc:exclude-mapping path="/getCode"/><!--配置拦截器的路径--><bean class="com.qy28.sm.interceptor.LoginInterceptor"/></mvc:interceptor></mvc:interceptors>

注解的方式

@Configuration
@ComponentScan(basePackages = {"com.qy28.cn"}, excludeFilters = {@ComponentScan.Filter(Service.class)
})
@EnableWebMvc
public class ControllerConfig implements WebMvcConfigurer {//使用@Autowired要先把拦截器放入容器中,如果不在容器中可以直接new@Autowiredprivate LoginInterceptor loginInterceptor;@Override//重写本方法配置拦截器public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/common/doLogin","/common/verifyCode","/common/getActivateEmail","/employee/doActivate");}
}

14.定义异常处理类

//类上添加@RestControllerAdvice注解
@RestControllerAdvice
public class MyExceptionHandler {//方法上添加@ExceptionHandler注解参数为 自定义异常.class@ExceptionHandler(HttpException.class)public AxiosResult<Void> myHandler(MyException e) {//捕捉到异常后返回前端状态码return AxiosResult.error(e.getEnumStatus());}
}

15.前端验证状态码

axios.interceptors.response.use(function (response) {// 对响应数据做点什let {status, data, massage} = response.data;if (status == 200) {return data;} else if(status==305){//如果未登录则跳转至登录页面location.replace(`../login.html`)}else {if(status==303){$("#activate").css("display","block");}$(document).Toasts('create', {body: massage,title: '登录失败',icon: 'fas fa-envelope fa-lg',})}return Promise.reject(false);
}, function (error) {// 对响应错误做点什么return Promise.reject(error);
});

16.用户数据

如果之后的请求逻辑需要用到用户信息的话,直接从请求头中获取token,然后通过工具类解析token,从reids中取用户信息,可以减少与数据库的交互,也保证了用户信息的安全

//抽取成一个方法
public static User getUser(){//通过请求从请求头中获取tokenString token = ServletUtils.getRequest().getHeader("Authorization").split(" ")[1];//解析token获取redis中的keyString uuid = verifierToken(token).getClaim("UUID").asString();//通过key获取数据String cache = RedisUtils.getCache(uuid, RedisKey.LOGIN_USER);//字符串转对象User user = JSONUtils.str2Obj(cache, User.class);return user;}

手机验证码+Redis登录验证+token+登录拦截相关推荐

  1. 手机短信注册验证与登录功能

    文章目录 一.前言 二.准备工作 三.发布短信 1.基本测试发布 ①可-使用测试模板进行调试 ②测试结果 ③注意,可能会调试失败,是因为没有余额.进入首页点击头像>进入余额充值:一条大概4分钱 ...

  2. 前端登录验证(Token)

    1.登录验证(token) 请求需要登录态标识的接口时,会先判断本地是否存储的有token. ①如果是客户端首次登陆, 会将用户密码发送给服务器端, 服务器判断用户密码是否正确, 如果验证正确的话返回 ...

  3. Spring Security自定义登录验证及登录返回结果

    Spring Security自定义登录验证及登录返回结果 一.功能描述 二.处理逻辑 简单流程 自定义UserDetails 自定义UserDetailsDAO 自定义UserDetailsServ ...

  4. java SpringBoot登录验证token拦截器

    用户访问接口验证,如果用户没有登录,则不让他访问除登录外的任何接口. 实现思路: 1.前端登录,后端创建token(通过JWT这个依赖),返给前端 2.前端访问其他接口,传递token,后端判断tok ...

  5. SpringSecurity:密码登录与token登录过程理解

    1. 用户名密码登录 登录成功 输入用户名.密码点击登录后,我看到的流程 (1)先经过UsernamePasswordAuthenticationFilter中的attemptAuthenticati ...

  6. mysql 登录验证_MySQL登录验证方式

    前些天接到一个客户发来的信息:自建MySQL实例之后,使用mysql -uroot@'192.168.3.6' 无法登录,密码没有问题:后面将所有其他不相干的mysql.user表数据删除后,可以正常 ...

  7. 文件的下载,随机验证码(无验证)登录注册

    文件的下载 文件下载的实质就是文件拷贝,将文件从服务器端拷贝到浏览器端. package com.oracle.demo02;import java.io.FileInputStream; impor ...

  8. php web 登录验证,Web登录Authorization验证

    通过Http访问服务器的一个服务软件的数据,浏览器会弹出以下验证弹框: 由于想用PHP curl方法获取数据,但不知道怎么设置用户名和密码验证参数: 这样发现不起作用: curl_setopt($cu ...

  9. 手撸SSO单点登录(四)登录验证-首次登录

    一.目标 前一章节讲解了各应用未登录的系统统一跳转至SSO统一登录页面.当输入用户名.密码后点击登录流程是怎么实现的.这一章节的目标主要是讲解OA(这里代表client.sso.com:8082)经过 ...

最新文章

  1. linux脚本开机自动执行脚本文件,如何让linux开机自动执行sata里头的脚本文件
  2. sklearn中的正则化
  3. 介绍一个功能强大的 Visual Studio Code 扩展 - Rest Client,能部分替代 Postman
  4. api接口返回动态的json格式?我太难了,尝试一下 linq to json
  5. 景点门票销售系统系统java_某景点门票销售管理系统
  6. centos中使用rpm包或yum命令在线安装的软件默认是安装在那个目录下
  7. 虚拟机网络连接三种方式(桥接、NAT、主机)
  8. 自旋锁:pthread_spinlock_t,互斥锁:pthread_mutex_t,条件变量:pthread_cond_t,读写锁:pthread_rwlock_t
  9. cmd复制文件到其他目录
  10. word批量替换向下箭头为回车符号、批量删除空行、批量空格与空行
  11. 测试用例设计方法_正交实验法(游戏向)
  12. Web前端js实现tif文件浏览(含多页tif)
  13. 【关于NI CAN USB-8473在实际应用中的案例分析】
  14. 2021年1月编程语言排行榜(C语言又第一名)
  15. 中国2016最热门应用和技术盘点,看到第3条就惊呆了!
  16. powerbi中的earlier函数
  17. 大数据如何帮助小数据? 主题报告速记与评述(二)
  18. 数据库的应用(私有云)
  19. 蓝桥 10 基础练习 十进制转十六进制 python
  20. 缴纳个人所得税标准是多少

热门文章

  1. 无法启动游戏 计算机丢失,win7系统无法启动游戏提示缺少vcomp100.dll文件怎么办...
  2. 商业智能BI - 派可数据商业智能BI可视化分析平台
  3. Miracast技术详解(一):Wi-Fi Display
  4. Endnote中设置期刊名缩写
  5. 博文总集(三) 之网工进阶
  6. 房型图户型图识别解析服务1.0版本发布
  7. Intergraph CADWorx (Plant, PID, Equipment, IP, SpecEditor) 2014 build 23.07.2013
  8. STP——BPDU格式及字段说明
  9. MyZip pro专业极速解压放弃了Mac上落后的拖拽解压压缩模式,采用『右键菜单』进行压缩、解压,极大的符合了用户最初的使用习惯。
  10. TPshop开源商城如何搭建环境(在虚拟机linux中)