【若依RuoYi短信验证码登录】汇总

这篇具有很好参考价值的文章主要介绍了【若依RuoYi短信验证码登录】汇总。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

遇到一个场景,需要同时支持手机号或者邮箱和密码或者验证码进行登录的场景,故来记录一下。

说明:此流程主要是基于若依框架集成的多种方式登录,主要演示登录业务逻辑和前端登录密码和验证码切换组件和配置Security

一:后端登录业务逻辑代码:

因为有多个端,多个语言共享登录接口,所以,接口定义尽量简单,接口内的逻辑判断尽量全面,判断手机号还是邮箱登录,再判断密码还是验证码登录,验证完了之后,再去验证用户是否存在数据库中,如果是密码登录的,则需要对比密码,然后再创建一个登录的token,返回。

 public AjaxResult login(LoginBody loginBody){
        //验证手机号和邮箱是否符合格式或者是否为空
        boolean isPhone = false;
        //先判断是手机号还是邮箱登录
        if(StringUtils.isNotEmpty(loginBody.getTel()) && Pattern.compile("^[1][1,2,3,4,5,6,7,8,9][0-9]{9}$").matcher(loginBody.getTel()).matches()){
            isPhone = true;
        }else if(StringUtils.isNotEmpty(loginBody.getEmail()) && loginBody.getEmail().matches("\\w{1,30}@[a-zA-Z0-9]{2,20}(\\.[a-zA-Z0-9]{2,20}){1,2}")){
            isPhone = false;
        }else{
            return AjaxResult.error("登录失败,邮箱和手机号不能同时为空!");
        }

        //在判断是密码还是验证码登录
        boolean isPassword = false;
        if(StringUtils.isNotEmpty(loginBody.getPassword())){
            isPassword = true;
        }else if(StringUtils.isNotEmpty(loginBody.getCode())){
            isPassword = false;
        }else{
            return AjaxResult.error("登录失败,密码和验证码不能同时为空!");
        }
		//验证码验证
        if(!isPassword){
            String codeKey = "0:" + isPhone? loginBody.getTel(): loginBody.getEmail());
            String value = redisCache.getCacheObject(codeKey);
            if (StringUtils.isNotEmpty(value)) {
                if (!value.equals(loginBody.getCode())) {
                    return AjaxResult.error("验证码错误!");
                }
            }else{
                return AjaxResult.error("验证码超时!");
            }
        }
  		// 用户验证
        Authentication authentication = null;
        try
        {
			if(isPassword){
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(isPhone? loginBody.getTel(): loginBody.getEmail(), loginBody.getPassword());
                AuthenticationContextHolder.setContext(authenticationToken);
                // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
                authentication = authenticationManager.authenticate(authenticationToken);
            }else{
                // 该方法会去调用UsernamePhoneUserDetailsServiceImpl.loadUserByUsername
                authentication = authenticationManager.authenticate(new UsernamePhoneAuthenticationToken(isPhone? loginBody.getTel(): loginBody.getEmail()));
            }        
		}
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }

        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
}
二:前端登录密码和验证码切换组件:
<template>
  <div class="login">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
      <h3 class="title">后台管理系统</h3>
      <el-form-item prop="loginP1" v-if="!isSmsLogin">
        <el-input v-model="loginForm.loginP1" type="text" auto-complete="off" placeholder="请输入手机号/邮箱" >
          <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="password" v-if="!isSmsLogin">
        <el-input
          v-model="loginForm.password"
          type="password"
          auto-complete="off"
          placeholder="请输入密码"
          @keyup.enter.native="handleLogin"
        >
          <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="loginP2" v-if="isSmsLogin">
        <el-input v-model="loginForm.loginP2" type="text" auto-complete="off" placeholder="请输入手机号/邮箱">
          <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
        </el-input>
      </el-form-item>
      <el-form-item prop="code" v-if="isSmsLogin">
        <el-input
          v-model="loginForm.code"
          auto-complete="off"
          placeholder="验证码"
          style="width: 63%"
          @keyup.enter.native="handleLogin"
        >
          <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
        </el-input>
        <div class="login-code">
          <el-button round @click.native.prevent="getSmsCode">{{computeTime>0 ? `(${computeTime}s)已发送` : '获取验证码'}}</el-button>
        </div>
      </el-form-item>
      <el-row>
        <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">{{isSmsLogin ? '记住手机号/邮箱' : '记住密码'}}</el-checkbox>
        <div class="sms-login">
          <el-button
            size="mini"
            type="text"
            @click.native.prevent="loginMethod"
          >
            <span v-if="isSmsLogin">账号密码登录</span>
            <span v-else>验证码登录</span>
          </el-button>
        </div>
      </el-row>
      <el-form-item style="width:100%;">
        <el-button
          :loading="loading"
          size="medium"
          type="primary"
          style="width:100%;"
          @click.native.prevent="handleLogin"
        >
          <span v-if="!loading">登 录</span>
          <span v-else>登 录 中...</span>
        </el-button>
        <div style="float: right;" v-if="register">
          <router-link class="link-type" :to="'/register'">立即注册</router-link>
        </div>
      </el-form-item>
    </el-form>
    <!--  底部  -->
    <div class="el-login-footer">
      <span>Copyright © 2018-2022 xiaoqiang All Rights Reserved.</span>
    </div>
  </div>
