ruoyi登录功能源码分析

Ruoyi登录功能源码分析

上一篇文章我们分析了一下若依登录验证码生成的代码,今天我们来分析一下登录功能的代码

1、发送登录请求

前端通过http://localhost/dev-api/login向后端发送登录请求并携带用户的登录表单

在后端中的com.ruoyi.web.controller.system包下的SysLoginController响应登录信息,后端接受到请求后首先将操作信息设置为成功,然后对用户的登录表单进行校验,校验通过返回token

在这里插入图片描述

2、登录校验

接下来我们来看一看登录校验的代码

   /**
     * 校验验证码
     * 
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        // 判断当前是否需要生成验证码
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        if (captchaEnabled)
        {
            // 根据生成验证码时返回给前端的uuid拼接验证码在redis中的key值
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
            // 从redis中获取验证码
            String captcha = redisCache.getCacheObject(verifyKey);
            // 删除验证码
            redisCache.deleteObject(verifyKey);
            if (captcha == null)
            {
                // 开启异步任务记录用户信息
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                throw new CaptchaExpireException();
            }
            if (!code.equalsIgnoreCase(captcha))
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                throw new CaptchaException();
            }
        }
    }

2.1 前置校验

后端在进行登录校验时首先会对登录验证码进行校验然后会对用户名和密码进行前置校验,验证码校验上一篇文章中已经说过所以这一章不再赘述

登录前置校验会调用loginPreCheck函数

        // 验证码校验
        validateCaptcha(username, code, uuid);
        // 登录前置校验
        loginPreCheck(username, password);
    /**
     * 校验验证码
     * 
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        // 判断当前是否需要生成验证码
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        if (captchaEnabled)
        {
            // 根据生成验证码时返回给前端的uuid拼接验证码在redis中的key值
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
            // 从redis中获取验证码
            String captcha = redisCache.getCacheObject(verifyKey);
            // 删除验证码
            redisCache.deleteObject(verifyKey);
            if (captcha == null)
            {
                // 开启异步任务记录用户信息
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                throw new CaptchaExpireException();
            }
            if (!code.equalsIgnoreCase(captcha))
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                throw new CaptchaException();
            }
        }
    }

2.2、登录认证

接下来是生成token,首先定义一个UsernamePasswordAuthenticationToken对象用来存放用户登录提供的认证凭证表示一个为认证的请求,然后是将用户的提供的登录认证凭证放到线程池的上下文中,调用authenticationManager的authenticate方法进行认证,最后记录登录信息并返回token

    /**
     * 登录验证
     * 
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid)
    {
        // 验证码校验
        validateCaptcha(username, code, uuid);
        // 登录前置校验
        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        try
        {
            // 将用户名和密码存放到UsernamePasswordAuthenticationToken中
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            // 将authenticationToken放到线程池的上下文中
            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")));
        // 从认证成功的authentication中取出用户信息
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        // 记录登录信息
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }

通过SpringSecurity的过滤器链调用UserDetailsServiceImpl进行用户名和密码的校验

/**
 * 用户验证处理
 *
 * @author ruoyi
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService
{
    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

    @Autowired
    private ISysUserService userService;
    
    @Autowired
    private SysPasswordService passwordService;

    @Autowired
    private SysPermissionService permissionService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        // 根据用户名查询用户
        SysUser user = userService.selectUserByUserName(username);
        if (StringUtils.isNull(user))
        {
            // 用户为空
            log.info("登录用户:{} 不存在.", username);
            throw new ServiceException(MessageUtils.message("user.not.exists"));
        }
        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
        {
            log.info("登录用户:{} 已被删除.", username);
            throw new ServiceException(MessageUtils.message("user.password.delete"));
        }
        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
        {
            log.info("登录用户:{} 已被停用.", username);
            throw new ServiceException(MessageUtils.message("user.blocked"));
        }

        // 校验密码
        passwordService.validate(user);

        return createLoginUser(user);
    }

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

2.3 生成token令牌

生成token

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser)
    {
        String token = IdUtils.fastUUID();
        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        return createToken(claims);
    }

设置用户信息

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(LoginUser loginUser)
    {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr();
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
    }

将token缓存到redis并设置过期时间(默认30分钟)

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser)
    {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = getTokenKey(loginUser.getToken());
        redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);

    }
  • expireTime * MILLIS_MINUTE);
    // 根据uuid将loginUser缓存
    String userKey = getTokenKey(loginUser.getToken());
    redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);

    }


到就登录成功了并且后端成功返回token

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/726746.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

扩散模型荣获CVPR2024最佳论文奖,最新成果让评估和改进生成模型更加效率!

CVPR 2024最佳论文奖新鲜出炉 其中一篇是Rich Human Feedback for Text-to-Image Generation&#xff0c;受大模型中的RLHF技术启发&#xff0c;团队用人类反馈来改进Stable Diffusion等文生图模型。 作者提出了收集丰富的细粒度人类反馈信息&#xff0c;用于更好地评估和改进…

深度优先遍历-在二叉树中找到两个节点的最近公共祖先

一、问题描述 二、解题思路 使用深度递归的方式&#xff0c;如果当前结点val为o1时&#xff0c;返回1&#xff0c;如果当前结点是val为o2时&#xff0c;返回2&#xff1b; 1.当前结点的左右子树结点返回值分别为1和2时&#xff0c;说明该结点是最近的公共祖先结点 2.当前结点…

联邦学习——学习笔记1:FedAvg算法

文章目录 本笔记参考自b站up主&#xff1a;丸一口 原视频链接 如上图&#xff0c;现有6个医院&#xff1a;眼科、儿科、妇科、骨科、综合医院1、综合医院2。中间节点为政府。 现政府要求用各个医院的数据训练某个模型&#xff0c;希望对某些疾病进行一些预测&#xff0c;数据…

【Linux】—在Linux中搭建Python环境

文章目录 前言一、检查Linux系统是否自带Python版本。二、安装依赖包(重要)三、下载Python-3.9.5安装包四、下载完成后&#xff0c;通过xftp6上传到Linux服务器上五、解压Python安装包六、编译安装Python七、配置Python环境变量八、运行Python&#xff0c;查看是否可用九、pyth…

图像处理与视觉感知复习--频率域图像增强图像变换

文章目录 图像变换与信号分解正弦信号与傅里叶级数傅里叶变换离散傅里叶变换(DFT)频率域滤波 图像变换与信号分解 空间域&#xff1a;就是像素域&#xff0c;在空间域的处理是在像素级的处理&#xff0c;如像素级的叠加。 频率域&#xff1a;任何一个波形都可以分解用多个正弦…

AI交互数字人如何赋能数智教育?

随着AI交互数字人技术的飞速发展&#xff0c;教育领域正经历着前所未有的变革。AI交互数字人为教育领域注入了全新活力&#xff0c;重塑着教学模式&#xff0c;为学生带来沉浸式学习体验。 AI交互数字人在教育领域中&#xff0c;可以应用在&#xff1a; 1、个性化学习教学指导…

APM Profile 在系统可观测体系中的应用

引言 应用程序性能分析&#xff08;Application Performance Management&#xff0c;APM&#xff09;是一个广泛的概念&#xff0c;涉及应用程序运行时各种性能指标的监测、诊断和优化。在可观测体系建设中&#xff0c;APM 是保障系统业务运行性能的关键技术&#xff0c;确保用…

递归算法:代码迷宫中的无限探索

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 目录 前言 一 深入理解递归 二 迭代VS递归 三 递归算法题目解析 3.1 汉诺塔问题 3.2 合并两个有序链表 3.3 反转链表 3.4 两两交换链表中的节点 3.5 Pow&#xff08;x&#xff0c;n&#xff09;&#xff08;快速幂)…

CRMEB-PHP多商户版安装系统配置清单

系统在安装完成之后&#xff0c;需要对系统进行一系列的配置&#xff0c;才能正常使用全部的功能&#xff0c;以下是官方整理的配置清单 平台后台 商户后台

计算机SCI期刊,中科院3区,易过审,专业认可度不错

一、期刊名称 Journal of Cloud Computing-Advances Systems and Applications 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;4 中科院分区&#xff1a;3区 三、期刊征稿范围 Journal of Cloud Computing&#xff1a;A…

MyBatis 动态 SQL怎么使用?

引言&#xff1a;在现代的软件开发中&#xff0c;数据库操作是任何应用程序的核心部分之一。而在 Java 开发领域&#xff0c;MyBatis 作为一款优秀的持久层框架&#xff0c;以其简洁的配置和强大的灵活性被广泛应用。动态 SQL 允许开发人员根据不同的条件和场景动态地生成和执行…

Flutter 简化线程Isolate的使用

文章目录 前言一、完整代码二、使用示例1、通过lambda启动线程2、获取线程返回值3、线程通信4、结束isolate 总结 前言 flutter的线程是数据独立的&#xff0c;每个线程一般通过sendport来传输数据&#xff0c;这样使得线程调用没那么方便&#xff0c;本文将提供一种支持lambd…

CIRCOS圈图绘制 - circos安装

Circos是绘制圈图的神器&#xff0c;在http://circos.ca/images/页面有很多CIRCOS可视化的示例。 Circos可以在线使用&#xff0c;在线使用时是把表格转为圈图&#xff0c;不过只允许最大75行和75列&#xff1b;做一些简单的示意图会比较好&#xff0c;最后时会介绍下在线的tab…

vue大屏适配方案

前言 开发过大屏的铁汁们应该知道&#xff0c;前期最头疼的就是大屏适配&#xff0c;由于大屏项目需要在市面上不是很常见的显示器上进行展示&#xff0c;所以要根据不同的尺寸进行适配&#xff0c;今天我将为大家分享的我使用的大屏适配方案&#xff0c;话不多说&#xff0c;直…

MySQL Server和Server启动程序(一)

MySQL Server mysqld&#xff0c;也称为MySQL Server&#xff0c;是一个单线程多任务的程序&#xff0c;它在MySQL安装中执行大部分工作。它不会生成额外的进程。MySQL Server管理对包含数据库和表的MySQL数据目录的访问。数据目录也是其他信息&#xff08;如日志文件和状态文…

Windows Server配置iSCSI,做ESXI共享存储

1&#xff1a;使用一台Windows Server2022主机配置iSCSI&#xff0c;准备给ESXI8.0做共享存储使用。有一些ESXI的功能必须使用共享存储才行&#xff0c;比如HA的功能。 2&#xff1a;登录系统&#xff0c;点击添加角色和功能。 3&#xff1a;之后一路下一步&#xff0c;在选择…

健身器械行业外贸ERP管理降本增效解决方案

随着经济的迅速发展&#xff0c;以及健身锻炼的普及&#xff0c;人们对健身器材的需求量也在大幅度增加。欧美市场增长迅猛&#xff0c;家用健身器材热度飙升&#xff0c;尤其是跑步机、健身单车等轻便型家用健身器材&#xff0c;备受消费者青睐。 出口的主要国家包括&#xf…

Git 和 TortoiseGit 安装和配置(图文详解)

使用git&#xff0c;需要在Windows上需要安装两个软件&#xff1a;1&#xff09;Git 2&#xff09;TortoiseGit 若需要&#xff0c;可以下载TortoiseGit汉化语言包。 注意&#xff1a;tortoiseGit是在安装了Git的基础上运行的&#xff0c;所以需要先安装Git&#xff0c;后安装…

智慧校园导航系统:技术驱动下的校园管理与师生体验革新

随着智慧校园建设的不断推进&#xff0c;校园导航系统作为提升校园管理效率、优化师生出行体验的重要工具&#xff0c;正逐渐成为各大高校的标配。本文将重点介绍维小帮智慧校园导航系统&#xff0c;如何通过创新的设计和功能&#xff0c;解决校园导航中的种种难题&#xff0c;…

1分钟带你部署本地Llama3大模型

介绍 LLaMa 3由Meta于2024年4月18日正式发布&#xff0c;这一版本是对先前LLaMa系列的重大升级。新发布的模型包括8B&#xff08;80亿参数&#xff09;和70B&#xff08;700亿参数&#xff09;两个版本&#xff0c;这两个版本在一系列行业标准基准测试中展示了最先进的性能。 从…