若依 Spring Security 短信,扫码登录

  1. 修改 LoginBody,添加登录类型字段

    @Data
    public class LoginBody {
    /**
    * 用户名
    */
    private String username;

    复制代码
     /**
      * 用户密码
      */
     private String password;
    
     /**
      * 验证码
      */
     private String code;
    
     /**
      * 唯一标识
      */
     private String uuid;
    
     /**
      * 登录类型
      */
     private String loginType;

    }

2.定义一个常量

复制代码
package com.ruoyi.common.core.domain.constant;

/**
 * @author Ls
 * @date 2024/6/4
 */
public class LoginTypeConstant {
    public static final String PASSWORD_LOGIN = "password";
    public static final String MOBILE_LOGIN = "mobile";
    public static final String QR_CODE_LOGIN = "qrCode";
}

3.修改登录接口

复制代码
    @PostMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody) {
        AjaxResult ajax = AjaxResult.success();
        loginBody.setLoginType(LoginTypeConstant.MOBILE_LOGIN);
        // 生成令牌
        String token = loginService.login(loginBody);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

4.重载登录实现类,基本不用改,loginbody 作为 credentials传入UsernamePasswordAuthenticationToken

复制代码
    public String login(LoginBody loginBody) {
        String username = loginBody.getUsername();
//        // 验证码校验
//        validateCaptcha(username, code, uuid);
//        // 登录前置校验
//        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        try {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, loginBody);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        } 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);
    }

5.自定义MyAuthenticationProvider类

复制代码
package com.ruoyi.framework.security.mobile;

import com.ruoyi.common.core.domain.constant.LoginTypeConstant;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.framework.web.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

/**
 * @author Ls
 * @date 2024/6/4
 */
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException, UserPasswordNotMatchException {
        String name = authentication.getName();
        LoginBody loginBody = (LoginBody) authentication.getCredentials();
        String loginType = loginBody.getLoginType();
        UserDetails user;
        switch (loginType) {
            case LoginTypeConstant.PASSWORD_LOGIN:
                user = userDetailsService.loadUserByUsername(name);
                String password = loginBody.getPassword();
                String encoderPassword = bCryptPasswordEncoder.encode(password);
                // 数据库账号密码的校验能通过就通过
                if (bCryptPasswordEncoder.matches(password, user.getPassword())) {
                    return new UsernamePasswordAuthenticationToken(user, encoderPassword);
                }
            case LoginTypeConstant.MOBILE_LOGIN:
                user = userDetailsService.loadUserByPhone(loginBody);
                return new UsernamePasswordAuthenticationToken(user, loginBody.getCode());
            case LoginTypeConstant.QR_CODE_LOGIN:
                break;
            default:
                break;
        }
        // 如果都登录不了,就返回异常输出
        throw new UserPasswordNotMatchException();
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

6.用户验证类新增手机号登录 UserDetailsServiceImpl

复制代码
    public UserDetails loadUserByPhone(LoginBody loginBody) throws UsernameNotFoundException {
        // TODO: 2024/6/4  各种业务效验,这里图省事验证码放在password
        if (!Objects.equals(loginBody.getPassword(), "123456")) {
            throw new ServiceException("验证码错误");
        }

        //这里应该是根据手机号查询,我这里图省事直接拿账号登录
        String username = loginBody.getUsername();
        SysUser user = userService.selectUserByUserName(username);
        return createLoginUser(user);
    }

7.SecurityConfig调整,配置一下MyAuthenticationProvider

复制代码
    /**
     * 自定义用户认证逻辑
     */
    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;


    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(myAuthenticationProvider);
    }
复制代码
相关推荐
天天被压力9 分钟前
【A股分钟级高频数据完全手册 #07】北交所历史分钟K线接口:京市Pro版分时批量
java·人工智能·python
天天被压力15 分钟前
【A股分钟级高频数据完全手册 #08】股票日、分时数据接口对比:普通分时与Pro版差异实测
java·人工智能·python
特立独行的猫A30 分钟前
AI 时代,我为什么推荐用 cj-tauri 开发桌面应用
java·后端·python
金銀銅鐵32 分钟前
从一个类名到一张类图:用反射自动生成 PlantUML
java·后端·uml
疯华整猫44 分钟前
被杀掉之后:我把 Android 的「唤起 + 常驻」重做了一遍
前端·面试·架构
nice_lcj5201 小时前
Java 并发容器入门:ConcurrentHashMap、CopyOnWriteArrayList、阻塞队列
java·面试·并发编程·阻塞队列·concurrenthashmap·copyonwritearraylist
微小的鱼1 小时前
Linux的渲染与显示系统链路
android·java·linux
若亦_2 小时前
Java中关于this的三种用法
java·开发语言·intellij-idea
山甫aa2 小时前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web
软件技术NINI2 小时前
基于SpringBoot和Vue的博物馆预约平台管理系统的设计与实现
java·毕业设计·毕设