</template>
三:配置Security:

按照Security的流程图可知,实现多种方式登录,只需要重写三个主要的组件,第一个用户认证处理过滤器,第二个用户认证token类,第三个,自定义短信登录身份认证。
若依登录验证码,java Spring,java,数据库,服务器,java

1.参考UsernamePasswordAuthenticationToken类,继承AbstractAuthenticationToken,重写以下几个方法,自定义短信登录token验证。

/**
 * 自定义短信登录token验证
 */
public class UsernamePhoneAuthenticationToken extends AbstractAuthenticationToken {

    /**
     * 手机号
     */
    private final Object principal;

    public UsernamePhoneAuthenticationToken(Object principals){
        super(null);
        this.principal = principals;
        setAuthenticated(false);
    }

    public UsernamePhoneAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities){
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }

    @Override
    public 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);
    }

    @Override
    public void eraseCredentials(){
        super.eraseCredentials();
    }
  1. 重写UserDetailsService类的loadUserByUsername方法,实现用户验证处理。
/**
 * 用户验证处理
 */
@Service("userDetailsByPhone")
public class UsernamePhoneUserDetailsServiceImpl implements UserDetailsService {

    private static final Logger logger = LoggerFactory.getLogger(UsernamePhoneUserDetailsServiceImpl.class);

    @Autowired
    private ISysUserService userService;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUser;
        if(Pattern.compile("^[1][1,2,3,4,5,6,7,8,9][0-9]{9}$").matcher(username).matches()){
            sysUser = sysUserMapper.selectUserByTel(username);
        }else if(username.matches("\\w{1,30}@[a-zA-Z0-9]{2,20}(\\.[a-zA-Z0-9]{2,20}){1,2}")){
            sysUser = sysUserMapper.selectUserByEmail(username);
        }else{
            throw new ServiceException("请使用手机号或者邮箱进行登录!");
        }

        if(StringUtils.isNull(sysUser)){
            logger.info("登录用户:{} 不存在.", username);
            throw new ServiceException("登录用户:" + username+ " 不存在");
        }
        return createLoginUser(sysUser);
    }

    public UserDetails createLoginUser(SysUser user){
        return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
    }

3.注意,此时会有两个用户验证的处理类,一个是原来的UserDetailsServiceImpl,另一个是现在的UsernamePhoneUserDetailsServiceImpl,需要去SecurityConfig配置类去配置不同的用户认证业务类,通过@Qualifer指定注入的bean。

/**
 * spring security配置
 * 
 * @author victor_zhang
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    /**
     * 自定义用户认证逻辑(账号密码)
     */
    @Autowired
    @Qualifier("userDetailsByPass")
    private UserDetailsService userDetailsService;

    /**
     * 自定义用户认证逻辑(手机号验证码)
     */
    @Autowired
    @Qualifier("userDetailsByPhone")
    private UserDetailsService userDetailsByPhone;

	//此处省略若干代码......

}

4.自定义一个短信登录的身份鉴权, UserDetailsService 只负责根据用户名返回用户信息,AuthenticationProvider负责将 UserDetails 组装成 Authentication 向调用者返回。

/**
 * 自定义短信登录身份认证
 */
public class UsernamePhoneAuthenticationProvider implements AuthenticationProvider {

    private UserDetailsService userDetailsService;

    public UsernamePhoneAuthenticationProvider(UserDetailsService userDetailsService){
        setUserDetailsService(userDetailsService);
    }

