RUOYI集成手机短信登录

2024-06-12 19:36

本文主要是介绍RUOYI集成手机短信登录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景:

工作过程中遇到需求,需要将短信验证码登录集成到RUOYI框架中。框架中使用的用户认证组件为Security,由于没有怎么研究过这个组件,这个功能不太会搞。所以这是一篇抄作业记录。参考文章如下

若依RuoYi整合短信验证码登录_若依短信登录-CSDN博客

任务需求描述请参考下面这篇文章

手机短信验证码登录-CSDN博客

需求分析:

根据需求描述,将需求分为以下几个工作任务:

1、需要有一个生成随机6位数验证码的方法

2、需要对接第三方平台将验证码发出去。(我这里对接的是阿里云)

3、对前端提供一个短信发送接口和手机号短信验证码登录接口

遇到的问题:

框架中只有提供用户名密码的登录认证方法,未提供手机号和验证码的认证方法。为前端提供的手机号短信验证码登录接口中无法使用用户名密码的这种认证方式。因此需要修改框架中的文件实现手机号和验证码的这种身份认证方法。

认证方法实现过程

想要实现新的身份认证方法就必须要搞明白Security这个组件的工作原理。参考如下文章

SpringBoot集成Spring Security(7)——认证流程_springboot整合springsecurity若依-CSDN博客

要实现整个过程必须要实现以下三个类

1、SmsCodeAuthenticationProvider 该类实现了AuthenticationProvider接口,用于实现自定义的认证逻辑

package com.ruoyi.framework.security.provider;import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.security.token.SmsCodeAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;public class SmsCodeAuthenticationProvider implements AuthenticationProvider {private UserDetailsService userDetailsService;public SmsCodeAuthenticationProvider() {}public SmsCodeAuthenticationProvider(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;String mobile = (String) authenticationToken.getPrincipal();String code = (String) authenticationToken.getCode();
//        checkSmsCode(mobile, code);UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);// 此时鉴权成功后,应当重新 new 一个拥有鉴权的 authenticationResult 返回SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities(), code);authenticationResult.setDetails(authenticationToken.getDetails());return authenticationResult;}private void checkSmsCode(String mobile, String code) {RedisCache redisCache = SpringUtils.getBean(RedisCache.class);String verifyKey = CacheConstants.SMS_CAPTCHA_CODE_KEY + mobile;String smsCode = redisCache.getCacheObject(verifyKey);if (smsCode == null) {throw new BadCredentialsException("验证码失效");}if (!code.equals(smsCode)) {throw new BadCredentialsException("验证码错误");}}@Overridepublic boolean supports(Class<?> authentication) {return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);}}

2、SmsCodeAuthenticationToken 该类继承了AbstractAuthenticationToken类,用来传递认证信息

package com.ruoyi.framework.security.token;import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;import java.util.Collection;public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;/*** 在 UsernamePasswordAuthenticationToken 中该字段代表登录的用户名,* 在这里就代表登录的手机号码*/private final Object principal;private final Object code;/*** 构建一个没有鉴权的 SmsCodeAuthenticationToken*/public SmsCodeAuthenticationToken(Object principal,Object code) {super(null);this.principal = principal;this.code = code;setAuthenticated(false);}/*** 构建拥有鉴权的 SmsCodeAuthenticationToken*/public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities, Object code) {super(authorities);this.principal = principal;this.code = code;// must use super, as we overridesuper.setAuthenticated(true);}@Overridepublic Object getCredentials() {return null;}@Overridepublic Object getPrincipal() {return this.principal;}public Object getCode() {return code;}@Overridepublic void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {if (isAuthenticated) {throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");}super.setAuthenticated(false);}@Overridepublic void eraseCredentials() {super.eraseCredentials();}
}

3、SmsUserDetailsServiceImpl 该类实现了UserDetailsService接口,在SmsCodeAuthenticationProvider 类的的认证过程中通过UserDetailsService接口回调到这个实现类,用来查询并创建登录用户信息。注意这个service必须有个名字,此处是“userDetailsByPhonenumber”

package com.ruoyi.framework.web.service;import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;@Service("userDetailsByPhonenumber")
public class SmsUserDetailsServiceImpl implements UserDetailsService {private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);@Autowiredprivate ISysUserService userService;@Autowiredprivate SysPermissionService permissionService;@Overridepublic UserDetails loadUserByUsername(String phone) throws UsernameNotFoundException {SysUser user = userService.selectUserByUserName(phone);if (StringUtils.isNull(user)){log.info("登录手机号:{} 不存在.", phone);throw new UsernameNotFoundException("登录手机号:" + phone + " 不存在");}else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){log.info("登录用户:{} 已被删除.", phone);throw new BaseException("对不起,您的账号:" + phone + " 已被删除");}else if (UserStatus.DISABLE.getCode().equals(user.getStatus())){log.info("登录用户:{} 已被停用.", phone);throw new BaseException("对不起,您的账号:" + phone + " 已停用");}return createLoginUser(user);}public UserDetails createLoginUser(SysUser user){return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));}
}

还要对以下几个文件进行修改

1、SecurityConfig 位于ruoyi-framework模块下的config文件夹中,是Security组件的配置类

①注入认证的服务,@Qualifier中指定了服务接口的实现类

②配置身份认证接口