    /**
     * 重写authentication方法,实现身份验证逻辑
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePhoneAuthenticationToken authenticationToken = (UsernamePhoneAuthenticationToken) authentication;
        String phone = (String) authenticationToken.getPrincipal();
        //委托 UserDetailsService 查找系统用户
        UserDetails userDetails = userDetailsService.loadUserByUsername(phone);
        //鉴权成功,返回一个拥有鉴权的AbstractAuthenticationToken
        UsernamePhoneAuthenticationToken authenticationTokenRes = new UsernamePhoneAuthenticationToken(userDetails, userDetails.getAuthorities());
        authenticationTokenRes.setDetails(authenticationToken.getDetails());
        return authenticationTokenRes;
    }

    /**
     * 重写supports方法,指定此AuthenticationProvider 仅支持短信验证码身份验证
     */
    @Override
    public boolean supports(Class<?> authentication){
        return UsernamePhoneAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

5.配置SecurityConfig 的configure方法

/**
 * spring security配置
 * 
 * @author victor_zhang
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    /**
     * 自定义用户认证逻辑(账号密码)
     */
    @Autowired
    @Qualifier("userDetailsByPass")
    private UserDetailsService userDetailsService;

    /**
     * 自定义用户认证逻辑(手机号验证码)
     */
    @Autowired
    @Qualifier("userDetailsByPhone")
    private UserDetailsService userDetailsByPhone;

    /**
     * 认证失败处理类
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;

    /**
     * 退出处理类
     */
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * token认证过滤器
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;
    
    //此处省略n行代码......

    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        //手机或邮箱的验证码的验证
        auth.authenticationProvider(new UsernamePhoneAuthenticationProvider(userDetailsByPhone));
        //账号密码的验证       
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

大概就这么多,如果有更好的方式,欢迎交流。文章来源地址https://www.toymoban.com/news/detail-680212.html

到了这里,关于【若依RuoYi短信验证码登录】汇总的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Security 6.x 系列【15】认证篇之实现短信验证码登录功能

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.0.4 本系列Spring Security 版本 6.0.2 源码地址:https://gitee.com/pearl-organization/study-spring-security-demo 目前大部分网站都支持使用 手机号+短信验证码 登录,比

    2024年02月05日
    浏览(70)
  • Ruoyi若依前后端分离框架【若依登录详细过程】

    后端包含ruoyi-admin,ruoyi-common,ruoyi-framework等多个模块,ruoyi-admin为启动模块。先看一下ruoyi-admin/src/main/application.yml配置文件。 指定了服务端启动的端口8080。我们运行ruoyi-admin/src/main/java/com/ruoyi/ RuoYiApplication.java即可启动后端,监听8080端口。 我们回到前端的登录界面。 views

    2024年02月05日
    浏览(69)
  • 若依(Ruoyi-Vue-Plus版)——1.登录(SaToken)

    这里学习一下若依框架的一个扩展版本: RuoYi-Vue-Plus: 后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token+Mybatis-Plus+Jackson+Xxl-Job+SpringDoc+Hutool+OSS 定期同步 (gitee.com) 官方文档:文档预览 - Gitee.com 项目有关SaToken登录写在最后,可跳过前面基本介绍部分。。。。 利用SaToken框架登录。

    2023年04月24日
    浏览(77)
  • 【ruoyi】微服务关闭登录验证码

    登录本地的nacos服务,修改:配置管理-配置列表-ruoyi-gateway-dev.yml 将验证码的enabled设置成false,即可

    2024年01月22日
    浏览(33)
  • SpringBoot——短信发送、手机验证码登录

    目录 一、短信发送 1.1 阿里云短信服务 1.1.1 设置短信签名 1.1.2 模板管理 1.1.3 设置AccessKey 1.2 短信发送——代码开发 1.2.1 导入maven坐标 1.2.2 调用API 1.2  手机验证码登录 1.2.1 用户数据库表 1.2.2  修改过滤器 1.2.3   随机生成验证码的工具类 1.2.4 手机验证码登录-- 发送验证码

    2023年04月22日
    浏览(44)
  • Java--用户登录/注册界面(连接Mysql数据库)并可以通过验证码登录

    1 效果展示 (1)登录界面 (2)注册界面 (3)动图展示 2 内容说明 (1)开发前,需引入一个连接Mysql 数据库驱动mysql-connector-java-5.1.30-bin.jar包 提取码:6666 (2)构建路径 (3)需要下载xampp软件 xampp软件下载 提取码:2255 xampp软件包含 Apache Web服务器、 Mysql Web服务器、Filezilla

    2024年02月09日
    浏览(63)
  • uni-app 之 短信验证码登录

    uni-app 之 短信验证码登录 image.png image.png

    2024年02月07日
    浏览(52)
  • TP6----------阿里云短信包验证码登录

    首先开通阿里云短信包,之后申请短信包签名,这里大家自行去阿里云申请 安装阿里云sdk 首先创建Sample类方便调用,我们需要有短信签名,签名模板,阿里云keyId和accessKeySecret 阿里云key在阿里云首页,右上角有个acesskey管理就可以看到 Sample.php文件 业务逻辑层生成随机验证码

    2024年02月15日
    浏览(67)
  • 手机短信验证码登录功能的开发实录(机器识别码、短信限流、错误提示、发送验证码倒计时60秒)

    短信验证码是通过发送验证码到手机的一种有效的验证码系统,作为比较准确和安全地保证购物的安全性,验证用户的正确性的一种手段,几乎网站登录都会使用该方式。 其特点是依据某些验证码接入商提供手机短信验证码服务,各网站通过接口发送请求到接入商的服务器,

    2024年02月02日
    浏览(47)
  • Java实现连接数据库验证登录和注册(附详细知识点讲解)

    学完Java基础后,一般会做个项目练手(上一篇博客有讲到 Java在线聊天室课程设计 ) 当中肯定会涉及到 登录验证 ,但没学过数据库 😅,不知道如何操作;只能把用户账户密码预存在一个txt文本当中,然后通过IO流读取验证 ⭐ 最后去搜相应的资料和网课进行学习,现在问题

    2024年02月02日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包