2、UserDetailsServiceImpl 该类是若依框架中账号密码认证的实现类,需要给这个service起一个名字,用来区分手机号验证码的实现类,并且@Primary这个注解是必须要加,不然系统无法启动

手机号短信验证码登录接口的实现

在SysLoginService中实现手机号验证码登录验证方法

/*** 手机号登录验证** @param mobile* @return*/public String smsLogin(String mobile,String code){// 用户验证Authentication authentication = null;try{SmsCodeAuthenticationToken authenticationToken = new SmsCodeAuthenticationToken(mobile,code);AuthenticationContextHolder.setContext(authenticationToken);// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication = authenticationManager.authenticate(authenticationToken);}catch (Exception e){AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobile, Constants.LOGIN_FAIL, e.getMessage()));throw new CustomException(e.getMessage());}finally{AuthenticationContextHolder.clearContext();}AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobile, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));LoginUser loginUser = (LoginUser) authentication.getPrincipal();recordLoginInfo(loginUser.getUserId());if (!soloLogin){// 如果用户不允许多终端同时登录,清除缓存信息String userIdKey = Constants.LOGIN_USERID_KEY + loginUser.getUser().getUserId();String userKey = redisCache.getCacheObject(userIdKey);if (StringUtils.isNotEmpty(userKey)){redisCache.deleteObject(userIdKey);redisCache.deleteObject(userKey);}}// 生成tokenreturn tokenService.createToken(loginUser);}

总结:

通过实现不同的provider可以实现不同的登录验证方式,例如邮箱登录验证和扫码登录验证等。不同的provider需要实现对应的token类,将认证信息传递到security组件中,provider的实现类中实现对认证信息的校验。UserDetailsService接口的实现类,用来查询用户在系统中的信息,一些数据状态的验证可以在此接口的实现类中实现验证逻辑。

这篇关于RUOYI集成手机短信登录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1055136

相关文章

SpringBoot集成Netty,Handler中@Autowired注解为空

最近建了个技术交流群,然后好多小伙伴都问关于Netty的问题,尤其今天的问题最特殊,功能大概是要在Netty接收消息时把数据写入数据库,那个小伙伴用的是 Spring Boot + MyBatis + Netty,所以就碰到了Handler中@Autowired注解为空的问题 参考了一些大神的博文,Spring Boot非controller使用@Autowired注解注入为null的问题,得到

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

智慧环保一体化平台登录

据悉,在当今这个数字化、智能化的时代,环境保护工作也需要与时俱进,不断创新。朗观视觉智慧环保一体化平台应运而生,它利用先进的信息技术手段,为环保工作提供了更加便捷、高效的管理方式,成为推动绿色发展的重要力量。 一、智慧环保一体化平台的诞生背景 随着工业化进程的加快,环境污染问题日益严重,传统的环保管理模式已经难以满足现代社会的需求。为了提高环保工作的效率和质量,智慧环保一体化平台应运而

BD错误集锦8——在集成Spring MVC + MyBtis编写mapper文件时需要注意格式 You have an error in your SQL syntax

报错的文件 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yuan.dao.YuanUserDao"><!

BD错误集锦7——在集成Spring MVC + MyBtis时使用c3p0作为数据库时报错Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatem

异常信息如下: Type Exception ReportMessage Handler dispatch failed; nested exception is java.lang.AbstractMethodError: Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatement.isClosed()Z is abstractDescr

陀螺仪LSM6DSV16X与AI集成(8)----MotionFX库解析空间坐标

陀螺仪LSM6DSV16X与AI集成.8--MotionFX库解析空间坐标 概述视频教学样品申请源码下载开启CRC串口设置开启X-CUBE-MEMS1设置加速度和角速度量程速率选择设置FIFO速率设置FIFO时间戳批处理速率配置过滤链初始化定义MotionFX文件卡尔曼滤波算法主程序执行流程lsm6dsv16x_motion_fx_determin欧拉角简介演示 概述 本文将探讨

Tkinter和selenium结合实现登录UC后台,最后打包成exe

主要实现的功能:小号模式自动登录UC阿里汇川广告后台,屏蔽账号密码输入 主要用的技术:用Tkinter展示所有的广告账号界面,使用selenium控制谷歌浏览器,打开阿里汇川登录页,登录汇川后台。 第一次写,遇到的坑比较多,三天,搞定。给自己一个棒棒~☺️ import Tkinter as tk import osimport sysimport requestsfrom sel

Spring 集成 RabbitMQ 与其概念,消息持久化,ACK机制

目录 RabbitMQ 概念exchange交换机机制 什么是交换机binding?Direct Exchange交换机Topic Exchange交换机Fanout Exchange交换机Header Exchange交换机RabbitMQ 的 Hello - Demo(springboot实现)RabbitMQ 的 Hello Demo(spring xml实现)RabbitMQ 在生产环境

springboot集成prometheus监控

1 Maven pom.xml引入依赖 <dependency><groupId>io.prometheus</groupId><artifactId>simpleclient_spring_boot</artifactId></dependency> 2 启动类引入注解 import io.prometheus.client.spring.boot.EnablePrometheusEnd

在vue项目中集成cesium

首先创建一个新的vue项目 安装vite中cesium插件 https://github.com/nshen/vite-plugin-cesium 安装插件 npm i cesium vite-plugin-cesium vite -D 配置插件 注释原有样式 修改代码 效果